From 594238a86cf785dda79461968e51d191ed9a1af4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 6 Jan 2019 14:41:42 +0000 Subject: [PATCH] Reorganize fmt and strings; Replace fmt.String_Buffer with strings.Builder --- core/fmt/fmt.odin | 2349 ++++++++++++++--------------- core/log/file_console_logger.odin | 37 +- core/os/os_essence.odin | 4 + core/os/os_linux.odin | 4 + core/os/os_osx.odin | 4 + core/os/os_windows.odin | 4 + core/strings/builder.odin | 107 ++ examples/demo/demo.odin | 2 +- 8 files changed, 1282 insertions(+), 1229 deletions(-) create mode 100644 core/strings/builder.odin diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index bcca57049..86772a281 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -7,14 +7,13 @@ import "core:bits" import "core:unicode/utf8" import "core:types" import "core:strconv" +import "core:strings" @(private) DEFAULT_BUFFER_SIZE :: 1<<12; -String_Buffer :: distinct [dynamic]byte; - -Fmt_Info :: struct { +Info :: struct { minus: bool, plus: bool, space: bool, @@ -30,89 +29,13 @@ Fmt_Info :: struct { reordered: bool, good_arg_index: bool, - buf: ^String_Buffer, + buf: ^strings.Builder, arg: any, // Temporary } - -string_buffer_from_slice :: proc(backing: []byte) -> String_Buffer { - s := transmute(mem.Raw_Slice)backing; - d := mem.Raw_Dynamic_Array{ - data = s.data, - len = 0, - cap = s.len, - allocator = mem.nil_allocator(), - }; - return transmute(String_Buffer)d; -} - - -to_string :: proc(buf: String_Buffer) -> string { - return string(buf[:]); -} - - -write_string :: proc(buf: ^String_Buffer, s: string) { - append_string(buf, s); -} -write_bytes :: proc(buf: ^String_Buffer, data: []byte) { - append(buf, ..data); -} -write_byte :: proc(buf: ^String_Buffer, data: byte) { - append(buf, data); -} -write_rune :: proc(buf: ^String_Buffer, r: rune) { - if r < utf8.RUNE_SELF { - write_byte(buf, byte(r)); - return; - } - - b, n := utf8.encode_rune(r); - write_bytes(buf, b[:n]); -} -write_encoded_rune :: proc(buf: ^String_Buffer, r: rune) { - write_byte(buf, '\''); - switch r { - case '\a': write_string(buf, "\\a"); - case '\b': write_string(buf, "\\b"); - case '\e': write_string(buf, "\\e"); - case '\f': write_string(buf, "\\f"); - case '\n': write_string(buf, "\\n"); - case '\r': write_string(buf, "\\r"); - case '\t': write_string(buf, "\\t"); - case '\v': write_string(buf, "\\v"); - case: - if r < 32 { - write_string(buf, "\\x"); - b: [2]byte; - s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil); - switch len(s) { - case 0: write_string(buf, "00"); - case 1: write_rune(buf, '0'); - case 2: write_string(buf, s); - } - } else { - write_rune(buf, r); - } - - } - write_byte(buf, '\''); -} - -write_u64 :: proc(buf: ^String_Buffer, i: u64, base: int) { - b: [129]byte; - s := strconv.append_bits(b[:], u64(i), base, false, 64, strconv.digits, nil); - write_string(buf, s); -} -write_i64 :: proc(buf: ^String_Buffer, i: i64, base: int) { - b: [129]byte; - s := strconv.append_bits(b[:], u64(i), base, true, 64, strconv.digits, nil); - write_string(buf, s); -} - fprint :: proc(fd: os.Handle, args: ..any) -> int { data: [DEFAULT_BUFFER_SIZE]byte; - buf := string_buffer_from_slice(data[:]); + buf := strings.builder_from_slice(data[:]); res := sbprint(&buf, ..args); os.write_string(fd, res); return len(res); @@ -120,14 +43,14 @@ fprint :: proc(fd: os.Handle, args: ..any) -> int { fprintln :: proc(fd: os.Handle, args: ..any) -> int { data: [DEFAULT_BUFFER_SIZE]byte; - buf := string_buffer_from_slice(data[:]); + buf := strings.builder_from_slice(data[:]); res := sbprintln(&buf, ..args); os.write_string(fd, res); return len(res); } fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int { data: [DEFAULT_BUFFER_SIZE]byte; - buf := string_buffer_from_slice(data[:]); + buf := strings.builder_from_slice(data[:]); res := sbprintf(&buf, fmt, ..args); os.write_string(fd, res); return len(res); @@ -146,51 +69,51 @@ printf_err :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, // aprint* procedures return a string that was allocated with the current context // They must be freed accordingly aprint :: proc(args: ..any) -> string { - buf := String_Buffer(make([dynamic]byte)); - sbprint(&buf, ..args); - return to_string(buf); + str := strings.make_builder(); + sbprint(&str, ..args); + return strings.to_string(str); } aprintln :: proc(args: ..any) -> string { - buf := String_Buffer(make([dynamic]byte)); - sbprintln(&buf, ..args); - return to_string(buf); + str := strings.make_builder(); + sbprintln(&str, ..args); + return strings.to_string(str); } aprintf :: proc(fmt: string, args: ..any) -> string { - buf := String_Buffer(make([dynamic]byte)); - sbprintf(&buf, fmt, ..args); - return to_string(buf); + str := strings.make_builder(); + sbprintf(&str, fmt, ..args); + return strings.to_string(str); } // tprint* procedures return a string that was allocated with the current context's temporary allocator tprint :: proc(args: ..any) -> string { - buf := String_Buffer(make([dynamic]byte, context.temp_allocator)); - sbprint(&buf, ..args); - return to_string(buf); + str := strings.make_builder(context.temp_allocator); + sbprint(&str, ..args); + return strings.to_string(str); } tprintln :: proc(args: ..any) -> string { - buf := String_Buffer(make([dynamic]byte, context.temp_allocator)); - sbprintln(&buf, ..args); - return to_string(buf); + str := strings.make_builder(context.temp_allocator); + sbprintln(&str, ..args); + return strings.to_string(str); } tprintf :: proc(fmt: string, args: ..any) -> string { - buf := String_Buffer(make([dynamic]byte, context.temp_allocator)); - sbprintf(&buf, fmt, ..args); - return to_string(buf); + str := strings.make_builder(context.temp_allocator); + sbprintf(&str, fmt, ..args); + return strings.to_string(str); } // bprint* procedures return a string using a buffer from an array bprint :: proc(buf: []byte, args: ..any) -> string { - sb := string_buffer_from_slice(buf[0:len(buf)]); + sb := strings.builder_from_slice(buf[0:len(buf)]); return sbprint(&sb, ..args); } bprintln :: proc(buf: []byte, args: ..any) -> string { - sb := string_buffer_from_slice(buf[0:len(buf)]); + sb := strings.builder_from_slice(buf[0:len(buf)]); return sbprintln(&sb, ..args); } bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string { - sb := string_buffer_from_slice(buf[0:len(buf)]); + sb := strings.builder_from_slice(buf[0:len(buf)]); return sbprintf(&sb, fmt, ..args); } @@ -216,22 +139,1122 @@ panicf :: proc "contextless" (fmt: string, args: ..any, loc := #caller_location) p("Panic", message, loc); } - - - - fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) { data: [DEFAULT_BUFFER_SIZE]byte; - buf := string_buffer_from_slice(data[:]); + buf := strings.builder_from_slice(data[:]); write_type(&buf, info); - os.write(fd, buf[:]); + os.write_string(fd, strings.to_string(buf)); } -write_typeid :: proc(buf: ^String_Buffer, id: typeid) { + + +sbprint :: proc(buf: ^strings.Builder, args: ..any) -> string { + fi: Info; + prev_string := false; + + fi.buf = buf; + + for arg, i in args { + is_string := arg != nil && types.is_string(type_info_of(arg.id)); + if i > 0 && !is_string && !prev_string { + strings.write_byte(buf, ' '); + } + fmt_value(&fi, args[i], 'v'); + prev_string = is_string; + } + return strings.to_string(buf^); +} + +sbprintln :: proc(buf: ^strings.Builder, args: ..any) -> string { + fi: Info; + fi.buf = buf; + + for _, i in args { + if i > 0 do strings.write_byte(buf, ' '); + + fmt_value(&fi, args[i], 'v'); + } + strings.write_byte(buf, '\n'); + return strings.to_string(buf^); +} + +sbprintf :: proc(b: ^strings.Builder, fmt: string, args: ..any) -> string { + 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}; + + prev_i := i; + for i < end && fmt[i] != '%' { + i += 1; + } + if i > prev_i { + strings.write_string(b, fmt[prev_i:i]); + } + if i >= end { + break loop; + } + + // Process a "verb" + i += 1; + + prefix_loop: for ; i < end; i += 1 { + switch fmt[i] { + case '+': + fi.plus = true; + case '-': + fi.minus = true; + fi.zero = false; + case ' ': + fi.space = true; + case '#': + fi.hash = true; + case '0': + fi.zero = !fi.minus; + case: + break prefix_loop; + } + } + + arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args)); + + // Width + if i < end && fmt[i] == '*' { + 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)"); + } + + if fi.width < 0 { + fi.width = -fi.width; + fi.minus = true; + fi.zero = false; + } + was_prev_index = false; + } else { + fi.width, i, fi.width_set = _parse_int(fmt, i); + if was_prev_index && fi.width_set { // %[6]2d + fi.good_arg_index = false; + } + } + + // Precision + if i < end && fmt[i] == '.' { + i += 1; + if was_prev_index { // %[6].2d + fi.good_arg_index = false; + } + if i < end && fmt[i] == '*' { + arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args)); + i += 1; + fi.prec, arg_index, fi.prec_set = int_from_arg(args, arg_index); + if fi.prec < 0 { + fi.prec = 0; + fi.prec_set = false; + } + if !fi.prec_set { + strings.write_string(fi.buf, "%!(BAD PRECISION)"); + } + was_prev_index = false; + } else { + fi.prec, i, fi.prec_set = _parse_int(fmt, i); + if !fi.prec_set { + // fi.prec_set = true; + // fi.prec = 0; + } + } + } + + if !was_prev_index { + arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args)); + } + + if i >= end { + strings.write_string(b, "%!(NO VERB)"); + break loop; + } + + verb, w := utf8.decode_rune_in_string(fmt[i:]); + i += w; + + switch { + case verb == '%': + strings.write_byte(b, '%'); + case !fi.good_arg_index: + strings.write_string(b, "%!(BAD ARGUMENT NUMBER)"); + case arg_index >= len(args): + strings.write_string(b, "%!(MISSING ARGUMENT)"); + case: + fmt_arg(&fi, args[arg_index], verb); + arg_index += 1; + } + } + + if !fi.reordered && arg_index < len(args) { + strings.write_string(b, "%!(EXTRA "); + for arg, index in args[arg_index:] { + if index > 0 do strings.write_string(b, ", "); + + if arg == nil do strings.write_string(b, ""); + else do fmt_arg(&fi, args[index], 'v'); + } + strings.write_string(b, ")"); + } + + return strings.to_string(b^); +} + + + + + + + +_parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok: bool) { + is_digit :: inline proc(r: byte) -> bool { return '0' <= r && r <= '9' } + + new_offset = offset; + for new_offset <= len(s) { + c := s[new_offset]; + if !is_digit(c) do break; + new_offset += 1; + + result *= 10; + result += int(c)-'0'; + } + ok = new_offset > offset; + return; +} + +_arg_number :: proc(fi: ^Info, arg_index: int, format: string, offset, arg_count: int) -> (index, new_offset: int, ok: bool) { + parse_arg_number :: proc(format: string) -> (int, int, bool) { + if len(format) < 3 do return 0, 1, false; + + for i in 1..len(format)-1 { + if format[i] == ']' { + width, new_index, ok := _parse_int(format, 1); + if !ok || new_index != i { + return 0, i+1, false; + } + return width-1, i+1, true; + } + } + + return 0, 1, false; + } + + + if len(format) <= offset || format[offset] != '[' { + return arg_index, offset, false; + } + fi.reordered = true; + + width: int; + index, width, ok = parse_arg_number(format[offset:]); + if ok && 0 <= index && index < arg_count { + return index, offset+width, true; + } + fi.good_arg_index = false; + return arg_index, offset+width, false; +} + +int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) { + num := 0; + new_arg_index := arg_index; + ok := true; + if arg_index < len(args) { + arg := args[arg_index]; + arg.id = runtime.typeid_base(arg.id); + switch i in arg { + case int: num = i; + case i8: num = int(i); + case i16: num = int(i); + case i32: num = int(i); + case i64: num = int(i); + case u8: num = int(i); + case u16: num = int(i); + case u32: num = int(i); + case u64: num = int(i); + case: + ok = false; + } + } + + if ok { + new_arg_index += 1; + } + + return num, new_arg_index, ok; +} + + +fmt_bad_verb :: proc(using fi: ^Info, verb: rune) { + strings.write_string(buf, "%!"); + strings.write_rune(buf, verb); + strings.write_byte(buf, '('); + if arg.id != nil { + write_typeid(buf, arg.id); + strings.write_byte(buf, '='); + fmt_value(fi, arg, 'v'); + } else { + strings.write_string(buf, ""); + } + strings.write_byte(buf, ')'); +} + +fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) { + switch verb { + case 't', 'v': + strings.write_string(buf, b ? "true" : "false"); + case: + fmt_bad_verb(fi, verb); + } +} + + +fmt_write_padding :: proc(fi: ^Info, width: int) { + if width <= 0 do return; + + pad_byte: byte = '0'; + if fi.space do pad_byte = ' '; + + for i := 0; i < width; i += 1 { + strings.write_byte(fi.buf, pad_byte); + } +} + +_fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, digits: string) { + _, neg := strconv.is_integer_negative(u, is_signed, bit_size); + + BUF_SIZE :: 256; + if fi.width_set || fi.prec_set { + width := fi.width + fi.prec + 3; // 3 extra bytes for sign and prefix + if width > BUF_SIZE { + // TODO(bill):???? + panic("_fmt_int: buffer overrun. Width and precision too big"); + } + } + + prec := 0; + if fi.prec_set { + prec = fi.prec; + if prec == 0 && u == 0 { + prev_zero := fi.zero; + fi.zero = false; + fmt_write_padding(fi, fi.width); + fi.zero = prev_zero; + return; + } + } else if fi.zero && fi.width_set { + prec = fi.width; + if neg || fi.plus || fi.space { + // There needs to be space for the "sign" + prec -= 1; + } + } + + switch base { + case 2, 8, 10, 12, 16: + break; + case: + panic("_fmt_int: unknown base, whoops"); + } + + buf: [256]byte; + start := 0; + + using strconv.Int_Flag; + flags: strconv.Int_Flags; + if fi.hash && !fi.zero do flags |= {Prefix}; + if fi.plus do flags |= {Plus}; + if fi.space do flags |= {Space}; + s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags); + + if fi.hash && fi.zero { + c: byte = 0; + switch base { + case 2: c = 'b'; + case 8: c = 'o'; + case 12: c = 'z'; + case 16: c = 'x'; + } + if c != 0 { + strings.write_byte(fi.buf, '0'); + strings.write_byte(fi.buf, c); + } + } + + prev_zero := fi.zero; + defer fi.zero = prev_zero; + fi.zero = false; + _pad(fi, s); +} + + +__DIGITS_LOWER := "0123456789abcdefx"; +__DIGITS_UPPER := "0123456789ABCDEFX"; + +fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) { + switch verb { + case 'c', 'r', 'v': + strings.write_rune(fi.buf, r); + case: + fmt_int(fi, u64(r), false, 32, verb); + } +} + +fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) { + switch verb { + case 'v': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER); + case 'b': _fmt_int(fi, u, 2, is_signed, bit_size, __DIGITS_LOWER); + case 'o': _fmt_int(fi, u, 8, is_signed, bit_size, __DIGITS_LOWER); + case 'd': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER); + case 'z': _fmt_int(fi, u, 12, is_signed, bit_size, __DIGITS_LOWER); + case 'x': _fmt_int(fi, u, 16, is_signed, bit_size, __DIGITS_LOWER); + case 'X': _fmt_int(fi, u, 16, is_signed, bit_size, __DIGITS_UPPER); + case 'c', 'r': + fmt_rune(fi, rune(u), verb); + case 'U': + r := rune(u); + if r < 0 || r > utf8.MAX_RUNE { + fmt_bad_verb(fi, verb); + } else { + strings.write_string(fi.buf, "U+"); + _fmt_int(fi, u, 16, false, bit_size, __DIGITS_UPPER); + } + + case: + fmt_bad_verb(fi, verb); + } +} + +_pad :: proc(fi: ^Info, s: string) { + if !fi.width_set { + strings.write_string(fi.buf, s); + return; + } + + + width := fi.width - utf8.rune_count_in_string(s); + if fi.minus { // right pad + strings.write_string(fi.buf, s); + fmt_write_padding(fi, width); + } else { // left pad + fmt_write_padding(fi, width); + strings.write_string(fi.buf, s); + } +} + +fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { + switch verb { + // case 'e', 'E', 'f', 'F', 'g', 'G', 'v': + // case 'f', 'F', 'v': + + case 'f', 'F', 'v': + prec: int = 3; + if fi.prec_set do prec = fi.prec; + buf: [386]byte; + + str := strconv.append_float(buf[1:], v, 'f', prec, bit_size); + str = string(buf[:len(str)+1]); + if str[1] == '+' || str[1] == '-' { + str = str[1:]; + } else { + str[0] = '+'; + } + + if fi.space && !fi.plus && str[0] == '+' { + str[0] = ' '; + } + + if len(str) > 1 && (str[1] == 'N' || str[1] == 'I') { + strings.write_string(fi.buf, str); + return; + } + + if fi.plus || str[0] != '+' { + if fi.zero && fi.width_set && fi.width > len(str) { + strings.write_byte(fi.buf, str[0]); + fmt_write_padding(fi, fi.width - len(str)); + strings.write_string(fi.buf, str[1:]); + } else { + _pad(fi, str); + } + } else { + _pad(fi, str[1:]); + } + + case: + fmt_bad_verb(fi, verb); + } +} +fmt_string :: proc(fi: ^Info, s: string, verb: rune) { + switch verb { + case 's', 'v': + strings.write_string(fi.buf, s); + + case 'x', 'X': + space := fi.space; + fi.space = false; + defer fi.space = space; + + for i in 0..len(s)-1 { + if i > 0 && space do strings.write_byte(fi.buf, ' '); + char_set := __DIGITS_UPPER; + if verb == 'x' do char_set = __DIGITS_LOWER; + _fmt_int(fi, u64(s[i]), 16, false, 8, char_set); + } + + case: + fmt_bad_verb(fi, verb); + } +} +fmt_cstring :: proc(fi: ^Info, s: cstring, verb: rune) { + fmt_string(fi, string(s), verb); +} + +fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) { + switch verb { + case 'p', 'v': + u := u64(uintptr(p)); + if !fi.hash || verb == 'v' { + strings.write_string(fi.buf, "0x"); + } + _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER); + case: + fmt_bad_verb(fi, verb); + } +} + +enum_value_to_string :: proc(v: any) -> (string, bool) { + v.id = runtime.typeid_base(v.id); + type_info := type_info_of(v.id); + + switch e in type_info.variant { + case: return "", false; + case runtime.Type_Info_Enum: + get_str :: proc(i: $T, e: runtime.Type_Info_Enum) -> (string, bool) { + if types.is_string(e.base) { + for val, idx in e.values { + if v, ok := val.(T); ok && v == i { + return e.names[idx], true; + } + } + } else if len(e.values) == 0 { + return "", true; + } else { + for val, idx in e.values { + if v, ok := val.(T); ok && v == i { + return e.names[idx], true; + } + } + } + return "", false; + } + + a := any{v.data, runtime.type_info_base(e.base).id}; + switch v in a { + case rune: return get_str(v, e); + case i8: return get_str(v, e); + case i16: return get_str(v, e); + case i32: return get_str(v, e); + case i64: return get_str(v, e); + case int: return get_str(v, e); + case u8: return get_str(v, e); + case u16: return get_str(v, e); + case u32: return get_str(v, e); + case u64: return get_str(v, e); + case uint: return get_str(v, e); + case uintptr: return get_str(v, e); + } + } + + return "", false; +} + +string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) { + ti := type_info_base(type_info_of(T)); + if e, ok := ti.variant.(Type_Info_Enum); ok { + for str, idx in e.names { + if s == str { + // NOTE(bill): Unsafe cast + ptr := cast(^T)&e.values[idx]; + return ptr^, true; + } + } + } + return T{}, false; +} + +fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { + if v.id == nil || v.data == nil { + strings.write_string(fi.buf, ""); + return; + } + + type_info := type_info_of(v.id); + switch e in type_info.variant { + case: fmt_bad_verb(fi, verb); + case runtime.Type_Info_Enum: + switch verb { + case: fmt_bad_verb(fi, verb); + case 'd', 'f': + fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, verb); + case 's', 'v': + str, ok := enum_value_to_string(v); + if !ok do str = "!%(BAD ENUM VALUE)"; + strings.write_string(fi.buf, str); + } + } +} + + + +enum_value_to_u64 :: proc(ev: runtime.Type_Info_Enum_Value) -> u64 { + switch i in ev { + case rune: return u64(i); + case i8: return u64(i); + case i16: return u64(i); + case i32: return u64(i); + case i64: return u64(i); + case int: return u64(i); + case u8: return u64(i); + case u16: return u64(i); + case u32: return u64(i); + case u64: return u64(i); + case uint: return u64(i); + case uintptr: return u64(i); + } + return 0; +} + +fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { + is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info, ) -> bool { + if ti == nil { + return false; + } + ti = runtime.type_info_base(ti); + switch info in ti.variant { + case runtime.Type_Info_Integer: + using runtime.Type_Info_Endianness; + switch info.endianness { + case Platform: return false; + case Little: return ODIN_ENDIAN != "little"; + case Big: return ODIN_ENDIAN != "big"; + } + } + return false; + } + + byte_swap :: bits.byte_swap; + + type_info := type_info_of(v.id); + switch info in type_info.variant { + case runtime.Type_Info_Named: + val := v; + val.id = info.base.id; + fmt_bit_set(fi, val, info.name); + + case runtime.Type_Info_Bit_Set: + bits: u64; + bit_size := u64(8*type_info.size); + + do_byte_swap := is_bit_set_different_endian_to_platform(info.underlying); + + switch bit_size { + case 0: bits = 0; + case 8: + x := (^u8)(v.data)^; + bits = u64(x); + case 16: + x := (^u16)(v.data)^; + if do_byte_swap do x = byte_swap(x); + bits = u64(x); + case 32: + x := (^u32)(v.data)^; + if do_byte_swap do x = byte_swap(x); + bits = u64(x); + case 64: + x := (^u64)(v.data)^; + if do_byte_swap do x = byte_swap(x); + bits = u64(x); + case: panic("unknown bit_size size"); + } + + et := runtime.type_info_base(info.elem); + + if name != "" { + strings.write_string(fi.buf, name); + } else { + write_type(fi.buf, type_info); + } + strings.write_byte(fi.buf, '{'); + defer strings.write_byte(fi.buf, '}'); + + e, is_enum := et.variant.(runtime.Type_Info_Enum); + commas := 0; + loop: for i in 0 .. bit_size-1 { + if bits & (1< 0 do strings.write_string(fi.buf, ", "); + + if is_enum do for ev, evi in e.values { + v := enum_value_to_u64(ev); + if v == i { + strings.write_string(fi.buf, e.names[evi]); + commas += 1; + continue loop; + } + } + strings.write_i64(fi.buf, i64(i), 10); + commas += 1; + } + } +} +fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") { + type_info := type_info_of(v.id); + switch info in type_info.variant { + case runtime.Type_Info_Named: + val := v; + val.id = info.base.id; + fmt_bit_field(fi, val, info.name); + case runtime.Type_Info_Bit_Field: + data: u64 = 0; + switch type_info.size { + case 1: data = cast(u64) (^u8)(v.data)^; + case 2: data = cast(u64)(^u16)(v.data)^; + case 4: data = cast(u64)(^u32)(v.data)^; + case 8: data = cast(u64)(^u64)(v.data)^; + } + + if bit_field_name != "" { + strings.write_string(fi.buf, bit_field_name); + strings.write_byte(fi.buf, '{'); + } else { + strings.write_string(fi.buf, "bit_field{"); + } + for name, i in info.names { + if i > 0 { + strings.write_string(fi.buf, ", "); + } + bits := u64(info.bits[i]); + offset := u64(info.offsets[i]); + strings.write_string(fi.buf, name); + strings.write_string(fi.buf, " = "); + + n := 8*u64(size_of(u64)); + sa := n - bits; + u := data>>offset; + u <<= sa; + u >>= sa; + + strings.write_u64(fi.buf, u, 10); + + } + strings.write_byte(fi.buf, '}'); + case: + strings.write_string(fi.buf, "HERE"); + } +} + +fmt_opaque :: proc(fi: ^Info, v: any) { + is_nil :: proc(data: rawptr, n: int) -> bool { + if data == nil do return true; + if n == 0 do return true; + + a := (^byte)(data); + for i in 0..n-1 do if mem.ptr_offset(a, i)^ != 0 { + return false; + } + return true; + } + + rt :: runtime; + + type_info := type_info_of(v.id); + + if is_nil(v.data, type_info.size) { + strings.write_string(fi.buf, "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 do return; + write_type(fi.buf, type_info); + strings.write_byte(fi.buf, '{'); + defer strings.write_byte(fi.buf, '}'); + + switch in elem.variant { + case rt.Type_Info_Integer, rt.Type_Info_Pointer, rt.Type_Info_Float: + fmt_value(fi, any{v.data, elem.id}, 'v'); + case: + // Okay + } + } else { + write_type(fi.buf, type_info); + strings.write_byte(fi.buf, '{'); + strings.write_byte(fi.buf, '}'); + } +} + +fmt_value :: proc(fi: ^Info, v: any, verb: rune) { + if v.data == nil || v.id == nil { + strings.write_string(fi.buf, ""); + return; + } + + type_info := type_info_of(v.id); + switch info in type_info.variant { + case runtime.Type_Info_Named: + switch b in info.base.variant { + case runtime.Type_Info_Struct: + if verb != 'v' { + fmt_bad_verb(fi, verb); + return; + } + if b.is_raw_union { + strings.write_string(fi.buf, info.name); + strings.write_string(fi.buf, "{}"); + return; + }; + strings.write_string(fi.buf, info.name); + strings.write_byte(fi.buf, '{'); + + hash := fi.hash; defer fi.hash = hash; + indent := fi.indent; defer fi.indent -= 1; + + fi.hash = false; + fi.indent += 1; + + if hash do strings.write_byte(fi.buf, '\n'); + + field_count := -1; + for name, i in b.names { + // if len(name) > 0 && name[0] == '_' do continue; + field_count += 1; + + if !hash && field_count > 0 do strings.write_string(fi.buf, ", "); + if hash do for in 0..fi.indent-1 do strings.write_byte(fi.buf, '\t'); + + strings.write_string(fi.buf, name); + strings.write_string(fi.buf, " = "); + + if t := b.types[i]; types.is_any(t) { + strings.write_string(fi.buf, "any{}"); + } else { + data := rawptr(uintptr(v.data) + b.offsets[i]); + fmt_arg(fi, any{data, t.id}, 'v'); + } + + if hash do strings.write_string(fi.buf, ",\n"); + } + + if hash do for in 0..indent-1 do strings.write_byte(fi.buf, '\t'); + strings.write_byte(fi.buf, '}'); + + case runtime.Type_Info_Bit_Set: + fmt_bit_set(fi, v); + case runtime.Type_Info_Bit_Field: + fmt_bit_field(fi, v); + case runtime.Type_Info_Opaque: + fmt_opaque(fi, v); + case: + fmt_value(fi, any{v.data, info.base.id}, verb); + } + + case runtime.Type_Info_Boolean: fmt_arg(fi, v, verb); + case runtime.Type_Info_Integer: fmt_arg(fi, v, verb); + case runtime.Type_Info_Rune: fmt_arg(fi, v, verb); + case runtime.Type_Info_Float: fmt_arg(fi, v, verb); + case runtime.Type_Info_Complex: fmt_arg(fi, v, verb); + case runtime.Type_Info_String: fmt_arg(fi, v, verb); + + case runtime.Type_Info_Pointer: + if v.id == typeid_of(^runtime.Type_Info) { + write_type(fi.buf, (^^runtime.Type_Info)(v.data)^); + } else { + fmt_pointer(fi, (^rawptr)(v.data)^, verb); + } + + case runtime.Type_Info_Array: + strings.write_byte(fi.buf, '['); + defer strings.write_byte(fi.buf, ']'); + for i in 0..info.count-1 { + if i > 0 do strings.write_string(fi.buf, ", "); + + data := uintptr(v.data) + uintptr(i*info.elem_size); + fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); + } + + case runtime.Type_Info_Dynamic_Array: + strings.write_byte(fi.buf, '['); + defer strings.write_byte(fi.buf, ']'); + array := cast(^mem.Raw_Dynamic_Array)v.data; + for i in 0..array.len-1 { + if i > 0 do strings.write_string(fi.buf, ", "); + + data := uintptr(array.data) + uintptr(i*info.elem_size); + fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); + } + + case runtime.Type_Info_Slice: + strings.write_byte(fi.buf, '['); + defer strings.write_byte(fi.buf, ']'); + slice := cast(^mem.Raw_Slice)v.data; + for i in 0..slice.len-1 { + if i > 0 do strings.write_string(fi.buf, ", "); + + data := uintptr(slice.data) + uintptr(i*info.elem_size); + fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); + } + + case runtime.Type_Info_Map: + if verb != 'v' { + fmt_bad_verb(fi, verb); + return; + } + + strings.write_string(fi.buf, "map["); + defer strings.write_byte(fi.buf, ']'); + + m := (^mem.Raw_Map)(v.data); + if m != nil { + if info.generated_struct == nil { + return; + } + entries := &m.entries; + gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct); + ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array); + entry_type := ed.elem.variant.(runtime.Type_Info_Struct); + entry_size := ed.elem_size; + + for i in 0..entries.len-1 { + if i > 0 do strings.write_string(fi.buf, ", "); + + data := uintptr(entries.data) + uintptr(i*entry_size); + header := cast(^runtime.Map_Entry_Header)data; + + if types.is_string(info.key) { + strings.write_string(fi.buf, header.key.str); + } else { + fi := Info{buf = fi.buf}; + fmt_arg(&fi, any{rawptr(&header.key.hash), info.key.id}, 'v'); + } + + strings.write_string(fi.buf, "="); + + value := data + entry_type.offsets[2]; + fmt_arg(fi, any{rawptr(value), info.value.id}, 'v'); + } + } + + case runtime.Type_Info_Struct: + if info.is_raw_union { + strings.write_string(fi.buf, "(raw_union)"); + return; + } + + strings.write_byte(fi.buf, '{'); + defer strings.write_byte(fi.buf, '}'); + + fi.indent += 1; defer fi.indent -= 1; + hash := fi.hash; defer fi.hash = hash; + fi.hash = false; + + + if hash do strings.write_byte(fi.buf, '\n'); + + for _, i in info.names { + if !hash && i > 0 do strings.write_string(fi.buf, ", "); + if hash { + for in 0..fi.indent-1 { + strings.write_byte(fi.buf, '\t'); + } + } + + strings.write_string(fi.buf, info.names[i]); + strings.write_string(fi.buf, " = "); + + if t := info.types[i]; types.is_any(t) { + strings.write_string(fi.buf, "any{}"); + } else { + data := uintptr(v.data) + info.offsets[i]; + fmt_arg(fi, any{rawptr(data), t.id}, 'v'); + } + if hash do strings.write_string(fi.buf, ",\n"); + } + + case runtime.Type_Info_Union: + tag_ptr := uintptr(v.data) + info.tag_offset; + tag_any := any{rawptr(tag_ptr), info.tag_type.id}; + + tag: i64 = -1; + switch i in tag_any { + case u8: tag = i64(i); + case i8: tag = i64(i); + case u16: tag = i64(i); + case i16: tag = i64(i); + case u32: tag = i64(i); + case i32: tag = i64(i); + case u64: tag = i64(i); + case i64: tag = i64(i); + case: panic("Invalid union tag type"); + } + + if v.data == nil || tag == 0 { + strings.write_string(fi.buf, "nil"); + } else { + id := info.variants[tag-1].id; + fmt_arg(fi, any{v.data, id}, verb); + } + + case runtime.Type_Info_Enum: + fmt_enum(fi, v, verb); + + case runtime.Type_Info_Procedure: + ptr := (^rawptr)(v.data)^; + if ptr == nil { + strings.write_string(fi.buf, "nil"); + } else { + write_typeid(fi.buf, v.id); + strings.write_string(fi.buf, " @ "); + fmt_pointer(fi, ptr, 'p'); + } + + case runtime.Type_Info_Type_Id: + id := (^typeid)(v.data)^; + write_typeid(fi.buf, id); + + case runtime.Type_Info_Bit_Field: + fmt_bit_field(fi, v); + + case runtime.Type_Info_Bit_Set: + fmt_bit_set(fi, v); + + case runtime.Type_Info_Opaque: + fmt_opaque(fi, v); + } +} + +fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { + switch verb { + case 'f', 'F', 'v': + r, i := real(c), imag(c); + fmt_float(fi, r, bits/2, verb); + if !fi.plus && i >= 0 { + strings.write_rune(fi.buf, '+'); + } + fmt_float(fi, i, bits/2, verb); + strings.write_rune(fi.buf, 'i'); + + case: + fmt_bad_verb(fi, verb); + return; + } +} + +fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { + if arg == nil { + strings.write_string(fi.buf, ""); + return; + } + fi.arg = arg; + + if verb == 'T' { + ti := type_info_of(arg.id); + switch a in arg { + case ^runtime.Type_Info: ti = a; + } + write_type(fi.buf, ti); + return; + } + + + 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, ')'); + return; + } + } + + base_arg := arg; + base_arg.id = runtime.typeid_base(base_arg.id); + switch a in base_arg { + case bool: fmt_bool(fi, bool(a), verb); + case b8: fmt_bool(fi, bool(a), verb); + case b16: fmt_bool(fi, bool(a), verb); + case b32: fmt_bool(fi, bool(a), verb); + case b64: fmt_bool(fi, bool(a), verb); + + case any: fmt_arg(fi, a, verb); + case rune: fmt_rune(fi, a, verb); + + case f32: fmt_float(fi, f64(a), 32, verb); + case f64: fmt_float(fi, a, 64, verb); + + case complex64: fmt_complex(fi, complex128(a), 64, verb); + case complex128: fmt_complex(fi, a, 128, verb); + + case i8: fmt_int(fi, u64(a), true, 8, verb); + case u8: fmt_int(fi, u64(a), false, 8, verb); + case i16: fmt_int(fi, u64(a), true, 16, verb); + case u16: fmt_int(fi, u64(a), false, 16, verb); + case i32: fmt_int(fi, u64(a), true, 32, verb); + case u32: fmt_int(fi, u64(a), false, 32, verb); + case i64: fmt_int(fi, u64(a), true, 64, verb); + case u64: fmt_int(fi, u64(a), false, 64, verb); + case int: fmt_int(fi, u64(a), true, 8*size_of(int), verb); + case uint: fmt_int(fi, u64(a), false, 8*size_of(uint), verb); + case uintptr: fmt_int(fi, u64(a), false, 8*size_of(uintptr), verb); + + case string: fmt_string(fi, a, verb); + case cstring: fmt_cstring(fi, a, verb); + + case typeid: write_typeid(fi.buf, a); + + case i16le: fmt_int(fi, u64(a), true, 16, verb); + case u16le: fmt_int(fi, u64(a), false, 16, verb); + case i32le: fmt_int(fi, u64(a), true, 32, verb); + case u32le: fmt_int(fi, u64(a), false, 32, verb); + case i64le: fmt_int(fi, u64(a), true, 64, verb); + case u64le: fmt_int(fi, u64(a), false, 64, verb); + + case i16be: fmt_int(fi, u64(a), true, 16, verb); + case u16be: fmt_int(fi, u64(a), false, 16, verb); + case i32be: fmt_int(fi, u64(a), true, 32, verb); + case u32be: fmt_int(fi, u64(a), false, 32, verb); + case i64be: fmt_int(fi, u64(a), true, 64, verb); + case u64be: fmt_int(fi, u64(a), false, 64, verb); + + case: fmt_value(fi, arg, verb); + } + +} + + + + + +write_typeid :: proc(buf: ^strings.Builder, id: typeid) { write_type(buf, type_info_of(id)); } -write_type :: proc(buf: ^String_Buffer, ti: ^runtime.Type_Info) { +write_type :: proc(buf: ^strings.Builder, ti: ^runtime.Type_Info) { + using strings; if ti == nil { write_string(buf, "nil"); return; @@ -364,7 +1387,7 @@ write_type :: proc(buf: ^String_Buffer, ti: ^runtime.Type_Info) { if i > 0 do write_string(buf, ", "); write_type(buf, variant); } - write_string(buf, "}"); + write_byte(buf, '}'); case runtime.Type_Info_Enum: write_string(buf, "enum "); @@ -374,14 +1397,14 @@ write_type :: proc(buf: ^String_Buffer, ti: ^runtime.Type_Info) { if i > 0 do write_string(buf, ", "); write_string(buf, name); } - write_string(buf, "}"); + write_byte(buf, '}'); case runtime.Type_Info_Bit_Field: write_string(buf, "bit_field "); if ti.align != 1 { write_string(buf, "#align "); write_i64(buf, i64(ti.align), 10); - write_rune(buf, ' '); + write_byte(buf, ' '); } write_string(buf, " {"); for name, i in info.names { @@ -390,7 +1413,7 @@ write_type :: proc(buf: ^String_Buffer, ti: ^runtime.Type_Info) { write_string(buf, ": "); write_i64(buf, i64(info.bits[i]), 10); } - write_string(buf, "}"); + write_byte(buf, '}'); case runtime.Type_Info_Bit_Set: write_string(buf, "bit_set["); @@ -418,1097 +1441,3 @@ write_type :: proc(buf: ^String_Buffer, ti: ^runtime.Type_Info) { } } - - -_parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok: bool) { - is_digit :: inline proc(r: byte) -> bool { return '0' <= r && r <= '9' } - - new_offset = offset; - for new_offset <= len(s) { - c := s[new_offset]; - if !is_digit(c) do break; - new_offset += 1; - - result *= 10; - result += int(c)-'0'; - } - ok = new_offset > offset; - return; -} - -_arg_number :: proc(fi: ^Fmt_Info, arg_index: int, format: string, offset, arg_count: int) -> (index, new_offset: int, ok: bool) { - parse_arg_number :: proc(format: string) -> (int, int, bool) { - if len(format) < 3 do return 0, 1, false; - - for i in 1..len(format)-1 { - if format[i] == ']' { - width, new_index, ok := _parse_int(format, 1); - if !ok || new_index != i { - return 0, i+1, false; - } - return width-1, i+1, true; - } - } - - return 0, 1, false; - } - - - if len(format) <= offset || format[offset] != '[' { - return arg_index, offset, false; - } - fi.reordered = true; - - width: int; - index, width, ok = parse_arg_number(format[offset:]); - if ok && 0 <= index && index < arg_count { - return index, offset+width, true; - } - fi.good_arg_index = false; - return arg_index, offset+width, false; -} - -int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) { - num := 0; - new_arg_index := arg_index; - ok := true; - if arg_index < len(args) { - arg := args[arg_index]; - arg.id = runtime.typeid_base(arg.id); - switch i in arg { - case int: num = i; - case i8: num = int(i); - case i16: num = int(i); - case i32: num = int(i); - case i64: num = int(i); - case u8: num = int(i); - case u16: num = int(i); - case u32: num = int(i); - case u64: num = int(i); - case: - ok = false; - } - } - - if ok { - new_arg_index += 1; - } - - return num, new_arg_index, ok; -} - - -fmt_bad_verb :: proc(using fi: ^Fmt_Info, verb: rune) { - write_string(buf, "%!"); - write_rune(buf, verb); - write_byte(buf, '('); - if arg.id != nil { - write_typeid(buf, arg.id); - write_byte(buf, '='); - fmt_value(fi, arg, 'v'); - } else { - write_string(buf, ""); - } - write_byte(buf, ')'); -} - -fmt_bool :: proc(using fi: ^Fmt_Info, b: bool, verb: rune) { - switch verb { - case 't', 'v': - write_string(buf, b ? "true" : "false"); - case: - fmt_bad_verb(fi, verb); - } -} - - -fmt_write_padding :: proc(fi: ^Fmt_Info, width: int) { - if width <= 0 do return; - - pad_byte: byte = '0'; - if fi.space do pad_byte = ' '; - - for i := 0; i < width; i += 1 { - write_byte(fi.buf, pad_byte); - } -} - -_fmt_int :: proc(fi: ^Fmt_Info, u: u64, base: int, is_signed: bool, bit_size: int, digits: string) { - _, neg := strconv.is_integer_negative(u, is_signed, bit_size); - - BUF_SIZE :: 256; - if fi.width_set || fi.prec_set { - width := fi.width + fi.prec + 3; // 3 extra bytes for sign and prefix - if width > BUF_SIZE { - // TODO(bill):???? - panic("_fmt_int: buffer overrun. Width and precision too big"); - } - } - - prec := 0; - if fi.prec_set { - prec = fi.prec; - if prec == 0 && u == 0 { - prev_zero := fi.zero; - fi.zero = false; - fmt_write_padding(fi, fi.width); - fi.zero = prev_zero; - return; - } - } else if fi.zero && fi.width_set { - prec = fi.width; - if neg || fi.plus || fi.space { - // There needs to be space for the "sign" - prec -= 1; - } - } - - switch base { - case 2, 8, 10, 12, 16: - break; - case: - panic("_fmt_int: unknown base, whoops"); - } - - buf: [256]byte; - start := 0; - - using strconv.Int_Flag; - flags: strconv.Int_Flags; - if fi.hash && !fi.zero do flags |= {Prefix}; - if fi.plus do flags |= {Plus}; - if fi.space do flags |= {Space}; - s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags); - - if fi.hash && fi.zero { - c: byte = 0; - switch base { - case 2: c = 'b'; - case 8: c = 'o'; - case 12: c = 'z'; - case 16: c = 'x'; - } - if c != 0 { - write_byte(fi.buf, '0'); - write_byte(fi.buf, c); - } - } - - prev_zero := fi.zero; - defer fi.zero = prev_zero; - fi.zero = false; - _pad(fi, s); -} - - -__DIGITS_LOWER := "0123456789abcdefx"; -__DIGITS_UPPER := "0123456789ABCDEFX"; - -fmt_rune :: proc(fi: ^Fmt_Info, r: rune, verb: rune) { - switch verb { - case 'c', 'r', 'v': - write_rune(fi.buf, r); - case: - fmt_int(fi, u64(r), false, 32, verb); - } -} - -fmt_int :: proc(fi: ^Fmt_Info, u: u64, is_signed: bool, bit_size: int, verb: rune) { - switch verb { - case 'v': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER); - case 'b': _fmt_int(fi, u, 2, is_signed, bit_size, __DIGITS_LOWER); - case 'o': _fmt_int(fi, u, 8, is_signed, bit_size, __DIGITS_LOWER); - case 'd': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER); - case 'z': _fmt_int(fi, u, 12, is_signed, bit_size, __DIGITS_LOWER); - case 'x': _fmt_int(fi, u, 16, is_signed, bit_size, __DIGITS_LOWER); - case 'X': _fmt_int(fi, u, 16, is_signed, bit_size, __DIGITS_UPPER); - case 'c', 'r': - fmt_rune(fi, rune(u), verb); - case 'U': - r := rune(u); - if r < 0 || r > utf8.MAX_RUNE { - fmt_bad_verb(fi, verb); - } else { - write_string(fi.buf, "U+"); - _fmt_int(fi, u, 16, false, bit_size, __DIGITS_UPPER); - } - - case: - fmt_bad_verb(fi, verb); - } -} - -_pad :: proc(fi: ^Fmt_Info, s: string) { - if !fi.width_set { - write_string(fi.buf, s); - return; - } - - - width := fi.width - utf8.rune_count_in_string(s); - if fi.minus { // right pad - write_string(fi.buf, s); - fmt_write_padding(fi, width); - } else { // left pad - fmt_write_padding(fi, width); - write_string(fi.buf, s); - } -} - -fmt_float :: proc(fi: ^Fmt_Info, v: f64, bit_size: int, verb: rune) { - switch verb { - // case 'e', 'E', 'f', 'F', 'g', 'G', 'v': - // case 'f', 'F', 'v': - - case 'f', 'F', 'v': - prec: int = 3; - if fi.prec_set do prec = fi.prec; - buf: [386]byte; - - str := strconv.append_float(buf[1:], v, 'f', prec, bit_size); - str = string(buf[:len(str)+1]); - if str[1] == '+' || str[1] == '-' { - str = str[1:]; - } else { - str[0] = '+'; - } - - if fi.space && !fi.plus && str[0] == '+' { - str[0] = ' '; - } - - if len(str) > 1 && (str[1] == 'N' || str[1] == 'I') { - write_string(fi.buf, str); - return; - } - - if fi.plus || str[0] != '+' { - if fi.zero && fi.width_set && fi.width > len(str) { - write_byte(fi.buf, str[0]); - fmt_write_padding(fi, fi.width - len(str)); - write_string(fi.buf, str[1:]); - } else { - _pad(fi, str); - } - } else { - _pad(fi, str[1:]); - } - - case: - fmt_bad_verb(fi, verb); - } -} -fmt_string :: proc(fi: ^Fmt_Info, s: string, verb: rune) { - switch verb { - case 's', 'v': - write_string(fi.buf, s); - - case 'x', 'X': - space := fi.space; - fi.space = false; - defer fi.space = space; - - for i in 0..len(s)-1 { - if i > 0 && space do write_byte(fi.buf, ' '); - char_set := __DIGITS_UPPER; - if verb == 'x' do char_set = __DIGITS_LOWER; - _fmt_int(fi, u64(s[i]), 16, false, 8, char_set); - } - - case: - fmt_bad_verb(fi, verb); - } -} -fmt_cstring :: proc(fi: ^Fmt_Info, s: cstring, verb: rune) { - fmt_string(fi, string(s), verb); -} - -fmt_pointer :: proc(fi: ^Fmt_Info, p: rawptr, verb: rune) { - switch verb { - case 'p', 'v': - u := u64(uintptr(p)); - if !fi.hash || verb == 'v' { - write_string(fi.buf, "0x"); - } - _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER); - case: - fmt_bad_verb(fi, verb); - } -} - -enum_value_to_string :: proc(v: any) -> (string, bool) { - v.id = runtime.typeid_base(v.id); - type_info := type_info_of(v.id); - - switch e in type_info.variant { - case: return "", false; - case runtime.Type_Info_Enum: - get_str :: proc(i: $T, e: runtime.Type_Info_Enum) -> (string, bool) { - if types.is_string(e.base) { - for val, idx in e.values { - if v, ok := val.(T); ok && v == i { - return e.names[idx], true; - } - } - } else if len(e.values) == 0 { - return "", true; - } else { - for val, idx in e.values { - if v, ok := val.(T); ok && v == i { - return e.names[idx], true; - } - } - } - return "", false; - } - - a := any{v.data, runtime.type_info_base(e.base).id}; - switch v in a { - case rune: return get_str(v, e); - case i8: return get_str(v, e); - case i16: return get_str(v, e); - case i32: return get_str(v, e); - case i64: return get_str(v, e); - case int: return get_str(v, e); - case u8: return get_str(v, e); - case u16: return get_str(v, e); - case u32: return get_str(v, e); - case u64: return get_str(v, e); - case uint: return get_str(v, e); - case uintptr: return get_str(v, e); - } - } - - return "", false; -} - -string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) { - ti := type_info_base(type_info_of(T)); - if e, ok := ti.variant.(Type_Info_Enum); ok { - for str, idx in e.names { - if s == str { - // NOTE(bill): Unsafe cast - ptr := cast(^T)&e.values[idx]; - return ptr^, true; - } - } - } - return T{}, false; -} - -fmt_enum :: proc(fi: ^Fmt_Info, v: any, verb: rune) { - if v.id == nil || v.data == nil { - write_string(fi.buf, ""); - return; - } - - type_info := type_info_of(v.id); - switch e in type_info.variant { - case: fmt_bad_verb(fi, verb); - case runtime.Type_Info_Enum: - switch verb { - case: fmt_bad_verb(fi, verb); - case 'd', 'f': - fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, verb); - case 's', 'v': - str, ok := enum_value_to_string(v); - if !ok do str = "!%(BAD ENUM VALUE)"; - write_string(fi.buf, str); - } - } -} - - - -enum_value_to_u64 :: proc(ev: runtime.Type_Info_Enum_Value) -> u64 { - switch i in ev { - case rune: return u64(i); - case i8: return u64(i); - case i16: return u64(i); - case i32: return u64(i); - case i64: return u64(i); - case int: return u64(i); - case u8: return u64(i); - case u16: return u64(i); - case u32: return u64(i); - case u64: return u64(i); - case uint: return u64(i); - case uintptr: return u64(i); - } - return 0; -} - -fmt_bit_set :: proc(fi: ^Fmt_Info, v: any, name: string = "") { - is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info, ) -> bool { - if ti == nil { - return false; - } - ti = runtime.type_info_base(ti); - switch info in ti.variant { - case runtime.Type_Info_Integer: - using runtime.Type_Info_Endianness; - switch info.endianness { - case Platform: return false; - case Little: return ODIN_ENDIAN != "little"; - case Big: return ODIN_ENDIAN != "big"; - } - } - return false; - } - - byte_swap :: bits.byte_swap; - - type_info := type_info_of(v.id); - switch info in type_info.variant { - case runtime.Type_Info_Named: - val := v; - val.id = info.base.id; - fmt_bit_set(fi, val, info.name); - - case runtime.Type_Info_Bit_Set: - bits: u64; - bit_size := u64(8*type_info.size); - - do_byte_swap := is_bit_set_different_endian_to_platform(info.underlying); - - switch bit_size { - case 0: bits = 0; - case 8: - x := (^u8)(v.data)^; - bits = u64(x); - case 16: - x := (^u16)(v.data)^; - if do_byte_swap do x = byte_swap(x); - bits = u64(x); - case 32: - x := (^u32)(v.data)^; - if do_byte_swap do x = byte_swap(x); - bits = u64(x); - case 64: - x := (^u64)(v.data)^; - if do_byte_swap do x = byte_swap(x); - bits = u64(x); - case: panic("unknown bit_size size"); - } - - et := runtime.type_info_base(info.elem); - - if name != "" { - write_string(fi.buf, name); - } else { - write_type(fi.buf, type_info); - } - write_byte(fi.buf, '{'); - defer write_byte(fi.buf, '}'); - - e, is_enum := et.variant.(runtime.Type_Info_Enum); - commas := 0; - loop: for i in 0 .. bit_size-1 { - if bits & (1< 0 do write_string(fi.buf, ", "); - - if is_enum do for ev, evi in e.values { - v := enum_value_to_u64(ev); - if v == i { - write_string(fi.buf, e.names[evi]); - commas += 1; - continue loop; - } - } - write_i64(fi.buf, i64(i), 10); - commas += 1; - } - } -} -fmt_bit_field :: proc(fi: ^Fmt_Info, v: any, bit_field_name: string = "") { - type_info := type_info_of(v.id); - switch info in type_info.variant { - case runtime.Type_Info_Named: - val := v; - val.id = info.base.id; - fmt_bit_field(fi, val, info.name); - case runtime.Type_Info_Bit_Field: - data: u64 = 0; - switch type_info.size { - case 1: data = cast(u64) (^u8)(v.data)^; - case 2: data = cast(u64)(^u16)(v.data)^; - case 4: data = cast(u64)(^u32)(v.data)^; - case 8: data = cast(u64)(^u64)(v.data)^; - } - - if bit_field_name != "" { - write_string(fi.buf, bit_field_name); - write_byte(fi.buf, '{'); - } else { - write_string(fi.buf, "bit_field{"); - } - for name, i in info.names { - if i > 0 { - write_string(fi.buf, ", "); - } - bits := u64(info.bits[i]); - offset := u64(info.offsets[i]); - write_string(fi.buf, name); - write_string(fi.buf, " = "); - - n := 8*u64(size_of(u64)); - sa := n - bits; - u := data>>offset; - u <<= sa; - u >>= sa; - - write_u64(fi.buf, u, 10); - - } - write_byte(fi.buf, '}'); - case: - write_string(fi.buf, "HERE"); - } -} - -fmt_opaque :: proc(fi: ^Fmt_Info, v: any) { - is_nil :: proc(data: rawptr, n: int) -> bool { - if data == nil do return true; - if n == 0 do return true; - - a := (^byte)(data); - for i in 0..n-1 do if mem.ptr_offset(a, i)^ != 0 { - return false; - } - return true; - } - - rt :: runtime; - - type_info := type_info_of(v.id); - - if is_nil(v.data, type_info.size) { - write_string(fi.buf, "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 do return; - write_type(fi.buf, type_info); - write_byte(fi.buf, '{'); - defer write_byte(fi.buf, '}'); - - switch in elem.variant { - case rt.Type_Info_Integer, rt.Type_Info_Pointer, rt.Type_Info_Float: - fmt_value(fi, any{v.data, elem.id}, 'v'); - case: - // Okay - } - } else { - write_type(fi.buf, type_info); - write_byte(fi.buf, '{'); - defer write_byte(fi.buf, '}'); - } -} - -fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) { - if v.data == nil || v.id == nil { - write_string(fi.buf, ""); - return; - } - - type_info := type_info_of(v.id); - switch info in type_info.variant { - case runtime.Type_Info_Named: - switch b in info.base.variant { - case runtime.Type_Info_Struct: - if verb != 'v' { - fmt_bad_verb(fi, verb); - return; - } - if b.is_raw_union { - write_string(fi.buf, info.name); - write_string(fi.buf, "{}"); - return; - }; - write_string(fi.buf, info.name); - write_byte(fi.buf, '{'); - - hash := fi.hash; defer fi.hash = hash; - indent := fi.indent; defer fi.indent -= 1; - - fi.hash = false; - fi.indent += 1; - - if hash do write_byte(fi.buf, '\n'); - - field_count := -1; - for name, i in b.names { - // if len(name) > 0 && name[0] == '_' do continue; - field_count += 1; - - if !hash && field_count > 0 do write_string(fi.buf, ", "); - if hash do for in 0..fi.indent-1 do write_byte(fi.buf, '\t'); - - write_string(fi.buf, name); - write_string(fi.buf, " = "); - - if t := b.types[i]; types.is_any(t) { - write_string(fi.buf, "any{}"); - } else { - data := rawptr(uintptr(v.data) + b.offsets[i]); - fmt_arg(fi, any{data, t.id}, 'v'); - } - - if hash do write_string(fi.buf, ",\n"); - } - - if hash do for in 0..indent-1 do write_byte(fi.buf, '\t'); - write_byte(fi.buf, '}'); - - case runtime.Type_Info_Bit_Set: - fmt_bit_set(fi, v); - case runtime.Type_Info_Bit_Field: - fmt_bit_field(fi, v); - case runtime.Type_Info_Opaque: - fmt_opaque(fi, v); - case: - fmt_value(fi, any{v.data, info.base.id}, verb); - } - - case runtime.Type_Info_Boolean: fmt_arg(fi, v, verb); - case runtime.Type_Info_Integer: fmt_arg(fi, v, verb); - case runtime.Type_Info_Rune: fmt_arg(fi, v, verb); - case runtime.Type_Info_Float: fmt_arg(fi, v, verb); - case runtime.Type_Info_Complex: fmt_arg(fi, v, verb); - case runtime.Type_Info_String: fmt_arg(fi, v, verb); - - case runtime.Type_Info_Pointer: - if v.id == typeid_of(^runtime.Type_Info) { - write_type(fi.buf, (^^runtime.Type_Info)(v.data)^); - } else { - fmt_pointer(fi, (^rawptr)(v.data)^, verb); - } - - case runtime.Type_Info_Array: - write_byte(fi.buf, '['); - defer write_byte(fi.buf, ']'); - for i in 0..info.count-1 { - if i > 0 do write_string(fi.buf, ", "); - - data := uintptr(v.data) + uintptr(i*info.elem_size); - fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); - } - - case runtime.Type_Info_Dynamic_Array: - write_byte(fi.buf, '['); - defer write_byte(fi.buf, ']'); - array := cast(^mem.Raw_Dynamic_Array)v.data; - for i in 0..array.len-1 { - if i > 0 do write_string(fi.buf, ", "); - - data := uintptr(array.data) + uintptr(i*info.elem_size); - fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); - } - - case runtime.Type_Info_Slice: - write_byte(fi.buf, '['); - defer write_byte(fi.buf, ']'); - slice := cast(^mem.Raw_Slice)v.data; - for i in 0..slice.len-1 { - if i > 0 do write_string(fi.buf, ", "); - - data := uintptr(slice.data) + uintptr(i*info.elem_size); - fmt_arg(fi, any{rawptr(data), info.elem.id}, verb); - } - - case runtime.Type_Info_Map: - if verb != 'v' { - fmt_bad_verb(fi, verb); - return; - } - - write_string(fi.buf, "map["); - defer write_byte(fi.buf, ']'); - - m := (^mem.Raw_Map)(v.data); - if m != nil { - if info.generated_struct == nil { - return; - } - entries := &m.entries; - gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct); - ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array); - entry_type := ed.elem.variant.(runtime.Type_Info_Struct); - entry_size := ed.elem_size; - - for i in 0..entries.len-1 { - if i > 0 do write_string(fi.buf, ", "); - - data := uintptr(entries.data) + uintptr(i*entry_size); - header := cast(^runtime.Map_Entry_Header)data; - - if types.is_string(info.key) { - write_string(fi.buf, header.key.str); - } else { - fi := Fmt_Info{buf = fi.buf}; - fmt_arg(&fi, any{rawptr(&header.key.hash), info.key.id}, 'v'); - } - - write_string(fi.buf, "="); - - value := data + entry_type.offsets[2]; - fmt_arg(fi, any{rawptr(value), info.value.id}, 'v'); - } - } - - case runtime.Type_Info_Struct: - if info.is_raw_union { - write_string(fi.buf, "(raw_union)"); - return; - } - - write_byte(fi.buf, '{'); - defer write_byte(fi.buf, '}'); - - fi.indent += 1; defer fi.indent -= 1; - hash := fi.hash; defer fi.hash = hash; - fi.hash = false; - - - if hash do write_byte(fi.buf, '\n'); - - for _, i in info.names { - if !hash && i > 0 do write_string(fi.buf, ", "); - if hash { - for in 0..fi.indent-1 { - write_byte(fi.buf, '\t'); - } - } - - write_string(fi.buf, info.names[i]); - write_string(fi.buf, " = "); - - if t := info.types[i]; types.is_any(t) { - write_string(fi.buf, "any{}"); - } else { - data := uintptr(v.data) + info.offsets[i]; - fmt_arg(fi, any{rawptr(data), t.id}, 'v'); - } - if hash do write_string(fi.buf, ",\n"); - } - - case runtime.Type_Info_Union: - tag_ptr := uintptr(v.data) + info.tag_offset; - tag_any := any{rawptr(tag_ptr), info.tag_type.id}; - - tag: i64 = -1; - switch i in tag_any { - case u8: tag = i64(i); - case i8: tag = i64(i); - case u16: tag = i64(i); - case i16: tag = i64(i); - case u32: tag = i64(i); - case i32: tag = i64(i); - case u64: tag = i64(i); - case i64: tag = i64(i); - case: panic("Invalid union tag type"); - } - - if v.data == nil || tag == 0 { - write_string(fi.buf, "nil"); - } else { - id := info.variants[tag-1].id; - fmt_arg(fi, any{v.data, id}, verb); - } - - case runtime.Type_Info_Enum: - fmt_enum(fi, v, verb); - - case runtime.Type_Info_Procedure: - ptr := (^rawptr)(v.data)^; - if ptr == nil { - write_string(fi.buf, "nil"); - } else { - write_typeid(fi.buf, v.id); - write_string(fi.buf, " @ "); - fmt_pointer(fi, ptr, 'p'); - } - - case runtime.Type_Info_Type_Id: - id := (^typeid)(v.data)^; - write_typeid(fi.buf, id); - - case runtime.Type_Info_Bit_Field: - fmt_bit_field(fi, v); - - case runtime.Type_Info_Bit_Set: - fmt_bit_set(fi, v); - - case runtime.Type_Info_Opaque: - fmt_opaque(fi, v); - } -} - -fmt_complex :: proc(fi: ^Fmt_Info, c: complex128, bits: int, verb: rune) { - switch verb { - case 'f', 'F', 'v': - r, i := real(c), imag(c); - fmt_float(fi, r, bits/2, verb); - if !fi.plus && i >= 0 { - write_rune(fi.buf, '+'); - } - fmt_float(fi, i, bits/2, verb); - write_rune(fi.buf, 'i'); - - case: - fmt_bad_verb(fi, verb); - return; - } -} - -fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) { - if arg == nil { - write_string(fi.buf, ""); - return; - } - fi.arg = arg; - - if verb == 'T' { - ti := type_info_of(arg.id); - switch a in arg { - case ^runtime.Type_Info: ti = a; - } - write_type(fi.buf, ti); - return; - } - - - custom_types: switch a in arg { - case runtime.Source_Code_Location: - if fi.hash && verb == 'v' { - write_string(fi.buf, a.file_path); - write_byte(fi.buf, '('); - write_i64(fi.buf, i64(a.line), 10); - write_byte(fi.buf, ':'); - write_i64(fi.buf, i64(a.column), 10); - write_byte(fi.buf, ')'); - return; - } - } - - base_arg := arg; - base_arg.id = runtime.typeid_base(base_arg.id); - switch a in base_arg { - case bool: fmt_bool(fi, bool(a), verb); - case b8: fmt_bool(fi, bool(a), verb); - case b16: fmt_bool(fi, bool(a), verb); - case b32: fmt_bool(fi, bool(a), verb); - case b64: fmt_bool(fi, bool(a), verb); - - case any: fmt_arg(fi, a, verb); - case rune: fmt_rune(fi, a, verb); - - case f32: fmt_float(fi, f64(a), 32, verb); - case f64: fmt_float(fi, a, 64, verb); - - case complex64: fmt_complex(fi, complex128(a), 64, verb); - case complex128: fmt_complex(fi, a, 128, verb); - - case i8: fmt_int(fi, u64(a), true, 8, verb); - case u8: fmt_int(fi, u64(a), false, 8, verb); - case i16: fmt_int(fi, u64(a), true, 16, verb); - case u16: fmt_int(fi, u64(a), false, 16, verb); - case i32: fmt_int(fi, u64(a), true, 32, verb); - case u32: fmt_int(fi, u64(a), false, 32, verb); - case i64: fmt_int(fi, u64(a), true, 64, verb); - case u64: fmt_int(fi, u64(a), false, 64, verb); - case int: fmt_int(fi, u64(a), true, 8*size_of(int), verb); - case uint: fmt_int(fi, u64(a), false, 8*size_of(uint), verb); - case uintptr: fmt_int(fi, u64(a), false, 8*size_of(uintptr), verb); - - case string: fmt_string(fi, a, verb); - case cstring: fmt_cstring(fi, a, verb); - - case typeid: write_typeid(fi.buf, a); - - case i16le: fmt_int(fi, u64(a), true, 16, verb); - case u16le: fmt_int(fi, u64(a), false, 16, verb); - case i32le: fmt_int(fi, u64(a), true, 32, verb); - case u32le: fmt_int(fi, u64(a), false, 32, verb); - case i64le: fmt_int(fi, u64(a), true, 64, verb); - case u64le: fmt_int(fi, u64(a), false, 64, verb); - - case i16be: fmt_int(fi, u64(a), true, 16, verb); - case u16be: fmt_int(fi, u64(a), false, 16, verb); - case i32be: fmt_int(fi, u64(a), true, 32, verb); - case u32be: fmt_int(fi, u64(a), false, 32, verb); - case i64be: fmt_int(fi, u64(a), true, 64, verb); - case u64be: fmt_int(fi, u64(a), false, 64, verb); - - case: fmt_value(fi, arg, verb); - } - -} - - - -sbprint :: proc(buf: ^String_Buffer, args: ..any) -> string { - fi: Fmt_Info; - prev_string := false; - - fi.buf = buf; - - for arg, i in args { - is_string := arg != nil && types.is_string(type_info_of(arg.id)); - if i > 0 && !is_string && !prev_string { - write_byte(buf, ' '); - } - fmt_value(&fi, args[i], 'v'); - prev_string = is_string; - } - return to_string(buf^); -} - -sbprintln :: proc(buf: ^String_Buffer, args: ..any) -> string { - fi: Fmt_Info; - fi.buf = buf; - - for _, i in args { - if i > 0 do write_byte(buf, ' '); - - fmt_value(&fi, args[i], 'v'); - } - write_byte(buf, '\n'); - return to_string(buf^); -} - -sbprintf :: proc(b: ^String_Buffer, fmt: string, args: ..any) -> string { - fi: Fmt_Info; - arg_index: int = 0; - end := len(fmt); - was_prev_index := false; - - - loop: for i := 0; i < end; /**/ { - fi = Fmt_Info{buf = b, good_arg_index = true}; - - prev_i := i; - for i < end && fmt[i] != '%' { - i += 1; - } - if i > prev_i { - write_string(b, fmt[prev_i:i]); - } - if i >= end { - break loop; - } - - // Process a "verb" - i += 1; - - prefix_loop: for ; i < end; i += 1 { - switch fmt[i] { - case '+': - fi.plus = true; - case '-': - fi.minus = true; - fi.zero = false; - case ' ': - fi.space = true; - case '#': - fi.hash = true; - case '0': - fi.zero = !fi.minus; - case: - break prefix_loop; - } - } - - arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args)); - - // Width - if i < end && fmt[i] == '*' { - i += 1; - fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index); - if !fi.width_set { - write_string(b, "%!(BAD WIDTH)"); - } - - if fi.width < 0 { - fi.width = -fi.width; - fi.minus = true; - fi.zero = false; - } - was_prev_index = false; - } else { - fi.width, i, fi.width_set = _parse_int(fmt, i); - if was_prev_index && fi.width_set { // %[6]2d - fi.good_arg_index = false; - } - } - - // Precision - if i < end && fmt[i] == '.' { - i += 1; - if was_prev_index { // %[6].2d - fi.good_arg_index = false; - } - if i < end && fmt[i] == '*' { - arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args)); - i += 1; - fi.prec, arg_index, fi.prec_set = int_from_arg(args, arg_index); - if fi.prec < 0 { - fi.prec = 0; - fi.prec_set = false; - } - if !fi.prec_set { - write_string(fi.buf, "%!(BAD PRECISION)"); - } - was_prev_index = false; - } else { - fi.prec, i, fi.prec_set = _parse_int(fmt, i); - if !fi.prec_set { - // fi.prec_set = true; - // fi.prec = 0; - } - } - } - - if !was_prev_index { - arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args)); - } - - if i >= end { - write_string(b, "%!(NO VERB)"); - break loop; - } - - verb, w := utf8.decode_rune_in_string(fmt[i:]); - i += w; - - switch { - case verb == '%': - write_byte(b, '%'); - case !fi.good_arg_index: - write_string(b, "%!(BAD ARGUMENT NUMBER)"); - case arg_index >= len(args): - write_string(b, "%!(MISSING ARGUMENT)"); - case: - fmt_arg(&fi, args[arg_index], verb); - arg_index += 1; - } - } - - if !fi.reordered && arg_index < len(args) { - write_string(b, "%!(EXTRA "); - for arg, index in args[arg_index:] { - if index > 0 do write_string(b, ", "); - - if arg == nil do write_string(b, ""); - else do fmt_arg(&fi, args[index], 'v'); - } - write_string(b, ")"); - } - - return to_string(b^); -} diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin index 1d598285e..1a961f910 100644 --- a/core/log/file_console_logger.odin +++ b/core/log/file_console_logger.odin @@ -1,6 +1,7 @@ package log import "core:fmt"; +import "core:strings"; import "core:os"; import "core:time"; @@ -13,18 +14,18 @@ Level_Headers := []string{ }; Default_Console_Logger_Opts :: Options{ - Option.Level, + Option.Level, Option.Terminal_Color, Option.Short_File_Path, - Option.Line, - Option.Procedure, + Option.Line, + Option.Procedure, } | Full_Timestamp_Opts; Default_File_Logger_Opts :: Options{ - Option.Level, + Option.Level, Option.Short_File_Path, - Option.Line, - Option.Procedure, + Option.Line, + Option.Procedure, } | Full_Timestamp_Opts; @@ -48,7 +49,7 @@ destroy_file_logger ::proc(log : ^Logger) { free(data); log^ = nil_logger(); } - + create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "") -> Logger { data := new(File_Console_Logger_Data); data.lowest_level = lowest; @@ -70,14 +71,14 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string if(data.file_handle != os.INVALID_HANDLE) do h = data.file_handle; else do h = level <= Level.Error ? os.stdout : os.stderr; backing: [1024]byte; //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths. - buf := fmt.string_buffer_from_slice(backing[:]); - + buf := strings.builder_from_slice(backing[:]); + do_level_header(options, level, &buf); when time.IS_SUPPORTED { if Full_Timestamp_Opts & options != nil { fmt.sbprint(&buf, "["); - t := time.now(); + t := time.now(); y, m, d := time.date(t); h, min, s := time.clock(t); if Option.Date in options do fmt.sbprintf(&buf, "%d-%02d-%02d ", y, m, d); @@ -90,10 +91,10 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string if data.ident != "" do fmt.sbprintf(&buf, "[%s] ", data.ident); //TODO(Hoej): When we have better atomics and such, make this thread-safe - fmt.fprintf(h, "%s %s\n", fmt.to_string(buf), text); + fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text); } -do_level_header :: proc(opts : Options, level : Level, buf : ^fmt.String_Buffer) { +do_level_header :: proc(opts : Options, level : Level, str : ^strings.Builder) { RESET :: "\x1b[0m"; RED :: "\x1b[31m"; @@ -109,18 +110,18 @@ do_level_header :: proc(opts : Options, level : Level, buf : ^fmt.String_Buffer) } if Option.Level in opts { - if Option.Terminal_Color in opts do fmt.sbprint(buf, col); - fmt.sbprint(buf, Level_Headers[level]); - if Option.Terminal_Color in opts do fmt.sbprint(buf, RESET); + if Option.Terminal_Color in opts do fmt.sbprint(str, col); + fmt.sbprint(str, Level_Headers[level]); + if Option.Terminal_Color in opts do fmt.sbprint(str, RESET); } } -do_location_header :: proc(opts : Options, buf : ^fmt.String_Buffer, location := #caller_location) { +do_location_header :: proc(opts : Options, buf : ^strings.Builder, location := #caller_location) { if Location_Header_Opts & opts != nil do fmt.sbprint(buf, "["); else do return; file := location.file_path; if Option.Short_File_Path in opts { - when os.OS == "windows" do delimiter := '\\'; else do delimiter := '/'; + when os.OS == "windows" do delimiter := '\\'; else do delimiter := '/'; last := 0; for r, i in location.file_path do if r == delimiter do last = i+1; file = location.file_path[last:]; @@ -139,4 +140,4 @@ do_location_header :: proc(opts : Options, buf : ^fmt.String_Buffer, location := } fmt.sbprint(buf, "] "); -} \ No newline at end of file +} diff --git a/core/os/os_essence.odin b/core/os/os_essence.odin index 0c61ce74d..2fe62b873 100644 --- a/core/os/os_essence.odin +++ b/core/os/os_essence.odin @@ -71,6 +71,10 @@ stdin := Handle(-1); // Not implemented stdout := Handle(0); stderr := Handle(0); +is_path_separator :: proc(r: rune) -> bool { + return r == '/'; +} + current_thread_id :: proc "contextless" () -> int { return OSGetThreadID(Handle(0x1000)); } diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 6e60f0e45..a4cb06a71 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -203,6 +203,10 @@ foreign dl { @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---; } +is_path_separator :: proc(r: rune) -> bool { + return r == '/'; +} + get_last_error :: proc() -> int { return __errno_location()^; } diff --git a/core/os/os_osx.odin b/core/os/os_osx.odin index 985699815..1914ca0c3 100644 --- a/core/os/os_osx.odin +++ b/core/os/os_osx.odin @@ -215,6 +215,10 @@ last_write_time :: proc(fd: Handle) -> File_Time {} last_write_time_by_name :: proc(name: string) -> File_Time {} */ +is_path_separator :: proc(r: rune) -> bool { + return r == '/'; +} + stat :: inline proc(path: string) -> (Stat, bool) { s: Stat; cstr := strings.new_cstring(path); diff --git a/core/os/os_windows.odin b/core/os/os_windows.odin index 6fd46ffd5..2fec6b845 100644 --- a/core/os/os_windows.odin +++ b/core/os/os_windows.odin @@ -61,6 +61,10 @@ ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0; args := _alloc_command_line_arguments(); +is_path_separator :: proc(r: rune) -> bool { + return r == '/' || r == '\\'; +} + open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) { if len(path) == 0 do return INVALID_HANDLE, ERROR_FILE_NOT_FOUND; diff --git a/core/strings/builder.odin b/core/strings/builder.odin new file mode 100644 index 000000000..94d0b4223 --- /dev/null +++ b/core/strings/builder.odin @@ -0,0 +1,107 @@ +package strings + +import "core:mem" +import "core:unicode/utf8" +import "core:strconv" + +Builder :: struct { + buf: [dynamic]byte, +} + +make_builder :: proc(allocator := context.allocator) -> Builder { + return Builder{make([dynamic]byte)}; +} + +builder_from_slice :: proc(backing: []byte) -> Builder { + s := transmute(mem.Raw_Slice)backing; + d := mem.Raw_Dynamic_Array{ + data = s.data, + len = 0, + cap = s.len, + allocator = mem.nil_allocator(), + }; + return transmute(Builder)d; +} +to_string :: proc(b: Builder) -> string { + return string(b.buf[:]); +} + +builder_len :: proc(b: Builder) -> int { + return len(b.buf); +} +builder_cap :: proc(b: Builder) -> int { + return cap(b.buf); +} + +write_byte :: proc(b: ^Builder, x: byte) { + append(&b.buf, x); +} + +write_rune :: proc(b: ^Builder, r: rune) -> int { + if r < utf8.RUNE_SELF { + write_byte(b, byte(r)); + return 1; + } + + s, n := utf8.encode_rune(r); + write_bytes(b, s[:n]); + return n; +} + +write_string :: proc(b: ^Builder, s: string) { + write_bytes(b, cast([]byte)s); +} + +write_bytes :: proc(b: ^Builder, x: []byte) { + append(&b.buf, ..x); +} + + +write_encoded_rune :: proc(b: ^Builder, r: rune) { + write_byte(b, '\''); + switch r { + case '\a': write_string(b, "\\a"); + case '\b': write_string(b, "\\b"); + case '\e': write_string(b, "\\e"); + case '\f': write_string(b, "\\f"); + case '\n': write_string(b, "\\n"); + case '\r': write_string(b, "\\r"); + case '\t': write_string(b, "\\t"); + case '\v': write_string(b, "\\v"); + case: + if r < 32 { + write_string(b, "\\x"); + buf: [2]byte; + s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil); + switch len(s) { + case 0: write_string(b, "00"); + case 1: write_rune(b, '0'); + case 2: write_string(b, s); + } + } else { + write_rune(b, r); + } + + } + write_byte(b, '\''); +} + + +write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) { + buf: [129]byte; + s := strconv.append_bits(buf[:], u64(i), base, false, 64, strconv.digits, nil); + write_string(b, s); +} +write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) { + buf: [129]byte; + s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil); + write_string(b, s); +} + +write_uint :: proc(b: ^Builder, i: uint, base: int = 10) { + write_u64(b, u64(i), base); +} +write_int :: proc(b: ^Builder, i: int, base: int = 10) { + write_i64(b, i64(i), base); +} + diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index ba6e71f44..21b6904c4 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -841,7 +841,7 @@ bit_set_type :: proc() { incl(&x, 'F'); assert('F' in x); excl(&x, 'F'); - assert(!('F' in x)); + assert('F' notin x); y |= {1, 4, 2}; assert(2 in y);