|
@@ -33,6 +33,8 @@ Info :: struct {
|
|
arg: any, // Temporary
|
|
arg: any, // Temporary
|
|
record_level: int,
|
|
record_level: int,
|
|
|
|
|
|
|
|
+ optional_len: Maybe(int),
|
|
|
|
+
|
|
n: int, // bytes written
|
|
n: int, // bytes written
|
|
}
|
|
}
|
|
|
|
|
|
@@ -941,6 +943,10 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
|
|
|
|
|
|
|
|
|
|
fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
|
|
fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
|
|
|
|
+ s := s
|
|
|
|
+ if ol, ok := fi.optional_len.?; ok {
|
|
|
|
+ s = s[:min(len(s), ol)]
|
|
|
|
+ }
|
|
switch verb {
|
|
switch verb {
|
|
case 's', 'v':
|
|
case 's', 'v':
|
|
if fi.width_set {
|
|
if fi.width_set {
|
|
@@ -1249,11 +1255,37 @@ fmt_write_array :: proc(fi: ^Info, array_data: rawptr, count: int, elem_size: in
|
|
|
|
|
|
|
|
|
|
@(private)
|
|
@(private)
|
|
-handle_tag :: proc(info: reflect.Type_Info_Struct, i: int, verb: ^rune) -> (do_continue: bool) {
|
|
|
|
- tag := info.tags[i]
|
|
|
|
- if value, ok := reflect.struct_tag_lookup(reflect.Struct_Tag(tag), "fmt"); ok {
|
|
|
|
- if value == "-" {
|
|
|
|
- return true
|
|
|
|
|
|
+handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb: ^rune, optional_len: ^int = nil) -> (do_continue: bool) {
|
|
|
|
+ tag := info.tags[idx]
|
|
|
|
+ if vt, ok := reflect.struct_tag_lookup(reflect.Struct_Tag(tag), "fmt"); ok {
|
|
|
|
+ value := strings.trim_space(string(vt))
|
|
|
|
+ switch value {
|
|
|
|
+ case "": return false
|
|
|
|
+ case "-": return true
|
|
|
|
+ }
|
|
|
|
+ r, w := utf8.decode_rune_in_string(value)
|
|
|
|
+ value = value[w:]
|
|
|
|
+ if value == "" || value[0] == ',' {
|
|
|
|
+ verb^ = r
|
|
|
|
+ if value[0] == ',' {
|
|
|
|
+ switch r {
|
|
|
|
+ case 's':
|
|
|
|
+ if optional_len != nil {
|
|
|
|
+ field_name := value[1:]
|
|
|
|
+ for f, i in info.names {
|
|
|
|
+ if f != field_name {
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ ptr := rawptr(uintptr(data) + info.offsets[i])
|
|
|
|
+ field := any{ptr, info.types[i].id}
|
|
|
|
+ if new_len, iok := reflect.as_int(field); iok {
|
|
|
|
+ optional_len^ = max(new_len, 0)
|
|
|
|
+ }
|
|
|
|
+ break
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
return false
|
|
@@ -1503,12 +1535,20 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
|
} else {
|
|
} else {
|
|
field_count := -1
|
|
field_count := -1
|
|
for name, i in b.names {
|
|
for name, i in b.names {
|
|
|
|
+ optional_len: int = -1
|
|
verb := 'v'
|
|
verb := 'v'
|
|
- if handle_tag(b, i, &verb) {
|
|
|
|
|
|
+ if handle_tag(v.data, b, i, &verb, &optional_len) {
|
|
continue
|
|
continue
|
|
}
|
|
}
|
|
field_count += 1
|
|
field_count += 1
|
|
|
|
|
|
|
|
+ if optional_len >= 0 {
|
|
|
|
+ fi.optional_len = optional_len
|
|
|
|
+ }
|
|
|
|
+ defer if optional_len >= 0 {
|
|
|
|
+ fi.optional_len = nil
|
|
|
|
+ }
|
|
|
|
+
|
|
if !hash && field_count > 0 { io.write_string(fi.writer, ", ") }
|
|
if !hash && field_count > 0 { io.write_string(fi.writer, ", ") }
|
|
if hash {
|
|
if hash {
|
|
fmt_write_indent(fi)
|
|
fmt_write_indent(fi)
|
|
@@ -1632,7 +1672,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
|
|
|
|
|
case runtime.Type_Info_Array:
|
|
case runtime.Type_Info_Array:
|
|
if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) {
|
|
if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) {
|
|
- s := strings.string_from_ptr((^byte)(v.data), info.count)
|
|
|
|
|
|
+ n := info.count
|
|
|
|
+ if ol, ok := fi.optional_len.?; ok {
|
|
|
|
+ n = min(n, ol)
|
|
|
|
+ }
|
|
|
|
+ s := strings.string_from_ptr((^byte)(v.data), n)
|
|
fmt_string(fi, s, verb)
|
|
fmt_string(fi, s, verb)
|
|
} else {
|
|
} else {
|
|
fmt_write_array(fi, v.data, info.count, info.elem_size, info.elem.id, verb)
|
|
fmt_write_array(fi, v.data, info.count, info.elem_size, info.elem.id, verb)
|
|
@@ -1690,7 +1734,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
|
case runtime.Type_Info_Dynamic_Array:
|
|
case runtime.Type_Info_Dynamic_Array:
|
|
array := cast(^mem.Raw_Dynamic_Array)v.data
|
|
array := cast(^mem.Raw_Dynamic_Array)v.data
|
|
if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) {
|
|
if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) {
|
|
- s := strings.string_from_ptr((^byte)(array.data), array.len)
|
|
|
|
|
|
+ n := array.len
|
|
|
|
+ if ol, ok := fi.optional_len.?; ok {
|
|
|
|
+ n = min(n, ol)
|
|
|
|
+ }
|
|
|
|
+ s := strings.string_from_ptr((^byte)(array.data), n)
|
|
fmt_string(fi, s, verb)
|
|
fmt_string(fi, s, verb)
|
|
} else if verb == 'p' {
|
|
} else if verb == 'p' {
|
|
fmt_pointer(fi, array.data, 'p')
|
|
fmt_pointer(fi, array.data, 'p')
|
|
@@ -1712,7 +1760,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
|
case runtime.Type_Info_Slice:
|
|
case runtime.Type_Info_Slice:
|
|
slice := cast(^mem.Raw_Slice)v.data
|
|
slice := cast(^mem.Raw_Slice)v.data
|
|
if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) {
|
|
if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) {
|
|
- s := strings.string_from_ptr((^byte)(slice.data), slice.len)
|
|
|
|
|
|
+ n := slice.len
|
|
|
|
+ if ol, ok := fi.optional_len.?; ok {
|
|
|
|
+ n = min(n, ol)
|
|
|
|
+ }
|
|
|
|
+ s := strings.string_from_ptr((^byte)(slice.data), n)
|
|
fmt_string(fi, s, verb)
|
|
fmt_string(fi, s, verb)
|
|
} else if verb == 'p' {
|
|
} else if verb == 'p' {
|
|
fmt_pointer(fi, slice.data, 'p')
|
|
fmt_pointer(fi, slice.data, 'p')
|
|
@@ -1850,7 +1902,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
|
field_count := -1
|
|
field_count := -1
|
|
for name, i in info.names {
|
|
for name, i in info.names {
|
|
verb := 'v'
|
|
verb := 'v'
|
|
- if handle_tag(info, i, &verb) {
|
|
|
|
|
|
+ if handle_tag(v.data, info, i, &verb) {
|
|
continue
|
|
continue
|
|
}
|
|
}
|
|
|
|
|