From 600e0ebed0c8d1b27de266edcee5cc392cfc306a Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 12 Jan 2025 12:13:29 +0100 Subject: Fix stray space vs. tab --- core/encoding/base32/base32.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'core/encoding') diff --git a/core/encoding/base32/base32.odin b/core/encoding/base32/base32.odin index 8629491b1..2267a872b 100644 --- a/core/encoding/base32/base32.odin +++ b/core/encoding/base32/base32.odin @@ -118,10 +118,10 @@ _encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.al @(optimization_mode="favor_size") decode :: proc( - data: string, - DEC_TBL := DEC_TABLE, - validate: Validate_Proc = _validate_default, - allocator := context.allocator) -> (out: []byte, err: Error) { + data: string, + DEC_TBL := DEC_TABLE, + validate: Validate_Proc = _validate_default, + allocator := context.allocator) -> (out: []byte, err: Error) { if len(data) == 0 { return nil, .None } -- cgit v1.2.3 From 51b80c5a2027270c634a6c4a41e8fed0216ae4c1 Mon Sep 17 00:00:00 2001 From: jkenda Date: Sat, 1 Feb 2025 10:32:07 +0100 Subject: encoding/json: marshal enumerated arrays to objects with key-value pairs --- core/encoding/json/marshal.odin | 16 ++++++++-- tests/core/encoding/json/test_core_json.odin | 48 +++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 4 deletions(-) (limited to 'core/encoding') diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index f0f0927a1..020facd14 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -209,13 +209,23 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: opt_write_end(w, opt, ']') or_return case runtime.Type_Info_Enumerated_Array: - opt_write_start(w, opt, '[') or_return + index_type := reflect.type_info_base(info.index) + enum_type := index_type.variant.(reflect.Type_Info_Enum) + + opt_write_start(w, opt, '{') or_return for i in 0.. u128 { } return u -} \ No newline at end of file +} diff --git a/tests/core/encoding/json/test_core_json.odin b/tests/core/encoding/json/test_core_json.odin index 27cce7faa..5fcdc7a27 100644 --- a/tests/core/encoding/json/test_core_json.odin +++ b/tests/core/encoding/json/test_core_json.odin @@ -482,4 +482,50 @@ map_with_integer_keys :: proc(t: ^testing.T) { testing.expectf(t, runtime.string_eq(item, my_map2[key]), "Expected value %s to be present in unmarshaled map", key) } } -} \ No newline at end of file +} + +@test +enumerated_array :: proc(t: ^testing.T) { + Fruit :: enum { Apple, Banana, Pear } + Fruit_Stock :: [Fruit]uint { + .Apple = 14, + .Banana = 3, + .Pear = 513, + } + + { // test unmarshaling from array + marshaled := "[14,3,513]" + + unmarshaled: [Fruit]uint + err := json.unmarshal_string(marshaled, &unmarshaled) + testing.expect_value(t, err, nil) + testing.expect_value(t, unmarshaled, Fruit_Stock) + } + + Sparse_Fruit :: enum { Apple, Banana, Cherry = 23, Pear } + Sparse_Fruit_Stock :: #partial #sparse [Sparse_Fruit]uint { + .Apple = 14, + .Banana = 3, + .Pear = 513, + } + + { // test unmarshaling from object + marshaled := `{"Apple":14,"Banana":3,"Cherry":0,"Pear":513}` + + unmarshaled: #sparse [Sparse_Fruit]uint + err := json.unmarshal_string(marshaled, &unmarshaled) + testing.expect_value(t, err, nil) + testing.expect_value(t, unmarshaled, Sparse_Fruit_Stock) + } + + { // test marshal -> unmarshal + marshaled, err_marshal := json.marshal(Sparse_Fruit_Stock) + defer delete(marshaled) + testing.expect_value(t, err_marshal, nil) + + unmarshaled: #sparse [Sparse_Fruit]uint + err_unmarshal := json.unmarshal(marshaled, &unmarshaled) + testing.expect_value(t, err_unmarshal, nil) + testing.expect_value(t, unmarshaled, Sparse_Fruit_Stock) + } +} -- cgit v1.2.3 From 3148acf6a69868ddec0780daa293866fe079b7d4 Mon Sep 17 00:00:00 2001 From: dozn Date: Mon, 17 Mar 2025 01:51:01 -0700 Subject: [core:encoding/json] When Unmarshalling, Only Match Struct Tags If Present --- core/encoding/json/unmarshal.odin | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'core/encoding') diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index 57371e360..a5f3bd8c6 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -390,6 +390,9 @@ unmarshal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_loca return prev } +// Struct tags can include not only the name of the JSON key, but also a tag such as `omitempty`. +// Example: `json:"key_name,omitempty"` +// This returns the first field as `json_name`, and the rest are returned as `extra`. @(private) json_name_from_tag_value :: proc(value: string) -> (json_name, extra: string) { json_name = value @@ -425,12 +428,6 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm defer delete(key, p.allocator) unmarshal_expect_token(p, .Colon) - - field_test :: #force_inline proc "contextless" (field_used: [^]byte, offset: uintptr) -> bool { - prev_set := field_used[offset/8] & byte(offset&7) != 0 - field_used[offset/8] |= byte(offset&7) - return prev_set - } field_used_bytes := (reflect.size_of_typeid(ti.id)+7)/8 field_used := intrinsics.alloca(field_used_bytes + 1, 1) // + 1 to not overflow on size_of 0 types. @@ -449,7 +446,9 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm if use_field_idx < 0 { for field, field_idx in fields { - if key == field.name { + tag_value := reflect.struct_tag_get(field.tag, "json") + json_name, _ := json_name_from_tag_value(tag_value) + if json_name == "" && key == field.name { use_field_idx = field_idx break } @@ -470,7 +469,9 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm } } - if field.name == key || (field.tag != "" && reflect.struct_tag_get(field.tag, "json") == key) { + tag_value := reflect.struct_tag_get(field.tag, "json") + json_name, _ := json_name_from_tag_value(tag_value) + if (json_name == "" && field.name == key) || json_name == key { offset = field.offset type = field.type found = true @@ -492,6 +493,11 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm } if field_found { + field_test :: #force_inline proc "contextless" (field_used: [^]byte, offset: uintptr) -> bool { + prev_set := field_used[offset/8] & byte(offset&7) != 0 + field_used[offset/8] |= byte(offset&7) + return prev_set + } if field_test(field_used, offset) { return .Multiple_Use_Field } -- cgit v1.2.3 From e4bc9677af62c74bb23f4c00d82d2a685ce64e50 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sat, 22 Mar 2025 00:20:00 +0100 Subject: fix unmarshalling bit sets in json Fixes #4761 --- core/encoding/json/unmarshal.odin | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'core/encoding') diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index 57371e360..151bd69c3 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -117,9 +117,25 @@ assign_int :: proc(val: any, i: $T) -> bool { case uint: dst = uint (i) case uintptr: dst = uintptr(i) case: + is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool { + if ti == nil { + return false + } + t := runtime.type_info_base(ti) + #partial switch info in t.variant { + case runtime.Type_Info_Integer: + switch info.endianness { + case .Platform: return false + case .Little: return ODIN_ENDIAN != .Little + case .Big: return ODIN_ENDIAN != .Big + } + } + return false + } + ti := type_info_of(v.id) - if _, ok := ti.variant.(runtime.Type_Info_Bit_Set); ok { - do_byte_swap := !reflect.bit_set_is_big_endian(v) + if info, ok := ti.variant.(runtime.Type_Info_Bit_Set); ok { + do_byte_swap := is_bit_set_different_endian_to_platform(info.underlying) switch ti.size * 8 { case 0: // no-op. case 8: -- cgit v1.2.3 From f7c4c80ef3e50d72e2ec499ef395ed19c514d512 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 5 Apr 2025 16:36:26 +0200 Subject: Fix broken examples in documentation tester. No more: ``` We could not find the procedure "pkg_foo_example :: proc()" needed to test the example created for "pkg.foo" The following procedures were found: bar() ``` --- core/container/intrusive/list/intrusive_list.odin | 18 +-- core/encoding/uuid/generation.odin | 4 +- core/flags/util.odin | 2 +- core/math/rand/rand.odin | 2 +- core/simd/simd.odin | 168 +++++++++++++++------- core/strings/strings.odin | 2 +- 6 files changed, 127 insertions(+), 69 deletions(-) (limited to 'core/encoding') diff --git a/core/container/intrusive/list/intrusive_list.odin b/core/container/intrusive/list/intrusive_list.odin index 5b29efb22..1e116ef18 100644 --- a/core/container/intrusive/list/intrusive_list.odin +++ b/core/container/intrusive/list/intrusive_list.odin @@ -278,19 +278,19 @@ Example: iterate_next_example :: proc() { l: list.List - one := My_Struct{value=1} - two := My_Struct{value=2} + one := My_Next_Struct{value=1} + two := My_Next_Struct{value=2} list.push_back(&l, &one.node) list.push_back(&l, &two.node) - it := list.iterator_head(l, My_Struct, "node") + it := list.iterator_head(l, My_Next_Struct, "node") for num in list.iterate_next(&it) { fmt.println(num.value) } } - My_Struct :: struct { + My_Next_Struct :: struct { node : list.Node, value: int, } @@ -325,22 +325,22 @@ Example: import "core:fmt" import "core:container/intrusive/list" - iterate_next_example :: proc() { + iterate_prev_example :: proc() { l: list.List - one := My_Struct{value=1} - two := My_Struct{value=2} + one := My_Prev_Struct{value=1} + two := My_Prev_Struct{value=2} list.push_back(&l, &one.node) list.push_back(&l, &two.node) - it := list.iterator_tail(l, My_Struct, "node") + it := list.iterator_tail(l, My_Prev_Struct, "node") for num in list.iterate_prev(&it) { fmt.println(num.value) } } - My_Struct :: struct { + My_Prev_Struct :: struct { node : list.Node, value: int, } diff --git a/core/encoding/uuid/generation.odin b/core/encoding/uuid/generation.odin index 7c9d4b80c..b210f6a52 100644 --- a/core/encoding/uuid/generation.odin +++ b/core/encoding/uuid/generation.odin @@ -240,7 +240,7 @@ Example: import "core:encoding/uuid" import "core:fmt" - main :: proc() { + generate_v8_hash_bytes_example :: proc() { my_uuid := uuid.generate_v8_hash(uuid.Namespace_DNS, "www.odin-lang.org", .SHA256) my_uuid_string := uuid.to_string(my_uuid, context.temp_allocator) fmt.println(my_uuid_string) @@ -306,7 +306,7 @@ Example: import "core:encoding/uuid" import "core:fmt" - main :: proc() { + generate_v8_hash_string_example :: proc() { my_uuid := uuid.generate_v8_hash(uuid.Namespace_DNS, "www.odin-lang.org", .SHA256) my_uuid_string := uuid.to_string(my_uuid, context.temp_allocator) fmt.println(my_uuid_string) diff --git a/core/flags/util.odin b/core/flags/util.odin index c182289be..ce7e2e36c 100644 --- a/core/flags/util.odin +++ b/core/flags/util.odin @@ -95,7 +95,7 @@ Example: import "core:flags" import "core:fmt" - subtag_example :: proc() { + get_subtag_example :: proc() { args_tag := "precision=3,signed" precision, has_precision := flags.get_subtag(args_tag, "precision") diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index 537256d32..c556702bd 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -33,7 +33,7 @@ Example: import "core:math/rand" import "core:fmt" - set_global_seed_example :: proc() { + reset_example :: proc() { rand.reset(1) fmt.println(rand.uint64()) } diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 0bce4e16a..37cc19ebd 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -1328,13 +1328,18 @@ Example: // to load valid positions of the `ptrs` array, and the array of defaults which // will have `127` in each position as the default value. - v1 := [4] f32 {1, 2, 3, 4}; - v2 := [4] f32 {9, 10,11,12}; - ptrs := #simd [4]rawptr { &v1[1], nil, &v2[1], nil } - mask := #simd [4]bool { true, false, true, false } - defaults := #simd [4]f32 { 0x7f, 0x7f, 0x7f, 0x7f } - res := simd.gather(ptrs, defaults, mask) - fmt.println(res) + import "core:fmt" + import "core:simd" + + simd_gather_example :: proc() { + v1 := [4] f32 {1, 2, 3, 4}; + v2 := [4] f32 {9, 10,11,12}; + ptrs := #simd [4]rawptr { &v1[1], nil, &v2[1], nil } + mask := #simd [4]bool { true, false, true, false } + defaults := #simd [4]f32 { 0x7f, 0x7f, 0x7f, 0x7f } + res := simd.gather(ptrs, defaults, mask) + fmt.println(res) + } Output: @@ -1396,14 +1401,19 @@ Example: // vectors. The addresses of store destinations are written to the first and the // third argument of the `ptr` vector, and the `mask` is set accordingly. - v1 := [4] f32 {1, 2, 3, 4}; - v2 := [4] f32 {5, 6, 7, 8}; - ptrs := #simd [4]rawptr { &v1[1], nil, &v2[1], nil } - mask := #simd [4]bool { true, false, true, false } - vals := #simd [4]f32 { 0x7f, 0x7f, 0x7f, 0x7f } - simd.scatter(ptrs, vals, mask) - fmt.println(v1) - fmt.println(v2) + import "core:fmt" + import "core:simd" + + simd_scatter_example :: proc() { + v1 := [4] f32 {1, 2, 3, 4}; + v2 := [4] f32 {5, 6, 7, 8}; + ptrs := #simd [4]rawptr { &v1[1], nil, &v2[1], nil } + mask := #simd [4]bool { true, false, true, false } + vals := #simd [4]f32 { 0x7f, 0x7f, 0x7f, 0x7f } + simd.scatter(ptrs, vals, mask) + fmt.println(v1) + fmt.println(v2) + } Output: @@ -1467,11 +1477,16 @@ Example: // third value (selected by the mask). The masked-off values are given the value // of 127 (`0x7f`). - src := [4] f32 {1, 2, 3, 4}; - mask := #simd [4]bool { true, false, true, false } - vals := #simd [4]f32 { 0x7f, 0x7f, 0x7f, 0x7f } - res := simd.masked_load(&src, vals, mask) - fmt.println(res) + import "core:fmt" + import "core:simd" + + simd_masked_load_example :: proc() { + src := [4] f32 {1, 2, 3, 4}; + mask := #simd [4]bool { true, false, true, false } + vals := #simd [4]f32 { 0x7f, 0x7f, 0x7f, 0x7f } + res := simd.masked_load(&src, vals, mask) + fmt.println(res) + } Output: @@ -1526,11 +1541,16 @@ Example: // Example below stores the value 127 into the first and the third slot of the // vector `v`. - v := [4] f32 {1, 2, 3, 4}; - mask := #simd [4]bool { true, false, true, false } - vals := #simd [4]f32 { 0x7f, 0x7f, 0x7f, 0x7f } - simd.masked_store(&v, vals, mask) - fmt.println(v) + import "core:fmt" + import "core:simd" + + simd_masked_store_example :: proc() { + v := [4] f32 {1, 2, 3, 4}; + mask := #simd [4]bool { true, false, true, false } + vals := #simd [4]f32 { 0x7f, 0x7f, 0x7f, 0x7f } + simd.masked_store(&v, vals, mask) + fmt.println(v) + } Output: @@ -1600,11 +1620,16 @@ Example: // the third lane of the result vector. All the other lanes of the result vector // will be initialized to the default value `127`. - v := [2] f64 {1, 2}; - mask := #simd [4]bool { true, false, true, false } - vals := #simd [4]f64 { 0x7f, 0x7f, 0x7f, 0x7f } - res := simd.masked_expand_load(&v, vals, mask) - fmt.println(res) + import "core:fmt" + import "core:simd" + + simd_masked_expand_load_example :: proc() { + v := [2] f64 {1, 2}; + mask := #simd [4]bool { true, false, true, false } + vals := #simd [4]f64 { 0x7f, 0x7f, 0x7f, 0x7f } + res := simd.masked_expand_load(&v, vals, mask) + fmt.println(res) + } Output: @@ -1661,11 +1686,16 @@ Example: // vector, the first and the third value. The items in the mask are set to `true` // in those lanes. - v := [2] f64 { }; - mask := #simd [4]bool { true, false, true, false } - vals := #simd [4]f64 { 1, 2, 3, 4 } - simd.masked_compress_store(&v, vals, mask) - fmt.println(v) + import "core:fmt" + import "core:simd" + + simd_masked_compress_store_example :: proc() { + v := [2] f64 { }; + mask := #simd [4]bool { true, false, true, false } + vals := #simd [4]f64 { 1, 2, 3, 4 } + simd.masked_compress_store(&v, vals, mask) + fmt.println(v) + } Output: @@ -1949,14 +1979,19 @@ Example: // The example below shows how the indices are used to determine which lanes of the // input vector get written into the result vector. - - x := #simd [4]f32 { 1.5, 2.5, 3.5, 4.5 } - res := simd.swizzle(x, 0, 3, 1, 1) - fmt.println("res") + + import "core:fmt" + import "core:simd" + + swizzle_example :: proc() { + x := #simd [4]f32 { 1.5, 2.5, 3.5, 4.5 } + res := simd.swizzle(x, 0, 3, 1, 1) + fmt.println(res) + } Output: - [ 1.5, 3.5, 2.5, 2.5 ] + <1.5, 4.5, 2.5, 2.5> The graphical representation of the operation is as follows. The `idx` vector in the picture represents the `indices` parameter: @@ -2013,8 +2048,14 @@ Example: // Since lanes 0, 1, 4, 7 contain negative numbers, the most significant // bits for them will be set. - v := #simd [8]i32 { -1, -2, +3, +4, -5, +6, +7, -8 } - fmt.println(simd.extract_msbs(v)) + + import "core:fmt" + import "core:simd" + + simd_extract_msbs_example :: proc() { + v := #simd [8]i32 { -1, -2, +3, +4, -5, +6, +7, -8 } + fmt.println(simd.extract_msbs(v)) + } Output: @@ -2052,8 +2093,14 @@ Example: // Since lanes 0, 2, 4, 6 contain odd integers, the least significant bits // for these lanes are set. - v := #simd [8]i32 { -1, -2, +3, +4, -5, +6, +7, -8 } - fmt.println(simd.extract_lsbs(v)) + + import "core:fmt" + import "core:simd" + + simd_extract_lsbs_example :: proc() { + v := #simd [8]i32 { -1, -2, +3, +4, -5, +6, +7, -8 } + fmt.println(simd.extract_lsbs(v)) + } Output: @@ -2097,15 +2144,20 @@ Example: // The example below shows how the indices are used to determine lanes of the // input vector that are shuffled into the result vector. - - a := #simd [4]f32 { 1, 2, 3, 4 } - b := #simd [4]f32 { 5, 6, 7, 8 } - res := simd.shuffle(a, b, 0, 4, 2, 5) - fmt.println("res") + + import "core:fmt" + import "core:simd" + + simd_shuffle_example :: proc() { + a := #simd [4]f32 { 1, 2, 3, 4 } + b := #simd [4]f32 { 5, 6, 7, 8 } + res := simd.shuffle(a, b, 0, 4, 2, 5) + fmt.println(res) + } Output: - [ 1, 5, 3, 6 ] + <1, 5, 3, 6> The graphical representation of the operation is as follows. The `idx` vector in the picture represents the `indices` parameter: @@ -2163,14 +2215,20 @@ Example: // The following example selects values from the two input vectors, `a` and `b` // into a single vector. - a := #simd [4] f64 { 1,2,3,4 } - b := #simd [4] f64 { 5,6,7,8 } - cond := #simd[4] int { 1, 0, 1, 0 } - fmt.println(simd.select(cond,a,b)) + + import "core:fmt" + import "core:simd" + + simd_select_example :: proc() { + a := #simd [4] f64 { 1,2,3,4 } + b := #simd [4] f64 { 5,6,7,8 } + cond := #simd[4] int { 1, 0, 1, 0 } + fmt.println(simd.select(cond,a,b)) + } Output: - [ 1, 6, 3, 8 ] + <1, 6, 3, 8> Graphically, the operation looks as follows. The `t` and `f` represent the `true` and `false` vectors respectively: diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 52230f572..247638cd6 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -787,7 +787,7 @@ Example: import "core:fmt" import "core:strings" - cut_example :: proc() { + cut_clone_example :: proc() { fmt.println(strings.cut_clone("some example text", 0, 4)) // -> "some" fmt.println(strings.cut_clone("some example text", 2, 2)) // -> "me" fmt.println(strings.cut_clone("some example text", 5, 7)) // -> "example" -- cgit v1.2.3 From 3287e1b0f0195401f6db8ffe9b94be67b352af57 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 7 Apr 2025 13:19:00 +0200 Subject: Fix HXA defer warning --- core/encoding/hxa/read.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'core/encoding') diff --git a/core/encoding/hxa/read.odin b/core/encoding/hxa/read.odin index a679946f8..6dde16848 100644 --- a/core/encoding/hxa/read.odin +++ b/core/encoding/hxa/read.odin @@ -79,7 +79,6 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato read_meta :: proc(r: ^Reader, capacity: u32le, allocator := context.allocator, loc := #caller_location) -> (meta_data: []Meta, err: Read_Error) { meta_data = make([]Meta, int(capacity), allocator=allocator) count := 0 - defer meta_data = meta_data[:count] for &m in meta_data { m.name = read_name(r) or_return @@ -105,6 +104,7 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato count += 1 } + meta_data = meta_data[:count] return } @@ -112,7 +112,6 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato stack_count := read_value(r, u32le) or_return layer_count := 0 layers = make(Layer_Stack, stack_count, allocator=allocator, loc=loc) - defer layers = layers[:layer_count] for &layer in layers { layer.name = read_name(r) or_return layer.components = read_value(r, u8) or_return @@ -136,6 +135,7 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato layer_count += 1 } + layers = layers[:layer_count] return } -- cgit v1.2.3 From 062a3c2fae3712c60af00798a0815509a732790b Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 19 Apr 2025 20:25:44 +0200 Subject: Fix parsing of CDATA tags (#5059) Fixes #5054 --- core/encoding/entity/entity.odin | 54 ++++++++-------- core/encoding/xml/tokenizer.odin | 38 ++++++------ core/encoding/xml/xml_reader.odin | 99 +++++++++++++++++------------- tests/core/assets/XML/entities.html | 5 ++ tests/core/encoding/xml/test_core_xml.odin | 12 ++-- 5 files changed, 110 insertions(+), 98 deletions(-) (limited to 'core/encoding') diff --git a/core/encoding/entity/entity.odin b/core/encoding/entity/entity.odin index d2f1d46b2..cb8fa8611 100644 --- a/core/encoding/entity/entity.odin +++ b/core/encoding/entity/entity.odin @@ -108,7 +108,7 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator := it couldn't have been part of an XML tag body to be decoded here. Keep in mind that we could already *be* inside a CDATA tag. - If so, write `>` as a literal and continue. + If so, write `<` as a literal and continue. */ if in_data { write_rune(&builder, '<') @@ -119,11 +119,9 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator := case ']': // If we're unboxing _and_ decoding CDATA, we'll have to check for the end tag. if in_data { - if t.read_offset + len(CDATA_END) < len(t.src) { - if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END { - in_data = false - t.read_offset += len(CDATA_END) - 1 - } + if strings.has_prefix(t.src[t.offset:], CDATA_END) { + in_data = false + t.read_offset += len(CDATA_END) - 1 } continue } else { @@ -297,40 +295,40 @@ _handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: X assert(t != nil && t.r == '<') if t.read_offset + len(CDATA_START) >= len(t.src) { return false, .None } - if string(t.src[t.offset:][:len(CDATA_START)]) == CDATA_START { - t.read_offset += len(CDATA_START) - 1 - + s := string(t.src[t.offset:]) + if strings.has_prefix(s, CDATA_START) { if .Unbox_CDATA in options && .Decode_CDATA in options { // We're unboxing _and_ decoding CDATA + t.read_offset += len(CDATA_START) - 1 return true, .None } - // CDATA is passed through. - offset := t.offset - - // Scan until end of CDATA. + // CDATA is passed through. Scan until end of CDATA. + start_offset := t.offset + t.read_offset += len(CDATA_START) for { - advance(t) or_return - if t.r < 0 { return true, .CDATA_Not_Terminated } - - if t.read_offset + len(CDATA_END) < len(t.src) { - if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END { - t.read_offset += len(CDATA_END) - 1 + advance(t) + if t.r < 0 { + // error(t, offset, "[scan_string] CDATA was not terminated\n") + return true, .CDATA_Not_Terminated + } - cdata := string(t.src[offset : t.read_offset]) - - if .Unbox_CDATA in options { - cdata = cdata[len(CDATA_START):] - cdata = cdata[:len(cdata) - len(CDATA_END)] - } + // Scan until the end of a CDATA tag. + if s = string(t.src[t.read_offset:]); strings.has_prefix(s, CDATA_END) { + t.read_offset += len(CDATA_END) + cdata := string(t.src[start_offset:t.read_offset]) - write_string(builder, cdata) - return false, .None + if .Unbox_CDATA in options { + cdata = cdata[len(CDATA_START):] + cdata = cdata[:len(cdata) - len(CDATA_END)] } + write_string(builder, cdata) + return false, .None } } - } else if string(t.src[t.offset:][:len(COMMENT_START)]) == COMMENT_START { + + } else if strings.has_prefix(s, COMMENT_START) { t.read_offset += len(COMMENT_START) // Comment is passed through by default. offset := t.offset diff --git a/core/encoding/xml/tokenizer.odin b/core/encoding/xml/tokenizer.odin index a2bbaf28e..3ef9a6388 100644 --- a/core/encoding/xml/tokenizer.odin +++ b/core/encoding/xml/tokenizer.odin @@ -16,6 +16,7 @@ package encoding_xml import "core:fmt" import "core:unicode" import "core:unicode/utf8" +import "core:strings" Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any) @@ -121,7 +122,7 @@ default_error_handler :: proc(pos: Pos, msg: string, args: ..any) { error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) { pos := offset_to_pos(t, offset) if t.err != nil { - t.err(pos, msg, ..args) + t.err(pos=pos, fmt=msg, args=args) } t.error_count += 1 } @@ -268,32 +269,27 @@ scan_comment :: proc(t: ^Tokenizer) -> (comment: string, err: Error) { // Skip CDATA skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) { - if t.read_offset + len(CDATA_START) >= len(t.src) { - // Can't be the start of a CDATA tag. + if s := string(t.src[t.offset:]); !strings.has_prefix(s, CDATA_START) { return .None } - if string(t.src[t.offset:][:len(CDATA_START)]) == CDATA_START { - t.read_offset += len(CDATA_START) - offset := t.offset + t.read_offset += len(CDATA_START) + offset := t.offset - cdata_scan: for { - advance_rune(t) - if t.ch < 0 { - error(t, offset, "[scan_string] CDATA was not terminated\n") - return .Premature_EOF - } + cdata_scan: for { + advance_rune(t) + if t.ch < 0 { + error(t, offset, "[scan_string] CDATA was not terminated\n") + return .Premature_EOF + } - // Scan until the end of a CDATA tag. - if t.read_offset + len(CDATA_END) < len(t.src) { - if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END { - t.read_offset += len(CDATA_END) - break cdata_scan - } - } + // Scan until the end of a CDATA tag. + if s := string(t.src[t.read_offset:]); strings.has_prefix(s, CDATA_END) { + t.read_offset += len(CDATA_END) + break cdata_scan } } - return + return .None } @(optimization_mode="favor_size") @@ -393,6 +389,8 @@ scan :: proc(t: ^Tokenizer, multiline_string := false) -> Token { case '/': kind = .Slash case '-': kind = .Dash case ':': kind = .Colon + case '[': kind = .Open_Bracket + case ']': kind = .Close_Bracket case '"', '\'': kind = .Invalid diff --git a/core/encoding/xml/xml_reader.odin b/core/encoding/xml/xml_reader.odin index b8c8b13a4..60744357c 100644 --- a/core/encoding/xml/xml_reader.odin +++ b/core/encoding/xml/xml_reader.odin @@ -56,7 +56,7 @@ Option_Flag :: enum { Option_Flags :: bit_set[Option_Flag; u16] Document :: struct { - elements: [dynamic]Element, + elements: [dynamic]Element `fmt:"v,element_count"`, element_count: Element_ID, prologue: Attributes, @@ -70,15 +70,15 @@ Document :: struct { // If we encounter comments before the root node, and the option to intern comments is given, this is where they'll live. // Otherwise they'll be in the element tree. - comments: [dynamic]string, + comments: [dynamic]string `fmt:"-"`, // Internal - tokenizer: ^Tokenizer, - allocator: mem.Allocator, + tokenizer: ^Tokenizer `fmt:"-"`, + allocator: mem.Allocator `fmt:"-"`, // Input. Either the original buffer, or a copy if `.Input_May_Be_Modified` isn't specified. - input: []u8, - strings_to_free: [dynamic]string, + input: []u8 `fmt:"-"`, + strings_to_free: [dynamic]string `fmt:"-"`, } Element :: struct { @@ -195,7 +195,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha loop: for { skip_whitespace(t) - // NOTE(Jeroen): This is faster as a switch. switch t.ch { case '<': // Consume peeked `<` @@ -306,9 +305,13 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha } } + case .Open_Bracket: + // This could be a CDATA tag part of a tag's body. Unread the ` (attr: Attribute, offset: int, err: E t := doc.tokenizer key := expect(t, .Ident) or_return - offset = t.offset - len(key.text) - _ = expect(t, .Eq) or_return value := expect(t, .String, multiline_string=true) or_return @@ -591,6 +561,47 @@ parse_doctype :: proc(doc: ^Document) -> (err: Error) { return .None } +parse_body :: proc(doc: ^Document, element: Element_ID, opts: Options) -> (err: Error) { + assert(doc != nil) + context.allocator = doc.allocator + t := doc.tokenizer + + body_text := scan_string(t, t.offset) or_return + needs_processing := .Unbox_CDATA in opts.flags + needs_processing |= .Decode_SGML_Entities in opts.flags + + if !needs_processing { + append(&doc.elements[element].value, body_text) + return + } + + decode_opts := entity.XML_Decode_Options{} + if .Keep_Tag_Body_Comments not_in opts.flags { + decode_opts += { .Comment_Strip } + } + + if .Decode_SGML_Entities not_in opts.flags { + decode_opts += { .No_Entity_Decode } + } + + if .Unbox_CDATA in opts.flags { + decode_opts += { .Unbox_CDATA } + if .Decode_SGML_Entities in opts.flags { + decode_opts += { .Decode_CDATA } + } + } + + decoded, decode_err := entity.decode_xml(body_text, decode_opts) + if decode_err == .None { + append(&doc.elements[element].value, decoded) + append(&doc.strings_to_free, decoded) + } else { + append(&doc.elements[element].value, body_text) + } + + return +} + Element_ID :: u32 new_element :: proc(doc: ^Document) -> (id: Element_ID) { @@ -609,4 +620,4 @@ new_element :: proc(doc: ^Document) -> (id: Element_ID) { cur := doc.element_count doc.element_count += 1 return cur -} +} \ No newline at end of file diff --git a/tests/core/assets/XML/entities.html b/tests/core/assets/XML/entities.html index 05a6b107e..a60f45070 100644 --- a/tests/core/assets/XML/entities.html +++ b/tests/core/assets/XML/entities.html @@ -25,5 +25,10 @@
| | | fj ` \ ® ϱ ∳ ⁏
+
HHellope!
+
HHellope!
+
HHellope!
+
HHellope!
+
HHellope!
\ No newline at end of file diff --git a/tests/core/encoding/xml/test_core_xml.odin b/tests/core/encoding/xml/test_core_xml.odin index c0e4329bd..409a8c9c0 100644 --- a/tests/core/encoding/xml/test_core_xml.odin +++ b/tests/core/encoding/xml/test_core_xml.odin @@ -114,7 +114,7 @@ xml_test_entities :: proc(t: ^testing.T) { }, expected_doctype = "html", }, - crc32 = 0x05373317, + crc32 = 0x48f41216, }) } @@ -128,7 +128,7 @@ xml_test_entities_unbox :: proc(t: ^testing.T) { }, expected_doctype = "html", }, - crc32 = 0x350ca83e, + crc32 = 0xd0567818, }) } @@ -142,7 +142,7 @@ xml_test_entities_unbox_decode :: proc(t: ^testing.T) { }, expected_doctype = "html", }, - crc32 = 0x7f58db7d, + crc32 = 0x68d2571e, }) } @@ -191,7 +191,7 @@ xml_test_unicode :: proc(t: ^testing.T) { } @(private) -run_test :: proc(t: ^testing.T, test: TEST) { +run_test :: proc(t: ^testing.T, test: TEST, loc := #caller_location) { path := strings.concatenate({TEST_SUITE_PATH, test.filename}) defer delete(path) @@ -205,10 +205,10 @@ run_test :: proc(t: ^testing.T, test: TEST) { crc32 := hash.crc32(tree_bytes) failed := err != test.err - testing.expectf(t, err == test.err, "%v: Expected return value %v, got %v", test.filename, test.err, err) + testing.expectf(t, err == test.err, "%v: Expected return value %v, got %v", test.filename, test.err, err, loc=loc) failed |= crc32 != test.crc32 - testing.expectf(t, crc32 == test.crc32, "%v: Expected CRC 0x%08x, got 0x%08x, with options %v", test.filename, test.crc32, crc32, test.options) + testing.expectf(t, crc32 == test.crc32, "%v: Expected CRC 0x%08x, got 0x%08x, with options %v", test.filename, test.crc32, crc32, test.options, loc=loc) if failed { // Don't fully print big trees. -- cgit v1.2.3 From ab5ca087a7a64e2085edd955b403da0e8bc2c4f0 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 19 Apr 2025 23:44:02 +0200 Subject: Add comment --- core/encoding/xml/xml_reader.odin | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'core/encoding') diff --git a/core/encoding/xml/xml_reader.odin b/core/encoding/xml/xml_reader.odin index 60744357c..d616be9dc 100644 --- a/core/encoding/xml/xml_reader.odin +++ b/core/encoding/xml/xml_reader.odin @@ -308,6 +308,10 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha case .Open_Bracket: // This could be a CDATA tag part of a tag's body. Unread the ` Date: Mon, 28 Apr 2025 22:03:20 +0300 Subject: Fix typo in private function --- core/encoding/json/tokenizer.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'core/encoding') diff --git a/core/encoding/json/tokenizer.odin b/core/encoding/json/tokenizer.odin index e46d879a7..ad928b7d9 100644 --- a/core/encoding/json/tokenizer.odin +++ b/core/encoding/json/tokenizer.odin @@ -101,7 +101,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { } } - scan_espace :: proc(t: ^Tokenizer) -> bool { + scan_escape :: proc(t: ^Tokenizer) -> bool { switch t.r { case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f': next_rune(t) @@ -310,7 +310,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) { break } if r == '\\' { - scan_espace(t) + scan_escape(t) } } -- cgit v1.2.3 From 4f00224dd2908dc21c5412eba9167c63a217bf33 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 29 Apr 2025 01:10:15 +0200 Subject: Add cbor.unmarshal_from_bytes taking a []byte --- core/encoding/cbor/unmarshal.odin | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'core/encoding') diff --git a/core/encoding/cbor/unmarshal.odin b/core/encoding/cbor/unmarshal.odin index c39255d9d..f752c5275 100644 --- a/core/encoding/cbor/unmarshal.odin +++ b/core/encoding/cbor/unmarshal.odin @@ -29,6 +29,7 @@ an input. unmarshal :: proc { unmarshal_from_reader, unmarshal_from_string, + unmarshal_from_bytes, } unmarshal_from_reader :: proc(r: io.Reader, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) { @@ -51,6 +52,11 @@ unmarshal_from_string :: proc(s: string, ptr: ^$T, flags := Decoder_Flags{}, all return } +// Unmarshals from a slice of bytes, see docs on the proc group `Unmarshal` for more info. +unmarshal_from_bytes :: proc(bytes: []byte, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) { + return unmarshal_from_string(string(bytes), ptr, flags, allocator, temp_allocator, loc) +} + unmarshal_from_decoder :: proc(d: Decoder, ptr: ^$T, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) { d := d -- cgit v1.2.3 From 3f5e09a0df18201e30a202a2074dc0c0a283c01b Mon Sep 17 00:00:00 2001 From: Barinzaya Date: Tue, 29 Apr 2025 08:19:43 -0400 Subject: Fixed an overflow when decoding a large CBOR slice. The initial allocation for the slice is limited to prevent untrusted data from forcing a huge allocation, but then the dynamic array was created with a capacity of the unlimited length, rather than the actual capacity of the allocation. This was causing a buffer overrun. --- core/encoding/cbor/unmarshal.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core/encoding') diff --git a/core/encoding/cbor/unmarshal.odin b/core/encoding/cbor/unmarshal.odin index f752c5275..24bbd8137 100644 --- a/core/encoding/cbor/unmarshal.odin +++ b/core/encoding/cbor/unmarshal.odin @@ -493,7 +493,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align, allocator=allocator, loc=loc) or_return defer if err != nil { mem.free_bytes(data, allocator=allocator, loc=loc) } - da := mem.Raw_Dynamic_Array{raw_data(data), 0, length, context.allocator } + da := mem.Raw_Dynamic_Array{raw_data(data), 0, scap, context.allocator } assign_array(d, &da, t.elem, length) or_return -- cgit v1.2.3 From a9df1b1cde1037d030f4e823ce576dfd9bcf9c97 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 20 May 2025 15:41:35 -0400 Subject: Rename `core:encoding/ansi` to `core:terminal/ansi` --- core/encoding/ansi/ansi.odin | 137 ---------------------------------- core/encoding/ansi/doc.odin | 20 ----- core/log/file_console_logger.odin | 2 +- core/terminal/ansi/ansi.odin | 137 ++++++++++++++++++++++++++++++++++ core/terminal/ansi/doc.odin | 20 +++++ core/testing/reporting.odin | 2 +- core/testing/runner.odin | 2 +- core/testing/signal_handler_libc.odin | 4 +- examples/all/all_main.odin | 4 +- 9 files changed, 164 insertions(+), 164 deletions(-) delete mode 100644 core/encoding/ansi/ansi.odin delete mode 100644 core/encoding/ansi/doc.odin create mode 100644 core/terminal/ansi/ansi.odin create mode 100644 core/terminal/ansi/doc.odin (limited to 'core/encoding') diff --git a/core/encoding/ansi/ansi.odin b/core/encoding/ansi/ansi.odin deleted file mode 100644 index 5550a1671..000000000 --- a/core/encoding/ansi/ansi.odin +++ /dev/null @@ -1,137 +0,0 @@ -package ansi - -BEL :: "\a" // Bell -BS :: "\b" // Backspace -ESC :: "\e" // Escape - -// Fe Escape sequences - -CSI :: ESC + "[" // Control Sequence Introducer -OSC :: ESC + "]" // Operating System Command -ST :: ESC + "\\" // String Terminator - -// CSI sequences - -CUU :: "A" // Cursor Up -CUD :: "B" // Cursor Down -CUF :: "C" // Cursor Forward -CUB :: "D" // Cursor Back -CNL :: "E" // Cursor Next Line -CPL :: "F" // Cursor Previous Line -CHA :: "G" // Cursor Horizontal Absolute -CUP :: "H" // Cursor Position -ED :: "J" // Erase in Display -EL :: "K" // Erase in Line -SU :: "S" // Scroll Up -SD :: "T" // Scroll Down -HVP :: "f" // Horizontal Vertical Position -SGR :: "m" // Select Graphic Rendition -AUX_ON :: "5i" // AUX Port On -AUX_OFF :: "4i" // AUX Port Off -DSR :: "6n" // Device Status Report - -// CSI: private sequences - -SCP :: "s" // Save Current Cursor Position -RCP :: "u" // Restore Saved Cursor Position -DECAWM_ON :: "?7h" // Auto Wrap Mode (Enabled) -DECAWM_OFF :: "?7l" // Auto Wrap Mode (Disabled) -DECTCEM_SHOW :: "?25h" // Text Cursor Enable Mode (Visible) -DECTCEM_HIDE :: "?25l" // Text Cursor Enable Mode (Invisible) - -// SGR sequences - -RESET :: "0" -BOLD :: "1" -FAINT :: "2" -ITALIC :: "3" // Not widely supported. -UNDERLINE :: "4" -BLINK_SLOW :: "5" -BLINK_RAPID :: "6" // Not widely supported. -INVERT :: "7" // Also known as reverse video. -HIDE :: "8" // Not widely supported. -STRIKE :: "9" -FONT_PRIMARY :: "10" -FONT_ALT1 :: "11" -FONT_ALT2 :: "12" -FONT_ALT3 :: "13" -FONT_ALT4 :: "14" -FONT_ALT5 :: "15" -FONT_ALT6 :: "16" -FONT_ALT7 :: "17" -FONT_ALT8 :: "18" -FONT_ALT9 :: "19" -FONT_FRAKTUR :: "20" // Rarely supported. -UNDERLINE_DOUBLE :: "21" // May be interpreted as "disable bold." -NO_BOLD_FAINT :: "22" -NO_ITALIC_BLACKLETTER :: "23" -NO_UNDERLINE :: "24" -NO_BLINK :: "25" -PROPORTIONAL_SPACING :: "26" -NO_REVERSE :: "27" -NO_HIDE :: "28" -NO_STRIKE :: "29" - -FG_BLACK :: "30" -FG_RED :: "31" -FG_GREEN :: "32" -FG_YELLOW :: "33" -FG_BLUE :: "34" -FG_MAGENTA :: "35" -FG_CYAN :: "36" -FG_WHITE :: "37" -FG_COLOR :: "38" -FG_COLOR_8_BIT :: "38;5" // Followed by ";n" where n is in 0..=255 -FG_COLOR_24_BIT :: "38;2" // Followed by ";r;g;b" where r,g,b are in 0..=255 -FG_DEFAULT :: "39" - -BG_BLACK :: "40" -BG_RED :: "41" -BG_GREEN :: "42" -BG_YELLOW :: "43" -BG_BLUE :: "44" -BG_MAGENTA :: "45" -BG_CYAN :: "46" -BG_WHITE :: "47" -BG_COLOR :: "48" -BG_COLOR_8_BIT :: "48;5" // Followed by ";n" where n is in 0..=255 -BG_COLOR_24_BIT :: "48;2" // Followed by ";r;g;b" where r,g,b are in 0..=255 -BG_DEFAULT :: "49" - -NO_PROPORTIONAL_SPACING :: "50" -FRAMED :: "51" -ENCIRCLED :: "52" -OVERLINED :: "53" -NO_FRAME_ENCIRCLE :: "54" -NO_OVERLINE :: "55" - -// SGR: non-standard bright colors - -FG_BRIGHT_BLACK :: "90" // Also known as grey. -FG_BRIGHT_RED :: "91" -FG_BRIGHT_GREEN :: "92" -FG_BRIGHT_YELLOW :: "93" -FG_BRIGHT_BLUE :: "94" -FG_BRIGHT_MAGENTA :: "95" -FG_BRIGHT_CYAN :: "96" -FG_BRIGHT_WHITE :: "97" - -BG_BRIGHT_BLACK :: "100" // Also known as grey. -BG_BRIGHT_RED :: "101" -BG_BRIGHT_GREEN :: "102" -BG_BRIGHT_YELLOW :: "103" -BG_BRIGHT_BLUE :: "104" -BG_BRIGHT_MAGENTA :: "105" -BG_BRIGHT_CYAN :: "106" -BG_BRIGHT_WHITE :: "107" - -// Fp Escape sequences - -DECSC :: ESC + "7" // DEC Save Cursor -DECRC :: ESC + "8" // DEC Restore Cursor - -// OSC sequences - -WINDOW_TITLE :: "2" // Followed by ";" ST. -HYPERLINK :: "8" // Followed by ";[params];" ST. Closed by OSC HYPERLINK ";;" ST. -CLIPBOARD :: "52" // Followed by ";c;" ST. diff --git a/core/encoding/ansi/doc.odin b/core/encoding/ansi/doc.odin deleted file mode 100644 index 966e6be00..000000000 --- a/core/encoding/ansi/doc.odin +++ /dev/null @@ -1,20 +0,0 @@ -/* -package ansi implements constant references to many widely-supported ANSI -escape codes, primarily used in terminal emulators for enhanced graphics, such -as colors, text styling, and animated displays. - -For example, you can print out a line of cyan text like this: - fmt.println(ansi.CSI + ansi.FG_CYAN + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR) - -Multiple SGR (Select Graphic Rendition) codes can be joined by semicolons: - fmt.println(ansi.CSI + ansi.BOLD + ";" + ansi.FG_BLUE + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR) - -If your terminal supports 24-bit true color mode, you can also do this: - fmt.println(ansi.CSI + ansi.FG_COLOR_24_BIT + ";0;255;255" + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR) - -For more information, see: -- [[ https://en.wikipedia.org/wiki/ANSI_escape_code ]] -- [[ https://www.vt100.net/docs/vt102-ug/chapter5.html ]] -- [[ https://invisible-island.net/xterm/ctlseqs/ctlseqs.html ]] -*/ -package ansi diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin index f807f321f..0fe5c3477 100644 --- a/core/log/file_console_logger.odin +++ b/core/log/file_console_logger.odin @@ -3,11 +3,11 @@ package log import "base:runtime" -import "core:encoding/ansi" import "core:fmt" import "core:strings" import "core:os" import "core:terminal" +import "core:terminal/ansi" import "core:time" Level_Headers := [?]string{ diff --git a/core/terminal/ansi/ansi.odin b/core/terminal/ansi/ansi.odin new file mode 100644 index 000000000..5550a1671 --- /dev/null +++ b/core/terminal/ansi/ansi.odin @@ -0,0 +1,137 @@ +package ansi + +BEL :: "\a" // Bell +BS :: "\b" // Backspace +ESC :: "\e" // Escape + +// Fe Escape sequences + +CSI :: ESC + "[" // Control Sequence Introducer +OSC :: ESC + "]" // Operating System Command +ST :: ESC + "\\" // String Terminator + +// CSI sequences + +CUU :: "A" // Cursor Up +CUD :: "B" // Cursor Down +CUF :: "C" // Cursor Forward +CUB :: "D" // Cursor Back +CNL :: "E" // Cursor Next Line +CPL :: "F" // Cursor Previous Line +CHA :: "G" // Cursor Horizontal Absolute +CUP :: "H" // Cursor Position +ED :: "J" // Erase in Display +EL :: "K" // Erase in Line +SU :: "S" // Scroll Up +SD :: "T" // Scroll Down +HVP :: "f" // Horizontal Vertical Position +SGR :: "m" // Select Graphic Rendition +AUX_ON :: "5i" // AUX Port On +AUX_OFF :: "4i" // AUX Port Off +DSR :: "6n" // Device Status Report + +// CSI: private sequences + +SCP :: "s" // Save Current Cursor Position +RCP :: "u" // Restore Saved Cursor Position +DECAWM_ON :: "?7h" // Auto Wrap Mode (Enabled) +DECAWM_OFF :: "?7l" // Auto Wrap Mode (Disabled) +DECTCEM_SHOW :: "?25h" // Text Cursor Enable Mode (Visible) +DECTCEM_HIDE :: "?25l" // Text Cursor Enable Mode (Invisible) + +// SGR sequences + +RESET :: "0" +BOLD :: "1" +FAINT :: "2" +ITALIC :: "3" // Not widely supported. +UNDERLINE :: "4" +BLINK_SLOW :: "5" +BLINK_RAPID :: "6" // Not widely supported. +INVERT :: "7" // Also known as reverse video. +HIDE :: "8" // Not widely supported. +STRIKE :: "9" +FONT_PRIMARY :: "10" +FONT_ALT1 :: "11" +FONT_ALT2 :: "12" +FONT_ALT3 :: "13" +FONT_ALT4 :: "14" +FONT_ALT5 :: "15" +FONT_ALT6 :: "16" +FONT_ALT7 :: "17" +FONT_ALT8 :: "18" +FONT_ALT9 :: "19" +FONT_FRAKTUR :: "20" // Rarely supported. +UNDERLINE_DOUBLE :: "21" // May be interpreted as "disable bold." +NO_BOLD_FAINT :: "22" +NO_ITALIC_BLACKLETTER :: "23" +NO_UNDERLINE :: "24" +NO_BLINK :: "25" +PROPORTIONAL_SPACING :: "26" +NO_REVERSE :: "27" +NO_HIDE :: "28" +NO_STRIKE :: "29" + +FG_BLACK :: "30" +FG_RED :: "31" +FG_GREEN :: "32" +FG_YELLOW :: "33" +FG_BLUE :: "34" +FG_MAGENTA :: "35" +FG_CYAN :: "36" +FG_WHITE :: "37" +FG_COLOR :: "38" +FG_COLOR_8_BIT :: "38;5" // Followed by ";n" where n is in 0..=255 +FG_COLOR_24_BIT :: "38;2" // Followed by ";r;g;b" where r,g,b are in 0..=255 +FG_DEFAULT :: "39" + +BG_BLACK :: "40" +BG_RED :: "41" +BG_GREEN :: "42" +BG_YELLOW :: "43" +BG_BLUE :: "44" +BG_MAGENTA :: "45" +BG_CYAN :: "46" +BG_WHITE :: "47" +BG_COLOR :: "48" +BG_COLOR_8_BIT :: "48;5" // Followed by ";n" where n is in 0..=255 +BG_COLOR_24_BIT :: "48;2" // Followed by ";r;g;b" where r,g,b are in 0..=255 +BG_DEFAULT :: "49" + +NO_PROPORTIONAL_SPACING :: "50" +FRAMED :: "51" +ENCIRCLED :: "52" +OVERLINED :: "53" +NO_FRAME_ENCIRCLE :: "54" +NO_OVERLINE :: "55" + +// SGR: non-standard bright colors + +FG_BRIGHT_BLACK :: "90" // Also known as grey. +FG_BRIGHT_RED :: "91" +FG_BRIGHT_GREEN :: "92" +FG_BRIGHT_YELLOW :: "93" +FG_BRIGHT_BLUE :: "94" +FG_BRIGHT_MAGENTA :: "95" +FG_BRIGHT_CYAN :: "96" +FG_BRIGHT_WHITE :: "97" + +BG_BRIGHT_BLACK :: "100" // Also known as grey. +BG_BRIGHT_RED :: "101" +BG_BRIGHT_GREEN :: "102" +BG_BRIGHT_YELLOW :: "103" +BG_BRIGHT_BLUE :: "104" +BG_BRIGHT_MAGENTA :: "105" +BG_BRIGHT_CYAN :: "106" +BG_BRIGHT_WHITE :: "107" + +// Fp Escape sequences + +DECSC :: ESC + "7" // DEC Save Cursor +DECRC :: ESC + "8" // DEC Restore Cursor + +// OSC sequences + +WINDOW_TITLE :: "2" // Followed by ";" ST. +HYPERLINK :: "8" // Followed by ";[params];" ST. Closed by OSC HYPERLINK ";;" ST. +CLIPBOARD :: "52" // Followed by ";c;" ST. diff --git a/core/terminal/ansi/doc.odin b/core/terminal/ansi/doc.odin new file mode 100644 index 000000000..966e6be00 --- /dev/null +++ b/core/terminal/ansi/doc.odin @@ -0,0 +1,20 @@ +/* +package ansi implements constant references to many widely-supported ANSI +escape codes, primarily used in terminal emulators for enhanced graphics, such +as colors, text styling, and animated displays. + +For example, you can print out a line of cyan text like this: + fmt.println(ansi.CSI + ansi.FG_CYAN + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR) + +Multiple SGR (Select Graphic Rendition) codes can be joined by semicolons: + fmt.println(ansi.CSI + ansi.BOLD + ";" + ansi.FG_BLUE + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR) + +If your terminal supports 24-bit true color mode, you can also do this: + fmt.println(ansi.CSI + ansi.FG_COLOR_24_BIT + ";0;255;255" + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR) + +For more information, see: +- [[ https://en.wikipedia.org/wiki/ANSI_escape_code ]] +- [[ https://www.vt100.net/docs/vt102-ug/chapter5.html ]] +- [[ https://invisible-island.net/xterm/ctlseqs/ctlseqs.html ]] +*/ +package ansi diff --git a/core/testing/reporting.odin b/core/testing/reporting.odin index 6752cd79b..7c7eb7b2d 100644 --- a/core/testing/reporting.odin +++ b/core/testing/reporting.odin @@ -10,12 +10,12 @@ package testing */ import "base:runtime" -import "core:encoding/ansi" import "core:fmt" import "core:io" import "core:mem" import "core:path/filepath" import "core:strings" +import "core:terminal/ansi" // Definitions of colors for use in the test runner. SGR_RESET :: ansi.CSI + ansi.RESET + ansi.SGR diff --git a/core/testing/runner.odin b/core/testing/runner.odin index db0587370..c81d07109 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -13,7 +13,6 @@ package testing import "base:intrinsics" import "base:runtime" import "core:bytes" -import "core:encoding/ansi" @require import "core:encoding/base64" @require import "core:encoding/json" import "core:fmt" @@ -25,6 +24,7 @@ import "core:os" import "core:slice" @require import "core:strings" import "core:sync/chan" +import "core:terminal/ansi" import "core:thread" import "core:time" diff --git a/core/testing/signal_handler_libc.odin b/core/testing/signal_handler_libc.odin index 281fbde40..d17a6d6dc 100644 --- a/core/testing/signal_handler_libc.odin +++ b/core/testing/signal_handler_libc.odin @@ -12,9 +12,9 @@ package testing import "base:intrinsics" import "core:c/libc" -import "core:encoding/ansi" -import "core:sync" import "core:os" +import "core:sync" +import "core:terminal/ansi" @(private="file") stop_runner_flag: libc.sig_atomic_t diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 97ecfee45..de037f6cd 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -58,7 +58,6 @@ import trace "core:debug/trace" import dynlib "core:dynlib" import net "core:net" -import ansi "core:encoding/ansi" import base32 "core:encoding/base32" import base64 "core:encoding/base64" import cbor "core:encoding/cbor" @@ -130,6 +129,7 @@ import sync "core:sync" import testing "core:testing" import terminal "core:terminal" +import ansi "core:terminal/ansi" import edit "core:text/edit" import i18n "core:text/i18n" @@ -203,7 +203,6 @@ _ :: pe _ :: trace _ :: dynlib _ :: net -_ :: ansi _ :: base32 _ :: base64 _ :: csv @@ -260,6 +259,7 @@ _ :: strings _ :: sync _ :: testing _ :: terminal +_ :: ansi _ :: scanner _ :: i18n _ :: match -- cgit v1.2.3 From cac18b4aba41df093aa4cc3ccdc53588fe879882 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Tue, 27 May 2025 14:27:22 -0400 Subject: Fix incorrect CSV reader settings for example, fix typo in docs --- core/encoding/csv/doc.odin | 2 -- core/encoding/csv/reader.odin | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'core/encoding') diff --git a/core/encoding/csv/doc.odin b/core/encoding/csv/doc.odin index bfeadafd6..7abe2be49 100644 --- a/core/encoding/csv/doc.odin +++ b/core/encoding/csv/doc.odin @@ -63,8 +63,6 @@ Example: read_csv_from_string :: proc(filename: string) { r: csv.Reader r.trim_leading_space = true - r.reuse_record = true // Without it you have to delete(record) - r.reuse_record_buffer = true // Without it you have to each of the fields within it defer csv.reader_destroy(&r) csv_data, ok := os.read_entire_file(filename) diff --git a/core/encoding/csv/reader.odin b/core/encoding/csv/reader.odin index 5348624d5..577ef219d 100644 --- a/core/encoding/csv/reader.odin +++ b/core/encoding/csv/reader.odin @@ -130,7 +130,7 @@ reader_destroy :: proc(r: ^Reader) { for record, row_idx in csv.iterator_next(&r) { ... } TIP: If you process the results within the loop and don't need to own the results, - you can set the Reader's `reuse_record` and `reuse_record_reuse_record_buffer` to true; + you can set the Reader's `reuse_record` and `reuse_record_buffer` to true; you won't need to delete the record or its fields. */ iterator_next :: proc(r: ^Reader) -> (record: []string, idx: int, err: Error, more: bool) { -- cgit v1.2.3 From 85224b21e69678923b5a514586d50730b8d823a3 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Wed, 4 Jun 2025 21:51:12 +0200 Subject: encoding/cbor: support the matrix type --- core/encoding/cbor/marshal.odin | 18 ++++++++++++++++++ core/encoding/cbor/unmarshal.odin | 13 +++++++++++++ tests/core/encoding/cbor/test_core_cbor.odin | 20 ++++++++++++++++++++ 3 files changed, 51 insertions(+) (limited to 'core/encoding') diff --git a/core/encoding/cbor/marshal.odin b/core/encoding/cbor/marshal.odin index aca71deb2..ec597c8a9 100644 --- a/core/encoding/cbor/marshal.odin +++ b/core/encoding/cbor/marshal.odin @@ -612,6 +612,24 @@ _marshal_into_encoder :: proc(e: Encoder, v: any, ti: ^runtime.Type_Info) -> (er case: panic("unknown bit_size size") } + case runtime.Type_Info_Matrix: + count := info.column_count * info.elem_stride + err_conv(_encode_u64(e, u64(count), .Array)) or_return + + if impl, ok := _tag_implementations_type[info.elem.id]; ok { + for i in 0..marshal(e, any{rawptr(data), info.elem.id}) or_return + } + return + } + + elem_ti := runtime.type_info_core(type_info_of(info.elem.id)) + for i in 0.. count { + return _unsupported(v, hdr) + } + + da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator } + + out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return + if out_of_space { return _unsupported(v, hdr) } + return + case: return _unsupported(v, hdr) } } diff --git a/tests/core/encoding/cbor/test_core_cbor.odin b/tests/core/encoding/cbor/test_core_cbor.odin index ee853ebac..a76d690be 100644 --- a/tests/core/encoding/cbor/test_core_cbor.odin +++ b/tests/core/encoding/cbor/test_core_cbor.odin @@ -43,6 +43,7 @@ Foo :: struct { biggest: big.Int, smallest: big.Int, ignore_this: ^Foo `cbor:"-"`, + mat: matrix[4, 4]f32, } FooBar :: enum { @@ -95,6 +96,7 @@ test_marshalling :: proc(t: ^testing.T) { onetwenty = i128(12345), small_onetwenty = -i128(max(u64)), ignore_this = &Foo{}, + mat = 1, } big.atoi(&f.biggest, "1234567891011121314151617181920") @@ -120,6 +122,24 @@ test_marshalling :: proc(t: ^testing.T) { defer delete(diagnosis) testing.expect_value(t, diagnosis, `{ "no": null, + "mat": [ + 1.0000, + 0.0000, + 0.0000, + 0.0000, + 0.0000, + 1.0000, + 0.0000, + 0.0000, + 0.0000, + 0.0000, + 1.0000, + 0.0000, + 0.0000, + 0.0000, + 0.0000, + 1.0000 + ], "neg": -69, "nos": undefined, "now": 1(1701117968), -- cgit v1.2.3 From 9eefa2006eccdf942c01fc1421784edea78c5591 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Wed, 4 Jun 2025 22:00:02 +0200 Subject: encoding/cbor: support simd vectors --- core/encoding/cbor/marshal.odin | 18 ++++++++++++++++++ core/encoding/cbor/unmarshal.odin | 12 ++++++++++++ tests/core/encoding/cbor/test_core_cbor.odin | 8 ++++++++ 3 files changed, 38 insertions(+) (limited to 'core/encoding') diff --git a/core/encoding/cbor/marshal.odin b/core/encoding/cbor/marshal.odin index ec597c8a9..b23087c90 100644 --- a/core/encoding/cbor/marshal.odin +++ b/core/encoding/cbor/marshal.odin @@ -630,6 +630,24 @@ _marshal_into_encoder :: proc(e: Encoder, v: any, ti: ^runtime.Type_Info) -> (er _marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return } return + + case runtime.Type_Info_Simd_Vector: + err_conv(_encode_u64(e, u64(info.count), .Array)) or_return + + if impl, ok := _tag_implementations_type[info.elem.id]; ok { + for i in 0..marshal(e, any{rawptr(data), info.elem.id}) or_return + } + return + } + + elem_ti := runtime.type_info_core(type_info_of(info.elem.id)) + for i in 0.. t.count { + return _unsupported(v, hdr) + } + + da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator } + + out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return + if out_of_space { return _unsupported(v, hdr) } + return + case: return _unsupported(v, hdr) } } diff --git a/tests/core/encoding/cbor/test_core_cbor.odin b/tests/core/encoding/cbor/test_core_cbor.odin index a76d690be..7bea69d2e 100644 --- a/tests/core/encoding/cbor/test_core_cbor.odin +++ b/tests/core/encoding/cbor/test_core_cbor.odin @@ -44,6 +44,7 @@ Foo :: struct { smallest: big.Int, ignore_this: ^Foo `cbor:"-"`, mat: matrix[4, 4]f32, + vec: #simd [4]f64, } FooBar :: enum { @@ -97,6 +98,7 @@ test_marshalling :: proc(t: ^testing.T) { small_onetwenty = -i128(max(u64)), ignore_this = &Foo{}, mat = 1, + vec = 2, } big.atoi(&f.biggest, "1234567891011121314151617181920") @@ -145,6 +147,12 @@ test_marshalling :: proc(t: ^testing.T) { "now": 1(1701117968), "pos": 1212, "str": "Hellope", + "vec": [ + 2.0000, + 2.0000, + 2.0000, + 2.0000 + ], "yes": true, "comp": [ 32.0000, -- cgit v1.2.3 From b7de15caa342fd81fb203015e2fd22a82e523342 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:29:41 -0400 Subject: Clarify `strconv.append_*` to `strconv.write_*` --- core/encoding/cbor/cbor.odin | 6 +-- core/encoding/json/marshal.odin | 8 ++-- core/fmt/fmt.odin | 8 ++-- core/io/util.odin | 16 +++---- core/math/fixed/fixed.odin | 6 +-- core/net/url.odin | 2 +- core/os/os.odin | 2 +- core/os/os2/file_util.odin | 2 +- core/strconv/integers.odin | 14 +++---- core/strconv/strconv.odin | 84 ++++++++++++++++++------------------- core/strings/builder.odin | 12 +++--- tests/core/math/test_core_math.odin | 2 +- 12 files changed, 81 insertions(+), 81 deletions(-) (limited to 'core/encoding') diff --git a/core/encoding/cbor/cbor.odin b/core/encoding/cbor/cbor.odin index 8eb829ed3..1fb7c34ab 100644 --- a/core/encoding/cbor/cbor.odin +++ b/core/encoding/cbor/cbor.odin @@ -385,17 +385,17 @@ to_diagnostic_format_writer :: proc(w: io.Writer, val: Value, padding := 0) -> i // which we want for the diagnostic format. case f16: buf: [64]byte - str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f16), 8*size_of(f16)) + str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f16), 8*size_of(f16)) if str[0] == '+' && str != "+Inf" { str = str[1:] } io.write_string(w, str) or_return case f32: buf: [128]byte - str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f32), 8*size_of(f32)) + str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f32), 8*size_of(f32)) if str[0] == '+' && str != "+Inf" { str = str[1:] } io.write_string(w, str) or_return case f64: buf: [256]byte - str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f64), 8*size_of(f64)) + str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f64), 8*size_of(f64)) if str[0] == '+' && str != "+Inf" { str = str[1:] } io.write_string(w, str) or_return diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 020facd14..ebb9a639c 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -108,13 +108,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) { switch i in a { case u8, u16, u32, u64, u128: - s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix }) + s = strconv.write_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix }) case: - s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) + s = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) } } else { - s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) + s = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) } io.write_string(w, s) or_return @@ -286,7 +286,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: case runtime.Type_Info_Integer: buf: [40]byte u := cast_any_int_to_u128(ka) - name = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*kti.size, "0123456789", nil) + name = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*kti.size, "0123456789", nil) opt_write_key(w, opt, name) or_return case: return .Unsupported_Type diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 9c07847dd..0f6470cca 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1122,7 +1122,7 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d flags: strconv.Int_Flags if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} } if fi.plus { flags += {.Plus} } - s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags) + s := strconv.write_bits(buf[start:], u, base, is_signed, bit_size, digits, flags) prev_zero := fi.zero defer fi.zero = prev_zero fi.zero = false @@ -1207,7 +1207,7 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i flags: strconv.Int_Flags if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} } if fi.plus { flags += {.Plus} } - s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags) + s := strconv.write_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags) if fi.hash && fi.zero && fi.indent == 0 { c: byte = 0 @@ -1272,7 +1272,7 @@ _fmt_memory :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, units: st } buf: [256]byte - str := strconv.append_float(buf[:], amt, 'f', prec, 64) + str := strconv.write_float(buf[:], amt, 'f', prec, 64) // Add the unit at the end. copy(buf[len(str):], units[off:off+unit_len]) @@ -1424,7 +1424,7 @@ _fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: b buf: [386]byte // Can return "NaN", "+Inf", "-Inf", "+", "-". - str := strconv.append_float(buf[:], v, float_fmt, prec, bit_size) + str := strconv.write_float(buf[:], v, float_fmt, prec, bit_size) if !fi.plus { // Strip sign from "+" but not "+Inf". diff --git a/core/io/util.odin b/core/io/util.odin index fdbbd5b9f..fa98e007b 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -22,12 +22,12 @@ write_ptr_at :: proc(w: Writer_At, p: rawptr, byte_size: int, offset: i64, n_wri write_u64 :: proc(w: Writer, i: u64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { buf: [32]byte - s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil) + s := strconv.write_bits(buf[:], i, base, false, 64, strconv.digits, nil) return write_string(w, s, n_written) } write_i64 :: proc(w: Writer, i: i64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { buf: [32]byte - s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) + s := strconv.write_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) return write_string(w, s, n_written) } @@ -40,18 +40,18 @@ write_int :: proc(w: Writer, i: int, base: int = 10, n_written: ^int = nil) -> ( write_u128 :: proc(w: Writer, i: u128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { buf: [39]byte - s := strconv.append_bits_128(buf[:], i, base, false, 128, strconv.digits, nil) + s := strconv.write_bits_128(buf[:], i, base, false, 128, strconv.digits, nil) return write_string(w, s, n_written) } write_i128 :: proc(w: Writer, i: i128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { buf: [40]byte - s := strconv.append_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil) + s := strconv.write_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil) return write_string(w, s, n_written) } write_f16 :: proc(w: Writer, val: f16, n_written: ^int = nil) -> (n: int, err: Error) { buf: [386]byte - str := strconv.append_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val)) + str := strconv.write_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val)) s := buf[:len(str)+1] if s[1] == '+' || s[1] == '-' { s = s[1:] @@ -67,7 +67,7 @@ write_f16 :: proc(w: Writer, val: f16, n_written: ^int = nil) -> (n: int, err: E write_f32 :: proc(w: Writer, val: f32, n_written: ^int = nil) -> (n: int, err: Error) { buf: [386]byte - str := strconv.append_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val)) + str := strconv.write_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val)) s := buf[:len(str)+1] if s[1] == '+' || s[1] == '-' { s = s[1:] @@ -83,7 +83,7 @@ write_f32 :: proc(w: Writer, val: f32, n_written: ^int = nil) -> (n: int, err: E write_f64 :: proc(w: Writer, val: f64, n_written: ^int = nil) -> (n: int, err: Error) { buf: [386]byte - str := strconv.append_float(buf[1:], val, 'f', 2*size_of(val), 8*size_of(val)) + str := strconv.write_float(buf[1:], val, 'f', 2*size_of(val), 8*size_of(val)) s := buf[:len(str)+1] if s[1] == '+' || s[1] == '-' { s = s[1:] @@ -130,7 +130,7 @@ write_encoded_rune :: proc(w: Writer, r: rune, write_quote := true, n_written: ^ write_string(w, `\x`, &n) or_return buf: [2]byte - s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil) + s := strconv.write_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil) switch len(s) { case 0: write_string(w, "00", &n) or_return diff --git a/core/math/fixed/fixed.odin b/core/math/fixed/fixed.odin index b23090307..119e727a7 100644 --- a/core/math/fixed/fixed.odin +++ b/core/math/fixed/fixed.odin @@ -124,16 +124,16 @@ append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string { when size_of(Backing) < 16 { T :: u64 - append_uint :: strconv.append_uint + write_uint :: strconv.write_uint } else { T :: u128 - append_uint :: strconv.append_u128 + write_uint :: strconv.write_u128 } integer := T(x.i) >> Fraction_Width fraction := T(x.i) & (1< string { bytes, n := utf8.encode_rune(ch) for byte in bytes[:n] { buf: [2]u8 = --- - t := strconv.append_int(buf[:], i64(byte), 16) + t := strconv.write_int(buf[:], i64(byte), 16) strings.write_rune(&b, '%') strings.write_string(&b, t) } diff --git a/core/os/os.odin b/core/os/os.odin index 30b86d4cd..fde48fbf4 100644 --- a/core/os/os.odin +++ b/core/os/os.odin @@ -57,7 +57,7 @@ write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) { if r < 32 { if wrap(write_string(f, "\\x"), &n, &err) { return } b: [2]byte - s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) + s := strconv.write_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) switch len(s) { case 0: if wrap(write_string(f, "00"), &n, &err) { return } case 1: if wrap(write_rune(f, '0'), &n, &err) { return } diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index 8af46fab3..407c38f88 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -59,7 +59,7 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) { if r < 32 { if wrap(write_string(f, "\\x"), &n, &err) { return } b: [2]byte - s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) + s := strconv.write_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) switch len(s) { case 0: if wrap(write_string(f, "00"), &n, &err) { return } case 1: if wrap(write_rune(f, '0'), &n, &err) { return } diff --git a/core/strconv/integers.odin b/core/strconv/integers.odin index 98a432ac5..0db110d10 100644 --- a/core/strconv/integers.odin +++ b/core/strconv/integers.odin @@ -48,7 +48,7 @@ is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64, return } /* -Appends the string representation of an integer to a buffer with specified base, flags, and digit set. +Writes the string representation of an integer to a buffer with specified base, flags, and digit set. **Inputs** - buf: The buffer to append the integer representation to @@ -62,9 +62,9 @@ Appends the string representation of an integer to a buffer with specified base, **Returns** - The string containing the integer representation appended to the buffer */ -append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { +write_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { if base < 2 || base > MAX_BASE { - panic("strconv: illegal base passed to append_bits") + panic("strconv: illegal base passed to write_bits") } a: [129]byte @@ -146,7 +146,7 @@ is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u: return } /* -Appends the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set. +Writes the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set. **Inputs** - buf: The buffer to append the integer representation to @@ -158,11 +158,11 @@ Appends the string representation of a 128-bit integer to a buffer with specifie - flags: The Int_Flags bit set to control integer formatting **Returns** -- The string containing the integer representation appended to the buffer +- The string containing the integer representation written to the buffer */ -append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { +write_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { if base < 2 || base > MAX_BASE { - panic("strconv: illegal base passed to append_bits") + panic("strconv: illegal base passed to write_bits") } a: [140]byte diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 4cecd1911..99a290d83 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -1451,19 +1451,19 @@ parse_quaternion64 :: proc(str: string, n: ^int = nil) -> (value: quaternion64, return cast(quaternion64)v, ok } /* -Appends a boolean value as a string to the given buffer +Writes a boolean value as a string to the given buffer **Inputs** -- buf: The buffer to append the boolean value to -- b: The boolean value to be appended +- buf: The buffer to write the boolean value to +- b: The boolean value to be written Example: import "core:fmt" import "core:strconv" - append_bool_example :: proc() { + write_bool_example :: proc() { buf: [6]byte - result := strconv.append_bool(buf[:], true) + result := strconv.write_bool(buf[:], true) fmt.println(result, buf) } @@ -1472,9 +1472,9 @@ Output: true [116, 114, 117, 101, 0, 0] **Returns** -- The resulting string after appending the boolean value +- The resulting string after writing the boolean value */ -append_bool :: proc(buf: []byte, b: bool) -> string { +write_bool :: proc(buf: []byte, b: bool) -> string { n := 0 if b { n = copy(buf, "true") @@ -1484,20 +1484,20 @@ append_bool :: proc(buf: []byte, b: bool) -> string { return string(buf[:n]) } /* -Appends an unsigned integer value as a string to the given buffer with the specified base +Writes an unsigned integer value as a string to the given buffer with the specified base **Inputs** -- buf: The buffer to append the unsigned integer value to -- u: The unsigned integer value to be appended +- buf: The buffer to write the unsigned integer value to +- u: The unsigned integer value to be written - base: The base to use for converting the integer value Example: import "core:fmt" import "core:strconv" - append_uint_example :: proc() { + write_uint_example :: proc() { buf: [4]byte - result := strconv.append_uint(buf[:], 42, 16) + result := strconv.write_uint(buf[:], 42, 16) fmt.println(result, buf) } @@ -1506,26 +1506,26 @@ Output: 2a [50, 97, 0, 0] **Returns** -- The resulting string after appending the unsigned integer value +- The resulting string after writing the unsigned integer value */ -append_uint :: proc(buf: []byte, u: u64, base: int) -> string { - return append_bits(buf, u, base, false, 8*size_of(uint), digits, nil) +write_uint :: proc(buf: []byte, u: u64, base: int) -> string { + return write_bits(buf, u, base, false, 8*size_of(uint), digits, nil) } /* -Appends a signed integer value as a string to the given buffer with the specified base +Writes a signed integer value as a string to the given buffer with the specified base **Inputs** -- buf: The buffer to append the signed integer value to -- i: The signed integer value to be appended +- buf: The buffer to write the signed integer value to +- i: The signed integer value to be written - base: The base to use for converting the integer value Example: import "core:fmt" import "core:strconv" - append_int_example :: proc() { + write_int_example :: proc() { buf: [4]byte - result := strconv.append_int(buf[:], -42, 10) + result := strconv.write_int(buf[:], -42, 10) fmt.println(result, buf) } @@ -1534,16 +1534,16 @@ Output: -42 [45, 52, 50, 0] **Returns** -- The resulting string after appending the signed integer value +- The resulting string after writing the signed integer value */ -append_int :: proc(buf: []byte, i: i64, base: int) -> string { - return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil) +write_int :: proc(buf: []byte, i: i64, base: int) -> string { + return write_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil) } -append_u128 :: proc(buf: []byte, u: u128, base: int) -> string { - return append_bits_128(buf, u, base, false, 8*size_of(uint), digits, nil) +write_u128 :: proc(buf: []byte, u: u128, base: int) -> string { + return write_bits_128(buf, u, base, false, 8*size_of(uint), digits, nil) } /* @@ -1571,7 +1571,7 @@ Output: - The resulting string after converting the integer value */ itoa :: proc(buf: []byte, i: int) -> string { - return append_int(buf, i64(i), 10) + return write_int(buf, i64(i), 10) } /* Converts a string to an integer value @@ -1623,14 +1623,14 @@ atof :: proc(s: string) -> f64 { v, _ := parse_f64(s) return v } -// Alias to `append_float` -ftoa :: append_float +// Alias to `write_float` +ftoa :: write_float /* -Appends a float64 value as a string to the given buffer with the specified format and precision +Writes a float64 value as a string to the given buffer with the specified format and precision **Inputs** -- buf: The buffer to append the float64 value to -- f: The float64 value to be appended +- buf: The buffer to write the float64 value to +- f: The float64 value to be written - fmt: The byte specifying the format to use for the conversion - prec: The precision to use for the conversion - bit_size: The size of the float in bits (32 or 64) @@ -1639,9 +1639,9 @@ Example: import "core:fmt" import "core:strconv" - append_float_example :: proc() { + write_float_example :: proc() { buf: [8]byte - result := strconv.append_float(buf[:], 3.14159, 'f', 2, 64) + result := strconv.write_float(buf[:], 3.14159, 'f', 2, 64) fmt.println(result, buf) } @@ -1650,16 +1650,16 @@ Output: +3.14 [43, 51, 46, 49, 52, 0, 0, 0] **Returns** -- The resulting string after appending the float +- The resulting string after writing the float */ -append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { +write_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { return string(generic_ftoa(buf, f, fmt, prec, bit_size)) } /* -Appends a quoted string representation of the input string to a given byte slice and returns the result as a string +Writes a quoted string representation of the input string to a given byte slice and returns the result as a string **Inputs** -- buf: The byte slice to which the quoted string will be appended +- buf: The byte slice to which the quoted string will be written - str: The input string to be quoted !! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected @@ -1679,7 +1679,7 @@ Output: "'h''e''l''l''o'" [34, 39, 104, 39, 39, 101, 39, 39, 108, 39, 39, 108, 39, 39, 111, 39, 34, 0, 0, 0] **Returns** -- The resulting string after appending the quoted string representation +- The resulting string after writing the quoted string representation */ quote :: proc(buf: []byte, str: string) -> string { write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) { @@ -1719,10 +1719,10 @@ quote :: proc(buf: []byte, str: string) -> string { return string(buf[:i]) } /* -Appends a quoted rune representation of the input rune to a given byte slice and returns the result as a string +Writes a quoted rune representation of the input rune to a given byte slice and returns the result as a string **Inputs** -- buf: The byte slice to which the quoted rune will be appended +- buf: The byte slice to which the quoted rune will be written - r: The input rune to be quoted Example: @@ -1740,7 +1740,7 @@ Output: 'A' [39, 65, 39, 0] **Returns** -- The resulting string after appending the quoted rune representation +- The resulting string after writing the quoted rune representation */ quote_rune :: proc(buf: []byte, r: rune) -> string { write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) { @@ -1783,7 +1783,7 @@ quote_rune :: proc(buf: []byte, r: rune) -> string { if r < 32 { write_string(buf, &i, "\\x") b: [2]byte - s := append_bits(b[:], u64(r), 16, true, 64, digits, nil) + s := write_bits(b[:], u64(r), 16, true, 64, digits, nil) switch len(s) { case 0: write_string(buf, &i, "00") case 1: write_rune(buf, &i, '0') diff --git a/core/strings/builder.odin b/core/strings/builder.odin index 05382f04e..b1180d5e9 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -675,7 +675,7 @@ Returns: */ write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) { buf: [384]byte - s := strconv.append_float(buf[:], f, fmt, prec, bit_size) + s := strconv.write_float(buf[:], f, fmt, prec, bit_size) // If the result starts with a `+` then unless we always want signed results, // we skip it unless it's followed by an `I` (because of +Inf). if !always_signed && (buf[0] == '+' && buf[1] != 'I') { @@ -699,7 +699,7 @@ Returns: */ write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte - s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) + s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) if !always_signed && (buf[0] == '+' && buf[1] != 'I') { s = s[1:] } @@ -739,7 +739,7 @@ Output: */ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte - s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) + s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) if !always_signed && (buf[0] == '+' && buf[1] != 'I') { s = s[1:] } @@ -761,7 +761,7 @@ Returns: */ write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte - s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) + s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) if !always_signed && (buf[0] == '+' && buf[1] != 'I') { s = s[1:] } @@ -782,7 +782,7 @@ Returns: */ write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) { buf: [32]byte - s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil) + s := strconv.write_bits(buf[:], i, base, false, 64, strconv.digits, nil) return write_string(b, s) } /* @@ -800,7 +800,7 @@ Returns: */ write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) { buf: [32]byte - s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) + s := strconv.write_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) return write_string(b, s) } /* diff --git a/tests/core/math/test_core_math.odin b/tests/core/math/test_core_math.odin index 5797cb4ea..009e3fedd 100644 --- a/tests/core/math/test_core_math.odin +++ b/tests/core/math/test_core_math.odin @@ -1238,7 +1238,7 @@ test_count_digits :: proc(t: ^testing.T) { buf: [64]u8 for n in 0.. Date: Mon, 9 Jun 2025 15:09:04 +0200 Subject: Fix #4705 Allocate `doc.tokenizer`, and free it in `destroy`. --- core/encoding/xml/xml_reader.odin | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'core/encoding') diff --git a/core/encoding/xml/xml_reader.odin b/core/encoding/xml/xml_reader.odin index d616be9dc..707d2b3f3 100644 --- a/core/encoding/xml/xml_reader.odin +++ b/core/encoding/xml/xml_reader.odin @@ -175,7 +175,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha data = bytes.clone(data) } - t := &Tokenizer{} + t := new(Tokenizer) init(t, string(data), path, error_handler) doc = new(Document) @@ -403,6 +403,7 @@ destroy :: proc(doc: ^Document) { } delete(doc.strings_to_free) + free(doc.tokenizer) free(doc) } -- cgit v1.2.3