From 18da0b3418511c013cc3a06df72f50d4abc00042 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 3 Dec 2020 10:45:26 +0000 Subject: [PATCH] Integrate `package io` into core library --- core/encoding/json/marshal.odin | 5 +- core/fmt/fmt.odin | 473 +++++++++++++++++--------------- core/io/conv.odin | 15 + core/io/io.odin | 13 +- core/io/util.odin | 7 + core/reflect/types.odin | 278 ++++++++++--------- core/strings/builder.odin | 223 ++++++++++----- core/strings/conversion.odin | 21 +- core/strings/strings.odin | 15 +- 9 files changed, 621 insertions(+), 429 deletions(-) diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 52854fc80..7bdc3abc3 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -14,7 +14,8 @@ Marshal_Error :: enum { } marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) { - b := strings.make_builder(allocator); + b: strings.Builder; + strings.init_builder(&b, allocator); err := marshal_arg(&b, v); @@ -129,7 +130,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error { case b32: val = bool(b); case b64: val = bool(b); } - write_string(b, val ? "true" : "false"); + write_string_builder(b, val ? "true" : "false"); case Type_Info_Any: return .Unsupported_Type; diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 27e0de2ba..ff627bdb2 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -3,6 +3,7 @@ package fmt import "core:math/bits" import "core:mem" import "core:os" +import "core:io" import "core:reflect" import "core:runtime" import "core:strconv" @@ -29,7 +30,7 @@ Info :: struct { reordered: bool, good_arg_index: bool, - buf: ^strings.Builder, + writer: io.Writer, arg: any, // Temporary record_level: int, } @@ -111,7 +112,6 @@ fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> int { strings.flush_builder(&buf); return flush_data.bytes_written; } - // print* procedures return the number of bytes written print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep); } println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep); } @@ -130,17 +130,20 @@ eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fm // aprint* procedures return a string that was allocated with the current context // They must be freed accordingly aprint :: proc(args: ..any, sep := " ") -> string { - str := strings.make_builder(); + str: strings.Builder; + strings.init_builder(&str); sbprint(buf=&str, args=args, sep=sep); return strings.to_string(str); } aprintln :: proc(args: ..any, sep := " ") -> string { - str := strings.make_builder(); + str: strings.Builder; + strings.init_builder(&str); sbprintln(buf=&str, args=args, sep=sep); return strings.to_string(str); } aprintf :: proc(fmt: string, args: ..any) -> string { - str := strings.make_builder(); + str: strings.Builder; + strings.init_builder(&str); sbprintf(&str, fmt, ..args); return strings.to_string(str); } @@ -148,17 +151,20 @@ aprintf :: proc(fmt: string, args: ..any) -> string { // tprint* procedures return a string that was allocated with the current context's temporary allocator tprint :: proc(args: ..any, sep := " ") -> string { - str := strings.make_builder(context.temp_allocator); + str: strings.Builder; + strings.init_builder(&str, context.temp_allocator); sbprint(buf=&str, args=args, sep=sep); return strings.to_string(str); } tprintln :: proc(args: ..any, sep := " ") -> string { - str := strings.make_builder(context.temp_allocator); + str: strings.Builder; + strings.init_builder(&str, context.temp_allocator); sbprintln(buf=&str, args=args, sep=sep); return strings.to_string(str); } tprintf :: proc(fmt: string, args: ..any) -> string { - str := strings.make_builder(context.temp_allocator); + str: strings.Builder; + strings.init_builder(&str, context.temp_allocator); sbprintf(&str, fmt, ..args); return strings.to_string(str); } @@ -205,15 +211,34 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { + wprint(w=strings.to_writer(buf), args=args, sep=sep); + strings.flush_builder(buf); + return strings.to_string(buf^); +} + +sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { + wprintln(w=strings.to_writer(buf), args=args, sep=sep); + strings.flush_builder(buf); + return strings.to_string(buf^); +} + +sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string { + wprintf(w=strings.to_writer(buf), fmt=fmt, args=args); + strings.flush_builder(buf); + return strings.to_string(buf^); +} + + +wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int { fi: Info; - fi.buf = buf; + fi.writer = w; // NOTE(bill): Old approach // prev_string := false; // for arg, i in args { // is_string := arg != nil && reflect.is_string(type_info_of(arg.id)); // if i > 0 && !is_string && !prev_string { - // strings.write_byte(buf, ' '); + // io.write_byte(writer, ' '); // } // fmt_value(&fi, args[i], 'v'); // prev_string = is_string; @@ -223,47 +248,46 @@ sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { // so I am going to keep the same behaviour as `*println` for `*print` for _, i in args { if i > 0 { - strings.write_string(buf, sep); + io.write_string(fi.writer, sep); } fmt_value(&fi, args[i], 'v'); } - strings.flush_builder(buf); - return strings.to_string(buf^); + io.flush(auto_cast w); + return int(io.size(auto_cast w)); } -sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { +wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int { fi: Info; - fi.buf = buf; + fi.writer = w; for _, i in args { if i > 0 { - strings.write_string(buf, sep); + io.write_string(fi.writer, sep); } fmt_value(&fi, args[i], 'v'); } - strings.write_byte(buf, '\n'); - strings.flush_builder(buf); - return strings.to_string(buf^); + io.write_byte(fi.writer, '\n'); + io.flush(auto_cast w); + return int(io.size(auto_cast w)); } -sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { +wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi: Info; arg_index: int = 0; end := len(fmt); was_prev_index := false; - loop: for i := 0; i < end; /**/ { - fi = Info{buf = b, good_arg_index = true, reordered = fi.reordered}; + fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered}; prev_i := i; for i < end && !(fmt[i] == '%' || fmt[i] == '{' || fmt[i] == '}') { i += 1; } if i > prev_i { - strings.write_string(b, fmt[prev_i:i]); + io.write_string(fi.writer, fmt[prev_i:i]); } if i >= end { break loop; @@ -278,13 +302,13 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { // Skip extra one i += 1; } - strings.write_byte(b, char); + io.write_byte(fi.writer, char); continue loop; } else if char == '{' { if i < end && fmt[i] == char { // Skip extra one i += 1; - strings.write_byte(b, char); + io.write_byte(fi.writer, char); continue loop; } } @@ -315,7 +339,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { i += 1; fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index); if !fi.width_set { - strings.write_string(b, "%!(BAD WIDTH)"); + io.write_string(w, "%!(BAD WIDTH)"); } if fi.width < 0 { @@ -346,7 +370,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { fi.prec_set = false; } if !fi.prec_set { - strings.write_string(fi.buf, "%!(BAD PRECISION)"); + io.write_string(fi.writer, "%!(BAD PRECISION)"); } was_prev_index = false; } else { @@ -359,7 +383,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { } if i >= end { - strings.write_string(b, "%!(NO VERB)"); + io.write_string(fi.writer, "%!(NO VERB)"); break loop; } @@ -368,11 +392,11 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { switch { case verb == '%': - strings.write_byte(b, '%'); + io.write_byte(fi.writer, '%'); case !fi.good_arg_index: - strings.write_string(b, "%!(BAD ARGUMENT NUMBER)"); + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)"); case arg_index >= len(args): - strings.write_string(b, "%!(MISSING ARGUMENT)"); + io.write_string(fi.writer, "%!(MISSING ARGUMENT)"); case: fmt_arg(&fi, args[arg_index], verb); arg_index += 1; @@ -388,14 +412,14 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { arg_index = new_arg_index; i = new_i; } else { - strings.write_string(b, "%!(BAD ARGUMENT NUMBER "); + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER "); // Skip over the bad argument start_index := i; for i < end && fmt[i] != '}' && fmt[i] != ':' { i += 1; } fmt_arg(&fi, fmt[start_index:i], 'v'); - strings.write_string(b, ")"); + io.write_string(fi.writer, ")"); } } @@ -428,7 +452,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { i += 1; fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index); if !fi.width_set { - strings.write_string(b, "%!(BAD WIDTH)"); + io.write_string(fi.writer, "%!(BAD WIDTH)"); } if fi.width < 0 { @@ -459,7 +483,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { fi.prec_set = false; } if !fi.prec_set { - strings.write_string(fi.buf, "%!(BAD PRECISION)"); + io.write_string(fi.writer, "%!(BAD PRECISION)"); } was_prev_index = false; } else { @@ -473,7 +497,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { if i >= end { - strings.write_string(b, "%!(NO VERB)"); + io.write_string(fi.writer, "%!(NO VERB)"); break loop; } @@ -483,7 +507,7 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { } if i >= end { - strings.write_string(b, "%!(MISSING CLOSE BRACE)"); + io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)"); break loop; } @@ -492,11 +516,11 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { switch { case brace != '}': - strings.write_string(b, "%!(MISSING CLOSE BRACE)"); + io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)"); case !fi.good_arg_index: - strings.write_string(b, "%!(BAD ARGUMENT NUMBER)"); + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)"); case arg_index >= len(args): - strings.write_string(b, "%!(MISSING ARGUMENT)"); + io.write_string(fi.writer, "%!(MISSING ARGUMENT)"); case: fmt_arg(&fi, args[arg_index], verb); arg_index += 1; @@ -505,23 +529,23 @@ sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { } if !fi.reordered && arg_index < len(args) { - strings.write_string(b, "%!(EXTRA "); + io.write_string(fi.writer, "%!(EXTRA "); for arg, index in args[arg_index:] { if index > 0 { - strings.write_string(b, ", "); + io.write_string(fi.writer, ", "); } if arg == nil { - strings.write_string(b, ""); + io.write_string(fi.writer, ""); } else { fmt_arg(&fi, args[index], 'v'); } } - strings.write_string(b, ")"); + io.write_string(fi.writer, ")"); } - strings.flush_builder(b); - return strings.to_string(b^); + io.flush(auto_cast fi.writer); + return int(io.size(auto_cast fi.writer)); } @@ -599,23 +623,23 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) { fmt_bad_verb :: proc(using fi: ^Info, verb: rune) { - strings.write_string(buf, "%!"); - strings.write_rune(buf, verb); - strings.write_byte(buf, '('); + io.write_string(writer, "%!"); + io.write_rune(writer, verb); + io.write_byte(writer, '('); if arg.id != nil { - reflect.write_typeid(buf, arg.id); - strings.write_byte(buf, '='); + reflect.write_typeid(writer, arg.id); + io.write_byte(writer, '='); fmt_value(fi, arg, 'v'); } else { - strings.write_string(buf, ""); + io.write_string(writer, ""); } - strings.write_byte(buf, ')'); + io.write_byte(writer, ')'); } fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) { switch verb { case 't', 'v': - strings.write_string(buf, b ? "true" : "false"); + io.write_string(writer, b ? "true" : "false"); case: fmt_bad_verb(fi, verb); } @@ -633,7 +657,7 @@ fmt_write_padding :: proc(fi: ^Info, width: int) { } for i := 0; i < width; i += 1 { - strings.write_byte(fi.buf, pad_byte); + io.write_byte(fi.writer, pad_byte); } } @@ -692,8 +716,8 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d case 16: c = 'x'; } if c != 0 { - strings.write_byte(fi.buf, '0'); - strings.write_byte(fi.buf, c); + io.write_byte(fi.writer, '0'); + io.write_byte(fi.writer, c); } } @@ -758,8 +782,8 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i case 16: c = 'x'; } if c != 0 { - strings.write_byte(fi.buf, '0'); - strings.write_byte(fi.buf, c); + io.write_byte(fi.writer, '0'); + io.write_byte(fi.writer, c); } } @@ -775,9 +799,9 @@ __DIGITS_UPPER := "0123456789ABCDEFX"; fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) { switch verb { case 'c', 'r', 'v': - strings.write_rune(fi.buf, r); + io.write_rune(fi.writer, r); case 'q': - strings.write_quoted_rune(fi.buf, r); + strings.write_quoted_rune(fi.writer, r); case: fmt_int(fi, u64(r), false, 32, verb); } @@ -799,7 +823,7 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) { if r < 0 || r > utf8.MAX_RUNE { fmt_bad_verb(fi, verb); } else { - strings.write_string(fi.buf, "U+"); + io.write_string(fi.writer, "U+"); _fmt_int(fi, u, 16, false, bit_size, __DIGITS_UPPER); } @@ -824,7 +848,7 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru if r < 0 || r > utf8.MAX_RUNE { fmt_bad_verb(fi, verb); } else { - strings.write_string(fi.buf, "U+"); + io.write_string(fi.writer, "U+"); _fmt_int_128(fi, u, 16, false, bit_size, __DIGITS_UPPER); } @@ -835,18 +859,18 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru _pad :: proc(fi: ^Info, s: string) { if !fi.width_set { - strings.write_string(fi.buf, s); + io.write_string(fi.writer, s); return; } width := fi.width - utf8.rune_count_in_string(s); if fi.minus { // right pad - strings.write_string(fi.buf, s); + io.write_string(fi.writer, s); fmt_write_padding(fi, width); } else { // left pad fmt_write_padding(fi, width); - strings.write_string(fi.buf, s); + io.write_string(fi.writer, s); } } @@ -872,15 +896,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { } if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') { - strings.write_string(fi.buf, string(b)); + io.write_string(fi.writer, string(b)); return; } if fi.plus || b[0] != '+' { if fi.zero && fi.width_set && fi.width > len(b) { - strings.write_byte(fi.buf, b[0]); + io.write_byte(fi.writer, b[0]); fmt_write_padding(fi, fi.width - len(b)); - strings.write_string(fi.buf, string(b[1:])); + io.write_string(fi.writer, string(b[1:])); } else { _pad(fi, string(b)); } @@ -908,15 +932,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { } if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') { - strings.write_string(fi.buf, string(b)); + io.write_string(fi.writer, string(b)); return; } if fi.plus || str[0] != '+' { if fi.zero && fi.width_set && fi.width > len(b) { - strings.write_byte(fi.buf, b[0]); + io.write_byte(fi.writer, b[0]); fmt_write_padding(fi, fi.width - len(b)); - strings.write_string(fi.buf, string(b[1:])); + io.write_string(fi.writer, string(b[1:])); } else { _pad(fi, string(b)); } @@ -939,7 +963,7 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { case: panic("Unhandled float size"); } - strings.write_string(fi.buf, "0h"); + io.write_string(fi.writer, "0h"); _fmt_int(fi, u, 16, false, bit_size, __DIGITS_LOWER if verb == 'h' else __DIGITS_UPPER); @@ -952,15 +976,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { fmt_string :: proc(fi: ^Info, s: string, verb: rune) { switch verb { case 's', 'v': - strings.write_string(fi.buf, s); + io.write_string(fi.writer, s); if fi.width_set && len(s) < fi.width { for _ in 0.. 0 && space { - strings.write_byte(fi.buf, ' '); + io.write_byte(fi.writer, ' '); } char_set := __DIGITS_UPPER; if verb == 'x' { @@ -991,7 +1015,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) { switch verb { case 'p', 'v': if !fi.hash || verb == 'v' { - strings.write_string(fi.buf, "0x"); + io.write_string(fi.writer, "0x"); } _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER); @@ -1052,7 +1076,7 @@ string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) { fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { if v.id == nil || v.data == nil { - strings.write_string(fi.buf, ""); + io.write_string(fi.writer, ""); return; } @@ -1069,7 +1093,7 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { if !ok { str = "!%(BAD ENUM VALUE)"; } - strings.write_string(fi.buf, str); + io.write_string(fi.writer, str); } } } @@ -1101,6 +1125,17 @@ stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.T return "", false; } +fmt_write_i64 :: proc(w: io.Writer, i: i64, base: int = 10) { + buf: [32]byte; + s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil); + io.write_string(w, s); +} +fmt_write_u64 :: proc(w: io.Writer, i: u64, base: int) { + buf: [32]byte; + s := strconv.append_bits(buf[:], u64(i), base, false, 64, strconv.digits, nil); + io.write_string(w, s); +} + fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool { @@ -1161,12 +1196,12 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { et := runtime.type_info_base(info.elem); if name != "" { - strings.write_string(fi.buf, name); + io.write_string(fi.writer, name); } else { - reflect.write_type(fi.buf, type_info); + reflect.write_type(fi.writer, type_info); } - strings.write_byte(fi.buf, '{'); - defer strings.write_byte(fi.buf, '}'); + io.write_byte(fi.writer, '{'); + defer io.write_byte(fi.writer, '}'); e, is_enum := et.variant.(runtime.Type_Info_Enum); commas := 0; @@ -1176,21 +1211,21 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { } if commas > 0 { - strings.write_string(fi.buf, ", "); + io.write_string(fi.writer, ", "); } if is_enum { for ev, evi in e.values { v := u64(ev); if v == u64(i) { - strings.write_string(fi.buf, e.names[evi]); + io.write_string(fi.writer, e.names[evi]); commas += 1; continue loop; } } } v := i64(i) + info.lower; - strings.write_i64(fi.buf, v, 10); + fmt_write_i64(fi.writer, v, 10); commas += 1; } } @@ -1212,19 +1247,19 @@ fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") { } if bit_field_name != "" { - strings.write_string(fi.buf, bit_field_name); - strings.write_byte(fi.buf, '{'); + io.write_string(fi.writer, bit_field_name); + io.write_byte(fi.writer, '{'); } else { - strings.write_string(fi.buf, "bit_field{"); + io.write_string(fi.writer, "bit_field{"); } for name, i in info.names { if i > 0 { - strings.write_string(fi.buf, ", "); + io.write_string(fi.writer, ", "); } bits := u64(info.bits[i]); offset := u64(info.offsets[i]); - strings.write_string(fi.buf, name); - strings.write_string(fi.buf, " = "); + io.write_string(fi.writer, name); + io.write_string(fi.writer, " = "); n := 8*u64(size_of(u64)); sa := n - bits; @@ -1232,10 +1267,10 @@ fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") { u <<= sa; u >>= sa; - strings.write_u64(fi.buf, u, 10); + fmt_write_u64(fi.writer, u, 10); } - strings.write_byte(fi.buf, '}'); + io.write_byte(fi.writer, '}'); } } @@ -1258,16 +1293,16 @@ fmt_opaque :: proc(fi: ^Info, v: any) { type_info := type_info_of(v.id); if is_nil(v.data, type_info.size) { - strings.write_string(fi.buf, "nil"); + io.write_string(fi.writer, "nil"); return; } if ot, ok := rt.type_info_base(type_info).variant.(rt.Type_Info_Opaque); ok { elem := rt.type_info_base(ot.elem); if elem == nil { return; } - reflect.write_type(fi.buf, type_info); - strings.write_byte(fi.buf, '{'); - defer strings.write_byte(fi.buf, '}'); + reflect.write_type(fi.writer, type_info); + io.write_byte(fi.writer, '{'); + defer io.write_byte(fi.writer, '}'); #partial switch in elem.variant { case rt.Type_Info_Integer, rt.Type_Info_Pointer, rt.Type_Info_Float: @@ -1276,9 +1311,9 @@ fmt_opaque :: proc(fi: ^Info, v: any) { // Okay } } else { - reflect.write_type(fi.buf, type_info); - strings.write_byte(fi.buf, '{'); - strings.write_byte(fi.buf, '}'); + reflect.write_type(fi.writer, type_info); + io.write_byte(fi.writer, '{'); + io.write_byte(fi.writer, '}'); } } @@ -1289,14 +1324,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { n -= 1; } for in 0.."); + io.write_string(fi.writer, ""); return; } @@ -1369,7 +1404,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { w -= 1; switch { case u == 0: - strings.write_string(fi.buf, "0s"); + io.write_string(fi.writer, "0s"); return; case u < u64(time.Microsecond): prec = 0; @@ -1408,7 +1443,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { w -= 1; buf[w] = '-'; } - strings.write_string(fi.buf, string(buf[w:])); + io.write_string(fi.writer, string(buf[w:])); return; case time.Time: @@ -1417,20 +1452,20 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { h, min, s := time.clock(t); ns := i64(t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9; write_padded_number(fi, i64(y), 4); - strings.write_byte(fi.buf, '-'); + io.write_byte(fi.writer, '-'); write_padded_number(fi, i64(mon), 2); - strings.write_byte(fi.buf, '-'); + io.write_byte(fi.writer, '-'); write_padded_number(fi, i64(d), 2); - strings.write_byte(fi.buf, ' '); + io.write_byte(fi.writer, ' '); write_padded_number(fi, i64(h), 2); - strings.write_byte(fi.buf, ':'); + io.write_byte(fi.writer, ':'); write_padded_number(fi, i64(min), 2); - strings.write_byte(fi.buf, ':'); + io.write_byte(fi.writer, ':'); write_padded_number(fi, i64(s), 2); - strings.write_byte(fi.buf, '.'); + io.write_byte(fi.writer, '.'); write_padded_number(fi, i64(ns), 9); - strings.write_string(fi.buf, " +0000 UTC"); + io.write_string(fi.writer, " +0000 UTC"); return; } @@ -1441,15 +1476,15 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { return; } if b.is_raw_union { - strings.write_string(fi.buf, info.name); - strings.write_string(fi.buf, "{}"); + io.write_string(fi.writer, info.name); + io.write_string(fi.writer, "{}"); return; }; is_soa := b.soa_kind != .None; - strings.write_string(fi.buf, info.name); - strings.write_byte(fi.buf, '[' if is_soa else '{'); + io.write_string(fi.writer, info.name); + io.write_byte(fi.writer, '[' if is_soa else '{'); hash := fi.hash; defer fi.hash = hash; indent := fi.indent; defer fi.indent -= 1; @@ -1458,13 +1493,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { fi.indent += 1; if hash { - strings.write_byte(fi.buf, '\n'); + io.write_byte(fi.writer, '\n'); } defer { if hash { - for in 0.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && index > 0 { io.write_string(fi.writer, ", "); } field_count := -1; - if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } - strings.write_string(fi.buf, base_type_name); - strings.write_byte(fi.buf, '{'); - defer strings.write_byte(fi.buf, '}'); + io.write_string(fi.writer, base_type_name); + io.write_byte(fi.writer, '{'); + defer io.write_byte(fi.writer, '}'); for name, i in b.names { field_count += 1; - if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } if hash { - for in 0.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } if hash { - for in 0.."); + io.write_string(fi.writer, ""); return; } if fi.record_level < 1 { fi.record_level += 1; defer fi.record_level -= 1; - strings.write_byte(fi.buf, '&'); + io.write_byte(fi.writer, '&'); fmt_value(fi, a, verb); return; } @@ -1582,13 +1617,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Struct, runtime.Type_Info_Union: if ptr == nil { - strings.write_string(fi.buf, ""); + io.write_string(fi.writer, ""); return; } if fi.record_level < 1 { fi.record_level += 1; defer fi.record_level -= 1; - strings.write_byte(fi.buf, '&'); + io.write_byte(fi.writer, '&'); fmt_value(fi, a, verb); return; } @@ -1603,10 +1638,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { s := strings.string_from_ptr((^byte)(v.data), info.count); fmt_string(fi, s, verb); } else { - strings.write_byte(fi.buf, '['); - defer strings.write_byte(fi.buf, ']'); + io.write_byte(fi.writer, '['); + defer io.write_byte(fi.writer, ']'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } data := uintptr(v.data) + uintptr(i*info.elem_size); fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); @@ -1614,19 +1649,19 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } case runtime.Type_Info_Enumerated_Array: - strings.write_byte(fi.buf, '['); - defer strings.write_byte(fi.buf, ']'); + io.write_byte(fi.writer, '['); + defer io.write_byte(fi.writer, ']'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } idx, ok := stored_enum_value_to_string(info.index, info.min_value, i); if ok { - strings.write_byte(fi.buf, '.'); - strings.write_string(fi.buf, idx); + io.write_byte(fi.writer, '.'); + io.write_string(fi.writer, idx); } else { - strings.write_i64(fi.buf, i64(info.min_value)+i64(i)); + fmt_write_i64(fi.writer, i64(info.min_value)+i64(i)); } - strings.write_string(fi.buf, " = "); + io.write_string(fi.writer, " = "); data := uintptr(v.data) + uintptr(i*info.elem_size); fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); @@ -1640,10 +1675,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } else if verb == 'p' { fmt_pointer(fi, array.data, 'p'); } else { - strings.write_byte(fi.buf, '['); - defer strings.write_byte(fi.buf, ']'); + io.write_byte(fi.writer, '['); + defer io.write_byte(fi.writer, ']'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } data := uintptr(array.data) + uintptr(i*info.elem_size); fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); @@ -1652,12 +1687,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Simd_Vector: if info.is_x86_mmx { - strings.write_string(fi.buf, "intrinsics.x86_mmx<>"); + io.write_string(fi.writer, "intrinsics.x86_mmx<>"); } - strings.write_byte(fi.buf, '<'); - defer strings.write_byte(fi.buf, '>'); + io.write_byte(fi.writer, '<'); + defer io.write_byte(fi.writer, '>'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } data := uintptr(v.data) + uintptr(i*info.elem_size); fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); @@ -1672,10 +1707,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } else if verb == 'p' { fmt_pointer(fi, slice.data, 'p'); } else { - strings.write_byte(fi.buf, '['); - defer strings.write_byte(fi.buf, ']'); + io.write_byte(fi.writer, '['); + defer io.write_byte(fi.writer, ']'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } data := uintptr(slice.data) + uintptr(i*info.elem_size); fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); @@ -1687,8 +1722,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { return; } - strings.write_string(fi.buf, "map["); - defer strings.write_byte(fi.buf, ']'); + io.write_string(fi.writer, "map["); + defer io.write_byte(fi.writer, ']'); m := (^mem.Raw_Map)(v.data); if m != nil { @@ -1702,14 +1737,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { entry_size := ed.elem_size; for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } data := uintptr(entries.data) + uintptr(i*entry_size); key := data + entry_type.offsets[2]; - fmt_arg(&Info{buf = fi.buf}, any{rawptr(key), info.key.id}, 'v'); + fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v'); - strings.write_string(fi.buf, "="); + io.write_string(fi.writer, "="); value := data + entry_type.offsets[3]; fmt_arg(fi, any{rawptr(value), info.value.id}, 'v'); @@ -1718,21 +1753,21 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Struct: if info.is_raw_union { - strings.write_string(fi.buf, "(raw_union)"); + io.write_string(fi.writer, "(raw_union)"); return; } is_soa := info.soa_kind != .None; - strings.write_byte(fi.buf, '[' if is_soa else '{'); - defer strings.write_byte(fi.buf, ']' if is_soa else '}'); + io.write_byte(fi.writer, '[' if is_soa else '{'); + defer io.write_byte(fi.writer, ']' if is_soa else '}'); fi.indent += 1; defer fi.indent -= 1; hash := fi.hash; defer fi.hash = hash; fi.hash = false; - if hash { strings.write_byte(fi.buf, '\n'); } + if hash { io.write_byte(fi.writer, '\n'); } if is_soa { fi.indent += 1; @@ -1761,33 +1796,33 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { for index in 0.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && index > 0 { io.write_string(fi.writer, ", "); } field_count := -1; - if !hash && field_count > 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } - strings.write_string(fi.buf, base_type_name); - strings.write_byte(fi.buf, '{'); - defer strings.write_byte(fi.buf, '}'); + io.write_string(fi.writer, base_type_name); + io.write_byte(fi.writer, '{'); + defer io.write_byte(fi.writer, '}'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } if hash { - for in 0.. 0 { strings.write_string(fi.buf, ", "); } + if !hash && field_count > 0 { io.write_string(fi.writer, ", "); } if hash { - for in 0..= 0); if v.data == nil { - strings.write_string(fi.buf, "nil"); + io.write_string(fi.writer, "nil"); } else if info.no_nil { id := info.variants[tag].id; fmt_arg(fi, any{v.data, id}, verb); } else if tag == 0 { - strings.write_string(fi.buf, "nil"); + io.write_string(fi.writer, "nil"); } else { id := info.variants[tag-1].id; fmt_arg(fi, any{v.data, id}, verb); @@ -1886,16 +1921,16 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Procedure: ptr := (^rawptr)(v.data)^; if ptr == nil { - strings.write_string(fi.buf, "nil"); + io.write_string(fi.writer, "nil"); } else { - reflect.write_typeid(fi.buf, v.id); - strings.write_string(fi.buf, " @ "); + reflect.write_typeid(fi.writer, v.id); + io.write_string(fi.writer, " @ "); fmt_pointer(fi, ptr, 'p'); } case runtime.Type_Info_Type_Id: id := (^typeid)(v.data)^; - reflect.write_typeid(fi.buf, id); + reflect.write_typeid(fi.writer, id); case runtime.Type_Info_Bit_Field: fmt_bit_field(fi, v); @@ -1918,18 +1953,18 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { if verb == 'p' { fmt_pointer(fi, ptr, 'p'); } else if ptr == nil { - strings.write_string(fi.buf, "[]"); + io.write_string(fi.writer, "[]"); } else { len_ptr := uintptr(v.data) + uintptr(info.base_integer.size); len_any := any{rawptr(len_ptr), info.base_integer.id}; len, _ := reflect.as_int(len_any); slice_type := reflect.type_info_base(info.slice).variant.(runtime.Type_Info_Slice); - strings.write_byte(fi.buf, '['); - defer strings.write_byte(fi.buf, ']'); + io.write_byte(fi.writer, '['); + defer io.write_byte(fi.writer, ']'); for i in 0.. 0 { strings.write_string(fi.buf, ", "); } + if i > 0 { io.write_string(fi.writer, ", "); } data := uintptr(ptr) + uintptr(i*slice_type.elem_size); fmt_arg(fi, any{rawptr(data), slice_type.elem.id}, verb); @@ -1956,10 +1991,10 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { r, i := real(c), imag(c); fmt_float(fi, r, bits/2, verb); if !fi.plus && i >= 0 { - strings.write_rune(fi.buf, '+'); + io.write_rune(fi.writer, '+'); } fmt_float(fi, i, bits/2, verb); - strings.write_rune(fi.buf, 'i'); + io.write_rune(fi.writer, 'i'); case: fmt_bad_verb(fi, verb); @@ -1975,22 +2010,22 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { fmt_float(fi, r, bits/4, verb); if !fi.plus && i >= 0 { - strings.write_rune(fi.buf, '+'); + io.write_rune(fi.writer, '+'); } fmt_float(fi, i, bits/4, verb); - strings.write_rune(fi.buf, 'i'); + io.write_rune(fi.writer, 'i'); if !fi.plus && j >= 0 { - strings.write_rune(fi.buf, '+'); + io.write_rune(fi.writer, '+'); } fmt_float(fi, j, bits/4, verb); - strings.write_rune(fi.buf, 'j'); + io.write_rune(fi.writer, 'j'); if !fi.plus && k >= 0 { - strings.write_rune(fi.buf, '+'); + io.write_rune(fi.writer, '+'); } fmt_float(fi, k, bits/4, verb); - strings.write_rune(fi.buf, 'k'); + io.write_rune(fi.writer, 'k'); case: fmt_bad_verb(fi, verb); @@ -2000,7 +2035,7 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { if arg == nil { - strings.write_string(fi.buf, ""); + io.write_string(fi.writer, ""); return; } fi.arg = arg; @@ -2010,7 +2045,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { switch a in arg { case ^runtime.Type_Info: ti = a; } - reflect.write_type(fi.buf, ti); + reflect.write_type(fi.writer, ti); return; } @@ -2028,12 +2063,12 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { custom_types: switch a in arg { case runtime.Source_Code_Location: if fi.hash && verb == 'v' { - strings.write_string(fi.buf, a.file_path); - strings.write_byte(fi.buf, '('); - strings.write_i64(fi.buf, i64(a.line), 10); - strings.write_byte(fi.buf, ':'); - strings.write_i64(fi.buf, i64(a.column), 10); - strings.write_byte(fi.buf, ')'); + io.write_string(fi.writer, a.file_path); + io.write_byte(fi.writer, '('); + fmt_write_i64(fi.writer, i64(a.line), 10); + io.write_byte(fi.writer, ':'); + fmt_write_i64(fi.writer, i64(a.column), 10); + io.write_byte(fi.writer, ')'); return; } } @@ -2080,7 +2115,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { case string: fmt_string(fi, a, verb); case cstring: fmt_cstring(fi, a, verb); - case typeid: reflect.write_typeid(fi.buf, a); + case typeid: reflect.write_typeid(fi.writer, a); case i16le: fmt_int(fi, u64(a), true, 16, verb); case u16le: fmt_int(fi, u64(a), false, 16, verb); diff --git a/core/io/conv.odin b/core/io/conv.odin index 07f3494fd..e976bb7b7 100644 --- a/core/io/conv.odin +++ b/core/io/conv.odin @@ -114,6 +114,21 @@ to_writer_to :: proc(s: Stream) -> (w: Writer_To, err: Conversion_Error) { } return; } +to_Write_Closer :: proc(s: Stream) -> (w: Write_Closer, err: Conversion_Error) { + w.stream = s; + if s.stream_vtable == nil || s.impl_write == nil || s.impl_close == nil { + err = .Missing_Procedure; + } + return; +} +to_Write_Seeker :: proc(s: Stream) -> (w: Write_Seeker, err: Conversion_Error) { + w.stream = s; + if s.stream_vtable == nil || s.impl_write == nil || s.impl_seek == nil { + err = .Missing_Procedure; + } + return; +} + to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, err: Conversion_Error) { b.stream = s; diff --git a/core/io/io.odin b/core/io/io.odin index 0e39a4ef8..bc23c4c86 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -96,6 +96,9 @@ Read_Writer :: struct {using stream: Stream}; Read_Closer :: struct {using stream: Stream}; Read_Write_Closer :: struct {using stream: Stream}; Read_Write_Seeker :: struct {using stream: Stream}; + +Write_Closer :: struct {using stream: Stream}; +Write_Seeker :: struct {using stream: Stream}; Write_Flusher :: struct {using stream: Stream}; Write_Flush_Closer :: struct {using stream: Stream}; @@ -117,7 +120,6 @@ destroy :: proc(s: Stream) -> Error { if s.stream_vtable != nil && s.impl_destroy != nil { return s->impl_destroy(); } - // Instead of .Empty, .None is fine in this case return close_err; } @@ -205,9 +207,14 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64) -> (n: int, err: Error) { if err != nil { return 0, err; } - defer r->impl_seek(current_offset, .Start); - return r->impl_read(p); + n, err = r->impl_read(p); + if err != nil { + return; + } + _, err = r->impl_seek(current_offset, .Start); + return; + } write_at :: proc(w: Writer_At, p: []byte, offset: i64) -> (n: int, err: Error) { diff --git a/core/io/util.odin b/core/io/util.odin index e714ac7d0..06e95a8b3 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -14,6 +14,13 @@ write_i64 :: proc(w: Writer, i: i64, base: int = 10) -> (n: int, err: Error) { return write_string(w, s); } +write_uint :: proc(w: Writer, i: uint, base: int = 10) -> (n: int, err: Error) { + return write_u64(w, u64(i), base); +} +write_int :: proc(w: Writer, i: int, base: int = 10) -> (n: int, err: Error) { + return write_i64(w, i64(i), base); +} + @(private) Tee_Reader :: struct { using stream: Stream, diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 348d516b4..274625193 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -1,5 +1,6 @@ package reflect +import "core:io" import "core:strings" are_types_identical :: proc(a, b: ^Type_Info) -> bool { @@ -360,258 +361,273 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool { -write_typeid :: proc(buf: ^strings.Builder, id: typeid) { +write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid) { write_type(buf, type_info_of(id)); } +write_typeid_writer :: proc(writer: io.Writer, id: typeid) { + write_type(writer, type_info_of(id)); +} -write_type :: proc(buf: ^strings.Builder, ti: ^Type_Info) { +write_typeid :: proc{ + write_typeid_builder, + write_typeid_writer, +}; + +write_type :: proc{ + write_type_builder, + write_type_writer, +}; + +write_type_builder :: proc(buf: ^strings.Builder, ti: ^Type_Info) { + write_type_writer(strings.to_writer(buf), ti); +} +write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) { using strings; if ti == nil { - write_string(buf, "nil"); + write_string(w, "nil"); return; } switch info in ti.variant { case Type_Info_Named: - write_string(buf, info.name); + write_string(w, info.name); case Type_Info_Integer: switch ti.id { - case int: write_string(buf, "int"); - case uint: write_string(buf, "uint"); - case uintptr: write_string(buf, "uintptr"); + case int: write_string(w, "int"); + case uint: write_string(w, "uint"); + case uintptr: write_string(w, "uintptr"); case: - write_byte(buf, 'i' if info.signed else 'u'); - write_i64(buf, i64(8*ti.size), 10); + io.write_byte(w, 'i' if info.signed else 'u'); + io.write_i64(w, i64(8*ti.size), 10); switch info.endianness { case .Platform: // Okay - case .Little: write_string(buf, "le"); - case .Big: write_string(buf, "be"); + case .Little: write_string(w, "le"); + case .Big: write_string(w, "be"); } } case Type_Info_Rune: - write_string(buf, "rune"); + io.write_string(w, "rune"); case Type_Info_Float: - write_byte(buf, 'f'); - write_i64(buf, i64(8*ti.size), 10); + io.write_byte(w, 'f'); + io.write_i64(w, i64(8*ti.size), 10); switch info.endianness { case .Platform: // Okay - case .Little: write_string(buf, "le"); - case .Big: write_string(buf, "be"); + case .Little: write_string(w, "le"); + case .Big: write_string(w, "be"); } case Type_Info_Complex: - write_string(buf, "complex"); - write_i64(buf, i64(8*ti.size), 10); + io.write_string(w, "complex"); + io.write_i64(w, i64(8*ti.size), 10); case Type_Info_Quaternion: - write_string(buf, "quaternion"); - write_i64(buf, i64(8*ti.size), 10); + io.write_string(w, "quaternion"); + io.write_i64(w, i64(8*ti.size), 10); case Type_Info_String: if info.is_cstring { - write_string(buf, "cstring"); + write_string(w, "cstring"); } else { - write_string(buf, "string"); + write_string(w, "string"); } case Type_Info_Boolean: switch ti.id { - case bool: write_string(buf, "bool"); + case bool: write_string(w, "bool"); case: - write_byte(buf, 'b'); - write_i64(buf, i64(8*ti.size), 10); + io.write_byte(w, 'b'); + io.write_i64(w, i64(8*ti.size), 10); } case Type_Info_Any: - write_string(buf, "any"); + write_string(w, "any"); case Type_Info_Type_Id: - write_string(buf, "typeid"); + write_string(w, "typeid"); case Type_Info_Pointer: if info.elem == nil { - write_string(buf, "rawptr"); + write_string(w, "rawptr"); } else { - write_string(buf, "^"); - write_type(buf, info.elem); + write_string(w, "^"); + write_type(w, info.elem); } case Type_Info_Procedure: - write_string(buf, "proc"); + write_string(w, "proc"); if info.params == nil { - write_string(buf, "()"); + write_string(w, "()"); } else { t := info.params.variant.(Type_Info_Tuple); - write_string(buf, "("); + write_string(w, "("); for t, i in t.types { if i > 0 { - write_string(buf, ", "); + write_string(w, ", "); } - write_type(buf, t); + write_type(w, t); } - write_string(buf, ")"); + write_string(w, ")"); } if info.results != nil { - write_string(buf, " -> "); - write_type(buf, info.results); + write_string(w, " -> "); + write_type(w, info.results); } case Type_Info_Tuple: count := len(info.names); - if count != 1 { write_string(buf, "("); } + if count != 1 { write_string(w, "("); } for name, i in info.names { - if i > 0 { write_string(buf, ", "); } + if i > 0 { write_string(w, ", "); } t := info.types[i]; if len(name) > 0 { - write_string(buf, name); - write_string(buf, ": "); + write_string(w, name); + write_string(w, ": "); } - write_type(buf, t); + write_type(w, t); } - if count != 1 { write_string(buf, ")"); } + if count != 1 { write_string(w, ")"); } case Type_Info_Array: - write_string(buf, "["); - write_i64(buf, i64(info.count), 10); - write_string(buf, "]"); - write_type(buf, info.elem); + io.write_string(w, "["); + io.write_i64(w, i64(info.count), 10); + io.write_string(w, "]"); + write_type(w, info.elem); case Type_Info_Enumerated_Array: - write_string(buf, "["); - write_type(buf, info.index); - write_string(buf, "]"); - write_type(buf, info.elem); + write_string(w, "["); + write_type(w, info.index); + write_string(w, "]"); + write_type(w, info.elem); case Type_Info_Dynamic_Array: - write_string(buf, "[dynamic]"); - write_type(buf, info.elem); + io.write_string(w, "[dynamic]"); + write_type(w, info.elem); case Type_Info_Slice: - write_string(buf, "[]"); - write_type(buf, info.elem); + io.write_string(w, "[]"); + write_type(w, info.elem); case Type_Info_Map: - write_string(buf, "map["); - write_type(buf, info.key); - write_byte(buf, ']'); - write_type(buf, info.value); + io.write_string(w, "map["); + write_type(w, info.key); + io.write_byte(w, ']'); + write_type(w, info.value); case Type_Info_Struct: switch info.soa_kind { case .None: // Ignore case .Fixed: - write_string(buf, "#soa["); - write_i64(buf, i64(info.soa_len)); - write_byte(buf, ']'); - write_type(buf, info.soa_base_type); + io.write_string(w, "#soa["); + io.write_i64(w, i64(info.soa_len)); + io.write_byte(w, ']'); + write_type(w, info.soa_base_type); return; case .Slice: - write_string(buf, "#soa[]"); - write_type(buf, info.soa_base_type); + io.write_string(w, "#soa[]"); + write_type(w, info.soa_base_type); return; case .Dynamic: - write_string(buf, "#soa[dynamic]"); - write_type(buf, info.soa_base_type); + io.write_string(w, "#soa[dynamic]"); + write_type(w, info.soa_base_type); return; } - write_string(buf, "struct "); - if info.is_packed { write_string(buf, "#packed "); } - if info.is_raw_union { write_string(buf, "#raw_union "); } + write_string(w, "struct "); + if info.is_packed { write_string(w, "#packed "); } + if info.is_raw_union { write_string(w, "#raw_union "); } if info.custom_align { - write_string(buf, "#align "); - write_i64(buf, i64(ti.align), 10); - write_byte(buf, ' '); + io.write_string(w, "#align "); + io.write_i64(w, i64(ti.align), 10); + io.write_byte(w, ' '); } - write_byte(buf, '{'); + io.write_byte(w, '{'); for name, i in info.names { - if i > 0 { write_string(buf, ", "); } - write_string(buf, name); - write_string(buf, ": "); - write_type(buf, info.types[i]); + if i > 0 { write_string(w, ", "); } + io.write_string(w, name); + io.write_string(w, ": "); + write_type(w, info.types[i]); } - write_byte(buf, '}'); + io.write_byte(w, '}'); case Type_Info_Union: - write_string(buf, "union "); + write_string(w, "union "); if info.custom_align { - write_string(buf, "#align "); - write_i64(buf, i64(ti.align), 10); - write_byte(buf, ' '); + write_string(w, "#align "); + io.write_i64(w, i64(ti.align), 10); + io.write_byte(w, ' '); } - write_byte(buf, '{'); + io.write_byte(w, '{'); for variant, i in info.variants { - if i > 0 { write_string(buf, ", "); } - write_type(buf, variant); + if i > 0 { write_string(w, ", "); } + write_type(w, variant); } - write_byte(buf, '}'); + io.write_byte(w, '}'); case Type_Info_Enum: - write_string(buf, "enum "); - write_type(buf, info.base); - write_string(buf, " {"); + write_string(w, "enum "); + write_type(w, info.base); + write_string(w, " {"); for name, i in info.names { - if i > 0 { write_string(buf, ", "); } - write_string(buf, name); + if i > 0 { write_string(w, ", "); } + write_string(w, name); } - write_byte(buf, '}'); + io.write_byte(w, '}'); case Type_Info_Bit_Field: - write_string(buf, "bit_field "); + write_string(w, "bit_field "); if ti.align != 1 { - write_string(buf, "#align "); - write_i64(buf, i64(ti.align), 10); - write_byte(buf, ' '); + write_string(w, "#align "); + io.write_i64(w, i64(ti.align), 10); + io.write_byte(w, ' '); } - write_string(buf, " {"); + write_string(w, " {"); for name, i in info.names { - if i > 0 { write_string(buf, ", "); } - write_string(buf, name); - write_string(buf, ": "); - write_i64(buf, i64(info.bits[i]), 10); + if i > 0 { write_string(w, ", "); } + write_string(w, name); + write_string(w, ": "); + io.write_i64(w, i64(info.bits[i]), 10); } - write_byte(buf, '}'); + io.write_byte(w, '}'); case Type_Info_Bit_Set: - write_string(buf, "bit_set["); + write_string(w, "bit_set["); switch { case is_enum(info.elem): - write_type(buf, info.elem); + write_type(w, info.elem); case is_rune(info.elem): - write_encoded_rune(buf, rune(info.lower)); - write_string(buf, ".."); - write_encoded_rune(buf, rune(info.upper)); + write_encoded_rune(w, rune(info.lower)); + write_string(w, ".."); + write_encoded_rune(w, rune(info.upper)); case: - write_i64(buf, info.lower, 10); - write_string(buf, ".."); - write_i64(buf, info.upper, 10); + io.write_i64(w, info.lower, 10); + write_string(w, ".."); + io.write_i64(w, info.upper, 10); } if info.underlying != nil { - write_string(buf, "; "); - write_type(buf, info.underlying); + write_string(w, "; "); + write_type(w, info.underlying); } - write_byte(buf, ']'); + io.write_byte(w, ']'); case Type_Info_Opaque: - write_string(buf, "opaque "); - write_type(buf, info.elem); + write_string(w, "opaque "); + write_type(w, info.elem); case Type_Info_Simd_Vector: if info.is_x86_mmx { - write_string(buf, "intrinsics.x86_mmx"); + write_string(w, "intrinsics.x86_mmx"); } else { - write_string(buf, "#simd["); - write_i64(buf, i64(info.count)); - write_byte(buf, ']'); - write_type(buf, info.elem); + write_string(w, "#simd["); + io.write_i64(w, i64(info.count)); + io.write_byte(w, ']'); + write_type(w, info.elem); } case Type_Info_Relative_Pointer: - write_string(buf, "#relative("); - write_type(buf, info.base_integer); - write_string(buf, ") "); - write_type(buf, info.pointer); + write_string(w, "#relative("); + write_type(w, info.base_integer); + write_string(w, ") "); + write_type(w, info.pointer); case Type_Info_Relative_Slice: - write_string(buf, "#relative("); - write_type(buf, info.base_integer); - write_string(buf, ") "); - write_type(buf, info.slice); - + write_string(w, "#relative("); + write_type(w, info.base_integer); + write_string(w, ") "); + write_type(w, info.slice); } } diff --git a/core/strings/builder.odin b/core/strings/builder.odin index b31190215..3506b4bc8 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -3,6 +3,7 @@ package strings import "core:mem" import "core:unicode/utf8" import "core:strconv" +import "core:io" Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool); @@ -50,6 +51,39 @@ init_builder :: proc{ init_builder_len_cap, }; +@(private) +_builder_stream_vtable := &io.Stream_VTable{ + impl_flush = proc(s: io.Stream) -> io.Error { + b := (^Builder)(s.stream_data); + flush_builder(b); + return nil; + }, + impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + b := (^Builder)(s.stream_data); + n = write_bytes(b, p); + if len(b.buf) == cap(b.buf) { + err = .EOF; + } + return; + }, + impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error { + b := (^Builder)(s.stream_data); + _ = write_byte(b, c); + if len(b.buf) == cap(b.buf) { + return .EOF; + } + return nil; + }, +}; + +to_stream :: proc(b: ^Builder) -> io.Stream { + return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b}; +} +to_writer :: proc(b: ^Builder) -> io.Writer { + w, _ := io.to_writer(to_stream(b)); + return w; +} + @@ -139,36 +173,75 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { return; } -write_rune :: proc(b: ^Builder, r: rune) -> int { +write_rune :: proc{ + write_rune_builder, + write_rune_writer, +}; +write_rune_builder :: proc(b: ^Builder, r: rune) -> int { + return write_rune_writer(to_writer(b), r); +} +write_rune_writer :: proc(w: io.Writer, r: rune) -> int { if r < utf8.RUNE_SELF { - return write_byte(b, byte(r)); + return _write_byte(w, byte(r)); } s, n := utf8.encode_rune(r); - write_bytes(b, s[:n]); + n, _ = io.write(w, s[:n]); return n; } -write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) { + + +write_quoted_rune :: proc{ + write_quoted_rune_builder, + write_quoted_rune_writer, +}; + +write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) { + return write_quoted_rune_writer(to_writer(b), r); +} + +@(private) +_write_byte :: proc(w: io.Writer, r: byte) -> int { + err := io.write_byte(w, r); + return 1 if err == nil else 0; +} + +write_quoted_rune_writer :: proc(w: io.Writer, r: rune) -> (n: int) { + quote := byte('\''); - n += write_byte(b, quote); + n += _write_byte(w, quote); buf, width := utf8.encode_rune(r); if width == 1 && r == utf8.RUNE_ERROR { - n += write_byte(b, '\\'); - n += write_byte(b, 'x'); - n += write_byte(b, DIGITS_LOWER[buf[0]>>4]); - n += write_byte(b, DIGITS_LOWER[buf[0]&0xf]); + n += _write_byte(w, '\\'); + n += _write_byte(w, 'x'); + n += _write_byte(w, DIGITS_LOWER[buf[0]>>4]); + n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf]); } else { - n += write_escaped_rune(b, r, quote); + n += write_escaped_rune(w, r, quote); } - n += write_byte(b, quote); + n += _write_byte(w, quote); return; } -write_string :: proc(b: ^Builder, s: string) -> (n: int) { - return write_bytes(b, transmute([]byte)s); + +write_string :: proc{ + write_string_builder, + write_string_writer, +}; + +write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) { + return write_string_writer(to_writer(b), s); } +write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) { + n, _ = io.write(w, transmute([]byte)s); + return; +} + + + + pop_byte :: proc(b: ^Builder) -> (r: byte) { if len(b.buf) == 0 { return 0; @@ -190,8 +263,17 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) { @(private, static) DIGITS_LOWER := "0123456789abcdefx"; -write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) { - n += write_byte(b, quote); +write_quoted_string :: proc{ + write_quoted_string_builder, + write_quoted_string_writer, +}; + +write_quoted_string_builder :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) { + return write_quoted_string_writer(to_writer(b), str, quote); +} + +write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"') -> (n: int) { + n += _write_byte(w, quote); for width, s := 0, str; len(s) > 0; s = s[width:] { r := rune(s[0]); width = 1; @@ -199,57 +281,74 @@ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: r, width = utf8.decode_rune_in_string(s); } if width == 1 && r == utf8.RUNE_ERROR { - n += write_byte(b, '\\'); - n += write_byte(b, 'x'); - n += write_byte(b, DIGITS_LOWER[s[0]>>4]); - n += write_byte(b, DIGITS_LOWER[s[0]&0xf]); + n += _write_byte(w, '\\'); + n += _write_byte(w, 'x'); + n += _write_byte(w, DIGITS_LOWER[s[0]>>4]); + n += _write_byte(w, DIGITS_LOWER[s[0]&0xf]); continue; } - n += write_escaped_rune(b, r, quote); + n += write_escaped_rune(w, r, quote); } - n += write_byte(b, quote); + n += _write_byte(w, quote); return; } +write_encoded_rune :: proc{ + write_encoded_rune_builder, + write_encoded_rune_writer, +}; -write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) { +write_encoded_rune_builder :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) { + return write_encoded_rune_writer(to_writer(b), r, write_quote); + +} +write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) -> (n: int) { if write_quote { - n += write_byte(b, '\''); + n += _write_byte(w, '\''); } switch r { - case '\a': n += write_string(b, `\a"`); - case '\b': n += write_string(b, `\b"`); - case '\e': n += write_string(b, `\e"`); - case '\f': n += write_string(b, `\f"`); - case '\n': n += write_string(b, `\n"`); - case '\r': n += write_string(b, `\r"`); - case '\t': n += write_string(b, `\t"`); - case '\v': n += write_string(b, `\v"`); + case '\a': n += write_string(w, `\a"`); + case '\b': n += write_string(w, `\b"`); + case '\e': n += write_string(w, `\e"`); + case '\f': n += write_string(w, `\f"`); + case '\n': n += write_string(w, `\n"`); + case '\r': n += write_string(w, `\r"`); + case '\t': n += write_string(w, `\t"`); + case '\v': n += write_string(w, `\v"`); case: if r < 32 { - n += write_string(b, `\x`); + n += write_string(w, `\x`); buf: [2]byte; s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil); switch len(s) { - case 0: n += write_string(b, "00"); - case 1: n += write_byte(b, '0'); - case 2: n += write_string(b, s); + case 0: n += write_string(w, "00"); + case 1: n += _write_byte(w, '0'); + case 2: n += write_string(w, s); } } else { - n += write_rune(b, r); + n += write_rune(w, r); } } if write_quote { - n += write_byte(b, '\''); + n += _write_byte(w, '\''); } return; } -write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) { +write_escaped_rune :: proc{ + write_escaped_rune_builder, + write_escaped_rune_writer, +}; + +write_escaped_rune_builder :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) { + return write_escaped_rune_writer(to_writer(b), r, quote, html_safe); +} + +write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe := false) -> (n: int) { is_printable :: proc(r: rune) -> bool { if r <= 0xff { switch r { @@ -267,54 +366,54 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false if html_safe { switch r { case '<', '>', '&': - n += write_byte(b, '\\'); - n += write_byte(b, 'u'); + n += _write_byte(w, '\\'); + n += _write_byte(w, 'u'); for s := 12; s >= 0; s -= 4 { - n += write_byte(b, DIGITS_LOWER[r>>uint(s) & 0xf]); + n += _write_byte(w, DIGITS_LOWER[r>>uint(s) & 0xf]); } return; } } if r == rune(quote) || r == '\\' { - n += write_byte(b, '\\'); - n += write_byte(b, byte(r)); + n += _write_byte(w, '\\'); + n += _write_byte(w, byte(r)); return; } else if is_printable(r) { - n += write_encoded_rune(b, r, false); + n += write_encoded_rune(w, r, false); return; } switch r { - case '\a': n += write_string(b, `\a`); - case '\b': n += write_string(b, `\b`); - case '\e': n += write_string(b, `\e`); - case '\f': n += write_string(b, `\f`); - case '\n': n += write_string(b, `\n`); - case '\r': n += write_string(b, `\r`); - case '\t': n += write_string(b, `\t`); - case '\v': n += write_string(b, `\v`); + case '\a': n += write_string(w, `\a`); + case '\b': n += write_string(w, `\b`); + case '\e': n += write_string(w, `\e`); + case '\f': n += write_string(w, `\f`); + case '\n': n += write_string(w, `\n`); + case '\r': n += write_string(w, `\r`); + case '\t': n += write_string(w, `\t`); + case '\v': n += write_string(w, `\v`); case: switch c := r; { case c < ' ': - n += write_byte(b, '\\'); - n += write_byte(b, 'x'); - n += write_byte(b, DIGITS_LOWER[byte(c)>>4]); - n += write_byte(b, DIGITS_LOWER[byte(c)&0xf]); + n += _write_byte(w, '\\'); + n += _write_byte(w, 'x'); + n += _write_byte(w, DIGITS_LOWER[byte(c)>>4]); + n += _write_byte(w, DIGITS_LOWER[byte(c)&0xf]); case c > utf8.MAX_RUNE: c = 0xfffd; fallthrough; case c < 0x10000: - n += write_byte(b, '\\'); - n += write_byte(b, 'u'); + n += _write_byte(w, '\\'); + n += _write_byte(w, 'u'); for s := 12; s >= 0; s -= 4 { - n += write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]); + n += _write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf]); } case: - n += write_byte(b, '\\'); - n += write_byte(b, 'U'); + n += _write_byte(w, '\\'); + n += _write_byte(w, 'U'); for s := 28; s >= 0; s -= 4 { - n += write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]); + n += _write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf]); } } } diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin index 674fd9206..41add2778 100644 --- a/core/strings/conversion.odin +++ b/core/strings/conversion.odin @@ -8,7 +8,8 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> return ""; } - b := make_builder_len_cap(0, 0, allocator); + b: Builder; + init_builder(&b, 0, 0, allocator); s := s; for c, i in s { @@ -57,14 +58,16 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> } to_lower :: proc(s: string, allocator := context.allocator) -> string { - b := make_builder(0, len(s), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); for r in s { write_rune(&b, unicode.to_lower(r)); } return to_string(b); } to_upper :: proc(s: string, allocator := context.allocator) -> string { - b := make_builder(0, len(s), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); for r in s { write_rune(&b, unicode.to_upper(r)); } @@ -123,7 +126,8 @@ to_lower_camel_case :: to_camel_case; to_camel_case :: proc(s: string, allocator := context.allocator) -> string { s := s; s = trim_space(s); - b := make_builder(0, len(s), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) { if !is_delimiter(curr) { @@ -144,7 +148,8 @@ to_upper_camel_case :: to_pascal_case; to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { s := s; s = trim_space(s); - b := make_builder(0, len(s), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); string_case_iterator(&b, s, proc(b: ^Builder, prev, curr, next: rune) { if !is_delimiter(curr) { @@ -164,7 +169,8 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string { s := s; s = trim_space(s); - b := make_builder(0, len(s), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower; @@ -221,7 +227,8 @@ to_ada_case :: proc(s: string, allocator := context.allocator) -> string { s := s; s = trim_space(s); - b := make_builder(0, len(s), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); prev, curr: rune; diff --git a/core/strings/strings.odin b/core/strings/strings.odin index fd9e7299b..0306defc8 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -756,7 +756,8 @@ split_multi :: proc(s: string, substrs: []string, skip_empty := false, allocator // Adjacent invalid bytes are only replaced once scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string { str := s; - b := make_builder(0, len(str), allocator); + b: Builder; + init_builder(&b, 0, len(s), allocator); has_error := false; cursor := 0; @@ -811,7 +812,8 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> return ""; } - b := make_builder(allocator); + b: Builder; + init_builder(&b, allocator); str := s; column: int; @@ -868,7 +870,8 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte remains := length-1; pad_len := rune_count(pad); - b := make_builder(allocator); + b: Builder; + init_builder(&b, allocator); grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); write_pad_string(&b, pad, pad_len, remains/2); @@ -888,7 +891,8 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context remains := length-1; pad_len := rune_count(pad); - b := make_builder(allocator); + b: Builder; + init_builder(&b, allocator); grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); write_string(&b, str); @@ -907,7 +911,8 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex remains := length-1; pad_len := rune_count(pad); - b := make_builder(allocator); + b: Builder; + init_builder(&b, allocator); grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); write_pad_string(&b, pad, pad_len, remains);