diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 6df9ba489..ef9a92eba 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -36,6 +36,7 @@ Info :: struct { record_level: int, optional_len: Maybe(int), + use_nul_termination: bool, n: int, // bytes written } @@ -951,7 +952,7 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { fmt_string :: proc(fi: ^Info, s: string, verb: rune) { s, verb := s, verb if ol, ok := fi.optional_len.?; ok { - s = s[:min(len(s), ol)] + s = s[:clamp(ol, 0, len(s))] } if !fi.in_bad && fi.record_level > 0 && verb == 'v' { verb = 'q' @@ -1267,7 +1268,7 @@ fmt_write_array :: proc(fi: ^Info, array_data: rawptr, count: int, elem_size: in @(private) -handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb: ^rune, optional_len: ^int = nil) -> (do_continue: bool) { +handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb: ^rune, optional_len: ^int, use_nul_termination: ^bool) -> (do_continue: bool) { handle_optional_len :: proc(data: rawptr, info: reflect.Type_Info_Struct, field_name: string, optional_len: ^int) { if optional_len == nil { return @@ -1297,13 +1298,19 @@ handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb: verb^ = r if len(value) > 0 && value[0] == ',' { field_name := value[1:] - switch r { - case 's', 'q': - handle_optional_len(data, info, field_name, optional_len) - case 'v': - #partial switch reflect.type_kind(info.types[idx].id) { - case .String, .Multi_Pointer, .Array, .Slice, .Dynamic_Array: + if field_name == "0" { + if use_nul_termination != nil { + use_nul_termination^ = true + } + } else { + switch r { + case 's', 'q': handle_optional_len(data, info, field_name, optional_len) + case 'v': + #partial switch reflect.type_kind(info.types[idx].id) { + case .String, .Multi_Pointer, .Array, .Slice, .Dynamic_Array: + handle_optional_len(data, info, field_name, optional_len) + } } } } @@ -1430,8 +1437,9 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St field_count := -1 for name, i in info.names { optional_len: int = -1 + use_nul_termination: bool = false verb := 'v' - if handle_tag(v.data, info, i, &verb, &optional_len) { + if handle_tag(v.data, info, i, &verb, &optional_len, &use_nul_termination) { continue } field_count += 1 @@ -1442,6 +1450,8 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St defer if optional_len >= 0 { fi.optional_len = nil } + fi.use_nul_termination = use_nul_termination + defer fi.use_nul_termination = false if !do_trailing_comma && field_count > 0 { io.write_string(fi.writer, ", ") } if hash { @@ -1463,6 +1473,26 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St } } +@(private) +search_nul_termination :: proc(ptr: rawptr, elem_size: int, max_n: int) -> (n: int) { + for p := uintptr(ptr); max_n < 0 || n < max_n; p += uintptr(elem_size) { + if mem.check_zero_ptr(rawptr(p), elem_size) { + break + } + n += 1 + } + return n +} + +fmt_array_nul_terminated :: proc(fi: ^Info, data: rawptr, max_n: int, elem_size: int, elem: ^reflect.Type_Info, verb: rune) { + if data == nil { + io.write_string(fi.writer, "", &fi.n) + return + } + n := search_nul_termination(data, elem_size, max_n) + fmt_array(fi, data, n, elem_size, elem, verb) +} + fmt_array :: proc(fi: ^Info, data: rawptr, n: int, elem_size: int, elem: ^reflect.Type_Info, verb: rune) { if (verb == 's' || verb == 'q') && reflect.is_byte(elem) { if data == nil && n > 0 { @@ -1811,6 +1841,9 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { if n, ok := fi.optional_len.?; ok { fmt_array(fi, ptr, n, elem.size, elem, verb) return + } else if fi.use_nul_termination { + fmt_array_nul_terminated(fi, ptr, -1, elem.size, elem, verb) + return } #partial switch e in elem.variant { @@ -1902,26 +1935,38 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Array: n := info.count + ptr := v.data if ol, ok := fi.optional_len.?; ok { n = min(n, ol) + } else if fi.use_nul_termination { + fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb) + return } - fmt_array(fi, v.data, n, info.elem_size, info.elem, verb) + fmt_array(fi, ptr, n, info.elem_size, info.elem, verb) case runtime.Type_Info_Slice: slice := cast(^mem.Raw_Slice)v.data n := slice.len + ptr := slice.data if ol, ok := fi.optional_len.?; ok { n = min(n, ol) + } else if fi.use_nul_termination { + fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb) + return } - fmt_array(fi, slice.data, n, info.elem_size, info.elem, verb) + fmt_array(fi, ptr, n, info.elem_size, info.elem, verb) case runtime.Type_Info_Dynamic_Array: array := cast(^mem.Raw_Dynamic_Array)v.data n := array.len + ptr := array.data if ol, ok := fi.optional_len.?; ok { n = min(n, ol) + } else if fi.use_nul_termination { + fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb) + return } - fmt_array(fi, array.data, n, info.elem_size, info.elem, verb) + fmt_array(fi, ptr, n, info.elem_size, info.elem, verb) case runtime.Type_Info_Simd_Vector: io.write_byte(fi.writer, '<', &fi.n) diff --git a/core/mem/mem.odin b/core/mem/mem.odin index fd91a6c97..7295a2afa 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -109,6 +109,12 @@ check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool { case ptr == nil: return true } + switch len { + case 1: return (^u8)(ptr)^ == 0 + case 2: return intrinsics.unaligned_load((^u16)(ptr)) == 0 + case 4: return intrinsics.unaligned_load((^u32)(ptr)) == 0 + case 8: return intrinsics.unaligned_load((^u64)(ptr)) == 0 + } start := uintptr(ptr) start_aligned := align_forward_uintptr(start, align_of(uintptr))