From: Brendan Hansen Date: Mon, 4 Oct 2021 16:26:57 +0000 (-0500) Subject: better maps and json; fixed anonymous struct bug X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=35273cbca793686fd0c98719529125f0d9a627b8;p=onyx.git better maps and json; fixed anonymous struct bug --- diff --git a/bin/onyx b/bin/onyx index 3db06439..f789cd6c 100755 Binary files a/bin/onyx and b/bin/onyx differ diff --git a/core/container/map.onyx b/core/container/map.onyx index 30941b57..a504b114 100644 --- a/core/container/map.onyx +++ b/core/container/map.onyx @@ -1,11 +1,18 @@ package core.map -#private_file array :: package core.array -#private_file hash :: package core.hash -use package core.intrinsics.onyx { __zero_value } +#private_file { + array :: package core.array + hash :: package core.hash + memory :: package core.memory + math :: package core.math + + use package core.intrinsics.onyx { __zero_value, __initialize } +} Map :: struct (K: type_expr, V: type_expr) { - hashes : [..] i32; + allocator : Allocator; + + hashes : [] i32; entries : [..] Entry(K, V); // The value provided by `map.get`, if nothing was found. @@ -18,23 +25,26 @@ Map :: struct (K: type_expr, V: type_expr) { } } -make :: ($Key: type_expr, $Value: type_expr, default := __zero_value(Value), hash_count: i32 = 16) -> Map(Key, Value) { +make :: ($Key: type_expr, $Value: type_expr, default := __zero_value(Value)) -> Map(Key, Value) { map : Map(Key, Value); - init(^map, default = default, hash_count = hash_count); + init(^map, default = default); return map; } -init :: (use map: ^Map($K, $V), default := __zero_value(V), hash_count: i32 = 16) { - array.init(^hashes, hash_count); - array.init(^entries, 4); +init :: (use map: ^Map($K, $V), default := __zero_value(V)) { + __initialize(map); + allocator = context.allocator; default_value = default; - for i: 0 .. hash_count do array.push(^hashes, -1); + memory.alloc_slice(^hashes, 8, allocator=allocator); + memory.fill_slice(hashes, -1); + + array.init(^entries, allocator=allocator); } free :: (use map: ^Map($K, $V)) { - array.free(^hashes); + memory.free_slice(^hashes, allocator=allocator); array.free(^entries); } @@ -46,13 +56,10 @@ put :: (use map: ^Map($K, $V), key: K, value: V) { return; } - array.push(^entries, .{ - key = key, - value = value, - hashes[lr.hash_index], - }); - + entries << .{ hashes[lr.hash_index], key, value }; hashes[lr.hash_index] = entries.count - 1; + + if full(map) do grow(map); } has :: (use map: ^Map($K, $V), key: K) -> bool { @@ -83,7 +90,7 @@ delete :: (use map: ^Map($K, $V), key: K) { if lr.entry_prev < 0 do hashes[lr.hash_index] = entries[lr.entry_index].next; else do entries[lr.entry_prev].next = entries[lr.entry_index].next; - if lr.entry_index == entries.count - 1 { + if lr.entry_index == entries.count - 1{ array.pop(^entries); return; } @@ -118,28 +125,62 @@ empty :: (use map: ^Map($K, $V)) -> bool { // Private symbols // -#private_file -MapLookupResult :: struct { - hash_index : i32 = -1; - entry_index : i32 = -1; - entry_prev : i32 = -1; -} +#private_file { + MapLookupResult :: struct { + hash_index : i32 = -1; + entry_index : i32 = -1; + entry_prev : i32 = -1; + } + + lookup :: (use map: ^Map($K, $V), key: K) -> MapLookupResult { + lr := MapLookupResult.{}; + + hash_value: u32 = hash.to_u32(key); // You cannot use this type for the key, unless you add an overload. -#private_file -lookup :: (use map: ^Map($K, $V), key: K) -> MapLookupResult { - lr := MapLookupResult.{}; + lr.hash_index = hash_value % hashes.count; + lr.entry_index = hashes[lr.hash_index]; - hash_value: u32 = hash.to_u32(key); // You cannot use this type for the key, unless you add an overload. + while lr.entry_index >= 0 { + if entries[lr.entry_index].key == key do return lr; - lr.hash_index = hash_value % hashes.count; - lr.entry_index = hashes[lr.hash_index]; + lr.entry_prev = lr.entry_index; + lr.entry_index = entries[lr.entry_index].next; + } - while lr.entry_index >= 0 { - if entries[lr.entry_index].key == key do return lr; + return lr; + } + + add_entry :: (use map: ^Map($K, $V), key: K) -> i32 { + entry: Map.Entry(K, V); + entry.key = key; + entry.next = -1; + + entries << entry; + + return entries.count - 1; + } - lr.entry_prev = lr.entry_index; - lr.entry_index = entries[lr.entry_index].next; + full :: (use map: ^Map($K, $V)) => entries.count >= ~~(0.75f * ~~hashes.count); + + grow :: (use map: ^Map($K, $V)) { + new_size := math.max(hashes.count << 1, 8); + rehash(map, new_size); } - return lr; + rehash :: (use map: ^Map($K, $V), new_size: i32) { + memory.free_slice(^hashes, allocator); + memory.alloc_slice(^hashes, new_size, allocator); + memory.fill_slice(hashes, -1); + + for ^entry: entries do entry.next = -1; + + index := 0; + for ^entry: entries { + defer index += 1; + + lr := lookup(map, entry.key); + entries[index].next = hashes[lr.hash_index]; + hashes[lr.hash_index] = index; + } + } } diff --git a/core/conv.onyx b/core/conv.onyx index d648f2a8..76eaf087 100644 --- a/core/conv.onyx +++ b/core/conv.onyx @@ -477,8 +477,12 @@ str_format_va :: (buffer: [] u8, format: str, va: [] any) -> str { if info.kind == .Struct { s := cast(^Type_Info_Struct) info; - if s.name.count > 0 do output->write(s.name); - output->write(" { "); + if s.name.count > 0 { + output->write(s.name); + output->write(" { "); + } else { + output->write("{ "); + } { format := formatting; @@ -534,7 +538,11 @@ str_format_va :: (buffer: [] u8, format: str, va: [] any) -> str { // This assumes that the following type_info kinds are basically the same. if info.kind == .Dynamic_Array || info.kind == .Slice || info.kind == .Variadic_Argument { - output->write("[ "); + if formatting.pretty_printing { + output->write("["); + } else { + output->write("[ "); + } a := cast(^Type_Info_Dynamic_Array) info; arr := cast(^array.Untyped_Array) v.data; @@ -543,7 +551,7 @@ str_format_va :: (buffer: [] u8, format: str, va: [] any) -> str { format := formatting; format.quote_strings = true; - if format.pretty_printing do format.indentation += 2; + if format.pretty_printing do format.indentation += 4; for i: count { if i != 0 do output->write(", "); @@ -558,7 +566,7 @@ str_format_va :: (buffer: [] u8, format: str, va: [] any) -> str { if formatting.pretty_printing { - format.indentation -= 2; + format.indentation -= 4; output->write("\n"); for _: format.indentation do output->write(#char " "); output->write(#char "]"); diff --git a/core/intrinsics/onyx.onyx b/core/intrinsics/onyx.onyx index 6df73adf..cf752a90 100644 --- a/core/intrinsics/onyx.onyx +++ b/core/intrinsics/onyx.onyx @@ -2,3 +2,9 @@ package core.intrinsics.onyx __initialize :: (val: ^$T) -> void #intrinsic --- __zero_value :: ($T: type_expr) -> T #intrinsic --- + +init :: macro ($T: type_expr) -> T { + val: T; + __initialize(^val); + return val; +} diff --git a/core/memory.onyx b/core/memory.onyx index 17a99ea9..7fcbc969 100644 --- a/core/memory.onyx +++ b/core/memory.onyx @@ -59,6 +59,20 @@ copy_slice :: (sl: [] $T, allocator := context.allocator) -> [] T { return .{ data = data, count = sl.count }; } +resize_slice :: (sl: [] $T, new_size: i32, allocator := context.allocator) -> [] T { + new_slice: [] T; + new_slice.data = raw_alloc(allocator, sizeof T * new_size); + new_slice.count = new_size; + + copy(new_slice.data, sl.data, sl.count * sizeof T); + + return new_slice; +} + +fill_slice :: macro (sl: [] $T, v: T) { + for ^it: sl do *it = v; +} + align :: #match { (size: ^u64, align: u64) { if *size % align != 0 { diff --git a/examples/11_map.onyx b/examples/11_map.onyx index 5cab4dac..80b25e69 100644 --- a/examples/11_map.onyx +++ b/examples/11_map.onyx @@ -9,7 +9,7 @@ main :: (args: [] cstr) { // To use it, simply create one like so: ages : Map(str, u32); - map.init(^ages, default=0, hash_count=16); + map.init(^ages, default=0); defer map.free(^ages); // Alternatively you can use the map.make function to achieve the @@ -19,9 +19,7 @@ main :: (args: [] cstr) { // The defer statement ensures that the map memory will be freed // when this procedure exits, no matter through which way. The // 'default' argument is used to specify what value should be - // returned when the map does not contain the requested key. The - // hash_count is an implementation detail that will go away in the - // near future. It is the maximum number of chains in the hash table. + // returned when the map does not contain the requested key. // To put an entry into the map, use the map.put procedure. map.put(^ages, "Dwight", 32); diff --git a/modules/json/types.onyx b/modules/json/types.onyx index 74546d82..2f6567ef 100644 --- a/modules/json/types.onyx +++ b/modules/json/types.onyx @@ -93,6 +93,9 @@ Value_Float :: struct { Value_String :: struct { use base := Value.{ type = .String }; str_: str; + + // Set if the string should not be freed from the allocator. + static := false; } Value_Array :: struct { @@ -104,6 +107,8 @@ Value_Object :: struct { use base := Value.{ type = .Object }; object_: [..] struct { key : str; + key_is_static := false; + value : ^Value; }; } @@ -133,11 +138,35 @@ get_idx :: (v: ^Value, idx: i32) -> ^Value { return v_arr.array_[idx]; } +set :: #match { + macro (v: ^Value, key: str, value: ^Value, key_is_static := false) { + if v.type == .Object { + Value_Object_ :: Value_Object ; + + (cast(^Value_Object_) v).object_ << .{ key, key_is_static, value }; + } + }, + + // Quick thing for allocating json values on the stack. + macro (v: ^Value, key: str, value: str, key_is_static := false, value_is_static := false) { + Value_String_ :: Value_String ; + Value_Object_ :: Value_Object ; + + if v.type == .Object { + json_value := init(Value_String_); + json_value.str_ = value; + json_value.static = value_is_static; + + (cast(^Value_Object_) v).object_ << .{ key, key_is_static, ^json_value }; + } + } +} + free :: #match { (v: ^Value, allocator: Allocator) do switch v.type { case .String { v_str := cast(^Value_String) v; - raw_free(allocator, v_str.str_.data); + if !v_str.static do raw_free(allocator, v_str.str_.data); } case .Array { @@ -151,7 +180,7 @@ free :: #match { case .Object { v_obj := cast(^Value_Object) v; for ^entry: v_obj.object_ { - raw_free(allocator, entry.key.data); + if !entry.key_is_static do raw_free(allocator, entry.key.data); free(entry.value, allocator); } array.free(^v_obj.object_); @@ -162,3 +191,7 @@ free :: #match { free(root, allocator); }, } + +static_string :: (s: str) -> Value_String { + return .{ str_ = s, static = true }; +} diff --git a/modules/ui/ui.onyx b/modules/ui/ui.onyx index 5cfe8f7f..d9153969 100644 --- a/modules/ui/ui.onyx +++ b/modules/ui/ui.onyx @@ -16,11 +16,11 @@ UI_Id :: #type u32 } init_ui :: () { - map.init(^font_registry, hash_count=4); + map.init(^font_registry); - map.init(^animation_states, default=.{}, hash_count=4); - map.init(^workspace_states, default=.{}, hash_count=4); - map.init(^scrollable_region_states, default=.{}, hash_count=4); + map.init(^animation_states, default=.{}); + map.init(^workspace_states, default=.{}); + map.init(^scrollable_region_states, default=.{}); // Register a default font so things don't break everywhere builtin_font := create_font(#file_contents "./fonts/builtin.fnt", #file_contents "./fonts/builtin.data"); diff --git a/src/astnodes.c b/src/astnodes.c index 972b9a8c..3ab5bac2 100644 --- a/src/astnodes.c +++ b/src/astnodes.c @@ -567,7 +567,7 @@ b32 unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) { if (func == NULL) return 0; // FIXME: This is incorrect. It should actually yield and not return a failure. - if (func == &node_that_signals_a_yield) return 0; + if (func == (AstFunction *) &node_that_signals_a_yield) return 0; *pnode = (AstTyped *) func; node = *pnode; diff --git a/src/checker.c b/src/checker.c index 2681a434..fd282119 100644 --- a/src/checker.c +++ b/src/checker.c @@ -1138,6 +1138,7 @@ CheckStatus check_array_literal(AstArrayLiteral* al) { } al->flags |= Ast_Flag_Comptime; + assert(al->type->kind == Type_Kind_Array); Type* elem_type = al->type->Array.elem; bh_arr_each(AstTyped *, expr, al->values) { diff --git a/src/parser.c b/src/parser.c index 71cac81e..cc4891ed 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1964,6 +1964,7 @@ static AstStructType* parse_struct(OnyxParser* parser) { return (AstStructType *) poly_struct; } else { + ENTITY_SUBMIT(s_node); return s_node; } } @@ -2597,7 +2598,7 @@ static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol } // HACK: This should maybe be entered elsewhere? - ENTITY_SUBMIT(node); + if (node->kind != Ast_Kind_Struct_Type) ENTITY_SUBMIT(node); } AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding); diff --git a/tests/aoc-2020/day14.onyx b/tests/aoc-2020/day14.onyx index 78a16dc7..97c8397c 100644 --- a/tests/aoc-2020/day14.onyx +++ b/tests/aoc-2020/day14.onyx @@ -78,7 +78,7 @@ main :: (args: [] cstr) { file := reader.make(contents); - mem := map.make(u64, u64, default=0, hash_count=257); + mem := map.make(u64, u64, default=0); defer map.free(^mem); mask : Bitmask; diff --git a/tests/aoc-2020/day15.onyx b/tests/aoc-2020/day15.onyx index c5417d9b..50d2aa61 100644 --- a/tests/aoc-2020/day15.onyx +++ b/tests/aoc-2020/day15.onyx @@ -14,7 +14,7 @@ main :: (args: [] cstr) { // The current implementation of Map is rather slow at a large scale. // Any changes to the implementation of Map should be tested on this // file to validate if they 1) work and 2) are faster. - nums := map.make(u32, spoken_times, .{}, 32767); + nums := map.make(u32, spoken_times, .{}); defer map.free(^nums); turn := 1; diff --git a/tests/aoc-2020/day17.onyx b/tests/aoc-2020/day17.onyx index 19c29ab9..fa793c63 100644 --- a/tests/aoc-2020/day17.onyx +++ b/tests/aoc-2020/day17.onyx @@ -41,7 +41,7 @@ main :: (args: [] cstr) { file := reader.make(contents); - cubes := map.make(CubePos, CubeState, .{}, 2047); + cubes := map.make(CubePos, CubeState, .{}); defer map.free(^cubes); z := 0; diff --git a/tests/aoc-2020/day20.onyx b/tests/aoc-2020/day20.onyx index c616f275..917a28ad 100644 --- a/tests/aoc-2020/day20.onyx +++ b/tests/aoc-2020/day20.onyx @@ -246,7 +246,7 @@ main :: (args: [] cstr) { tiles := array.make(Tile); defer array.free(^tiles); - tile_map := map.make(u32, #type ^Tile, null, 67); + tile_map := map.make(u32, #type ^Tile, null); defer map.free(^tile_map); tile_data := calloc(200 * sizeof TileData); diff --git a/tests/aoc-2020/day21.onyx b/tests/aoc-2020/day21.onyx index a2326d1a..c1dc72a5 100644 --- a/tests/aoc-2020/day21.onyx +++ b/tests/aoc-2020/day21.onyx @@ -36,7 +36,7 @@ main :: (args: [] cstr) { file := reader.make(contents); - map.init(^ingredient_map, .{}, 127); + map.init(^ingredient_map, .{}); map.init(^allergen_map, .{}); defer { map.free(^ingredient_map); diff --git a/tests/aoc-2020/day22.onyx b/tests/aoc-2020/day22.onyx index 616d62f4..7e4d9d63 100644 --- a/tests/aoc-2020/day22.onyx +++ b/tests/aoc-2020/day22.onyx @@ -60,7 +60,7 @@ encode_hands :: (alloc: Allocator, p1: ^[..] u32, p2: ^[..] u32) -> str { } recursive_combat :: (player1: ^[..] u32, player2: ^[..] u32) -> u32 { - hand_seen := map.make(str, bool, false, 31); + hand_seen := map.make(str, bool, false); defer map.free(^hand_seen); while player1.count > 0 && player2.count > 0 { diff --git a/tests/aoc-2020/day24.onyx b/tests/aoc-2020/day24.onyx index 79a53514..efef79d6 100644 --- a/tests/aoc-2020/day24.onyx +++ b/tests/aoc-2020/day24.onyx @@ -31,7 +31,7 @@ main :: (args: [] cstr) { file_stream := io.string_stream_make(contents); file := io.reader_make(^file_stream); - grid := map.make(Vec2, Cell, .{}, 1021); // `true` is black + grid := map.make(Vec2, Cell, .{}); // `true` is black defer map.free(^grid); while !io.stream_end_of_file(^file_stream) { diff --git a/tests/aoc-2020/day25.onyx b/tests/aoc-2020/day25.onyx index 443a1cb8..5d47bfae 100644 --- a/tests/aoc-2020/day25.onyx +++ b/tests/aoc-2020/day25.onyx @@ -22,7 +22,7 @@ power_mod :: (base: u32, exp: u32, mod: u32) -> u32 { dlp_bsgs :: (n: u32, a: u32, b: u32) -> u32 { m := cast(u32) math.ceil(math.sqrt(cast(f64) n)); - t := map.make(u32, u32, default=0, hash_count=m/2); + t := map.make(u32, u32, default=0); defer map.free(^t); tmp: u64 = 1; diff --git a/tests/persist_locals.onyx b/tests/persist_locals.onyx index 81f319f0..d3e9c407 100644 --- a/tests/persist_locals.onyx +++ b/tests/persist_locals.onyx @@ -29,7 +29,7 @@ foo :: (x: i32) -> i32 { cached_fib :: (n: u64) -> u64 { #persist cache : map.Map(u64, u64); if cache.hashes.data == null { - map.init(^cache, hash_count=128); + map.init(^cache); } if n <= 1 do return n; @@ -54,4 +54,4 @@ cached_fib :: (n: u64) -> u64 { printf("{} ", cache.entries.count); } -} \ No newline at end of file +} diff --git a/tests/struct_robustness.onyx b/tests/struct_robustness.onyx index 55d5f974..09648d30 100644 --- a/tests/struct_robustness.onyx +++ b/tests/struct_robustness.onyx @@ -140,11 +140,9 @@ main :: (args: [] cstr) { test_polymorphic_union_with_use :: () { println("\n\nTesting a polymorphic union with use."); - PolyStruct :: struct (T: type_expr, R: type_expr) { - use container : struct #union { - t_data : T; - r_data : R; - }; + PolyStruct :: struct (T: type_expr, R: type_expr) #union { + t_data : T; + r_data : R; } printf("{} == 16\n", sizeof PolyStruct(i32, [] u32));