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.
}
}
-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);
}
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 {
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;
}
// 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;
+ }
+ }
}
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;
// 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;
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(", ");
if formatting.pretty_printing {
- format.indentation -= 2;
+ format.indentation -= 4;
output->write("\n");
for _: format.indentation do output->write(#char " ");
output->write(#char "]");
__initialize :: (val: ^$T) -> void #intrinsic ---
__zero_value :: ($T: type_expr) -> T #intrinsic ---
+
+init :: macro ($T: type_expr) -> T {
+ val: T;
+ __initialize(^val);
+ return val;
+}
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 {
// 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
// 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);
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 {
use base := Value.{ type = .Object };
object_: [..] struct {
key : str;
+ key_is_static := false;
+
value : ^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 {
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_);
free(root, allocator);
},
}
+
+static_string :: (s: str) -> Value_String {
+ return .{ str_ = s, static = true };
+}
}
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");
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;
}
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) {
return (AstStructType *) poly_struct;
} else {
+ ENTITY_SUBMIT(s_node);
return s_node;
}
}
}
// 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);
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;
// 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;
file := reader.make(contents);
- cubes := map.make(CubePos, CubeState, .{}, 2047);
+ cubes := map.make(CubePos, CubeState, .{});
defer map.free(^cubes);
z := 0;
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);
file := reader.make(contents);
- map.init(^ingredient_map, .{}, 127);
+ map.init(^ingredient_map, .{});
map.init(^allergen_map, .{});
defer {
map.free(^ingredient_map);
}
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 {
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) {
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;
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;
printf("{} ", cache.entries.count);
}
-}
\ No newline at end of file
+}
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));