From 13f824e349823f1bd6a897fe771544442d4dbe81 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 3 Mar 2025 12:20:18 +0100 Subject: [PATCH] Add ONHEAP for List and HashMap --- lib/std/collections/hashmap.c3 | 98 +++++++++++++++++++--------------- lib/std/collections/list.c3 | 20 ++++++- releasenotes.md | 2 +- 3 files changed, 74 insertions(+), 46 deletions(-) diff --git a/lib/std/collections/hashmap.c3 b/lib/std/collections/hashmap.c3 index a033555af..6bfc2fdd9 100644 --- a/lib/std/collections/hashmap.c3 +++ b/lib/std/collections/hashmap.c3 @@ -14,6 +14,9 @@ const float DEFAULT_LOAD_FACTOR = 0.75; const VALUE_IS_EQUATABLE = Value.is_eq; const bool COPY_KEYS = types::implements_copy(Key); +const Allocator MAP_HEAP_ALLOCATOR = (Allocator)&dummy; + +const HashMap ONHEAP = { .allocator = MAP_HEAP_ALLOCATOR }; struct Entry { @@ -34,11 +37,11 @@ struct HashMap (Printable) <* - @param [&inout] allocator "The allocator to use" - @require capacity > 0 "The capacity must be 1 or higher" - @require load_factor > 0.0 "The load factor must be higher than 0" - @require !self.allocator "Map was already initialized" - @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" + @param [&inout] allocator : "The allocator to use" + @require capacity > 0 : "The capacity must be 1 or higher" + @require load_factor > 0.0 : "The load factor must be higher than 0" + @require !self.is_initialized() : "Map was already initialized" + @require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum" *> fn HashMap* HashMap.init(&self, Allocator allocator, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) { @@ -51,10 +54,10 @@ fn HashMap* HashMap.init(&self, Allocator allocator, uint capacity = DEFAULT_INI } <* - @require capacity > 0 "The capacity must be 1 or higher" - @require load_factor > 0.0 "The load factor must be higher than 0" - @require !self.allocator "Map was already initialized" - @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" + @require capacity > 0 : "The capacity must be 1 or higher" + @require load_factor > 0.0 : "The load factor must be higher than 0" + @require !self.is_initialized() : "Map was already initialized" + @require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum" *> fn HashMap* HashMap.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) { @@ -62,12 +65,12 @@ fn HashMap* HashMap.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float } <* - @param [&inout] allocator "The allocator to use" - @require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values" - @require capacity > 0 "The capacity must be 1 or higher" - @require load_factor > 0.0 "The load factor must be higher than 0" - @require !self.allocator "Map was already initialized" - @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" + @param [&inout] allocator : "The allocator to use" + @require $vacount % 2 == 0 : "There must be an even number of arguments provided for keys and values" + @require capacity > 0 : "The capacity must be 1 or higher" + @require load_factor > 0.0 : "The load factor must be higher than 0" + @require !self.is_initialized() : "Map was already initialized" + @require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum" *> macro HashMap* HashMap.init_with_key_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) { @@ -79,11 +82,11 @@ macro HashMap* HashMap.init_with_key_values(&self, Allocator allocator, ..., uin } <* - @require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values" - @require capacity > 0 "The capacity must be 1 or higher" - @require load_factor > 0.0 "The load factor must be higher than 0" - @require !self.allocator "Map was already initialized" - @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" + @require $vacount % 2 == 0 : "There must be an even number of arguments provided for keys and values" + @require capacity > 0 : "The capacity must be 1 or higher" + @require load_factor > 0.0 : "The load factor must be higher than 0" + @require !self.is_initialized() : "Map was already initialized" + @require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum" *> macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) { @@ -91,14 +94,14 @@ macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT } <* - @param [in] keys "The keys for the HashMap entries" - @param [in] values "The values for the HashMap entries" - @param [&inout] allocator "The allocator to use" - @require keys.len == values.len "Both keys and values arrays must be the same length" - @require capacity > 0 "The capacity must be 1 or higher" - @require load_factor > 0.0 "The load factor must be higher than 0" - @require !self.allocator "Map was already initialized" - @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" + @param [in] keys : "The keys for the HashMap entries" + @param [in] values : "The values for the HashMap entries" + @param [&inout] allocator : "The allocator to use" + @require keys.len == values.len : "Both keys and values arrays must be the same length" + @require capacity > 0 : "The capacity must be 1 or higher" + @require load_factor > 0.0 : "The load factor must be higher than 0" + @require !self.is_initialized() : "Map was already initialized" + @require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum" *> fn HashMap* HashMap.init_from_keys_and_values(&self, Allocator allocator, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) { @@ -113,13 +116,13 @@ fn HashMap* HashMap.init_from_keys_and_values(&self, Allocator allocator, Key[] <* - @param [in] keys "The keys for the HashMap entries" - @param [in] values "The values for the HashMap entries" - @require keys.len == values.len "Both keys and values arrays must be the same length" - @require capacity > 0 "The capacity must be 1 or higher" - @require load_factor > 0.0 "The load factor must be higher than 0" - @require !self.allocator "Map was already initialized" - @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" + @param [in] keys : "The keys for the HashMap entries" + @param [in] values : "The values for the HashMap entries" + @require keys.len == values.len : "Both keys and values arrays must be the same length" + @require capacity > 0 : "The capacity must be 1 or higher" + @require load_factor > 0.0 : "The load factor must be higher than 0" + @require !self.is_initialized() : "Map was already initialized" + @require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum" *> fn HashMap* HashMap.tinit_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) { @@ -134,12 +137,13 @@ fn HashMap* HashMap.tinit_from_keys_and_values(&self, Key[] keys, Value[] values *> fn bool HashMap.is_initialized(&map) { - return (bool)map.allocator; + return map.allocator && map.allocator.ptr != &dummy; } <* - @param [&inout] allocator "The allocator to use" - @param [&in] other_map "The map to copy from." + @param [&inout] allocator : "The allocator to use" + @param [&in] other_map : "The map to copy from." + @require !self.is_initialized() : "Map was already initialized" *> fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map) { @@ -150,6 +154,7 @@ fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map <* @param [&in] other_map "The map to copy from." + @require !map.is_initialized() : "Map was already initialized" *> fn HashMap* HashMap.tinit_from_map(&map, HashMap* other_map) { @@ -224,10 +229,15 @@ fn bool HashMap.has_key(&map, Key key) fn bool HashMap.set(&map, Key key, Value value) @operator([]=) { // If the map isn't initialized, use the defaults to initialize it. - if (!map.allocator) - { - map.tinit(); - } + switch (map.allocator.ptr) + { + case &dummy: + map.init(mem); + case null: + map.tinit(); + default: + break; + } uint hash = rehash(key.hash()); uint index = index_for(hash, map.table.len); for (Entry *e = map.table[index]; e != null; e = e.next) @@ -269,7 +279,7 @@ fn void HashMap.clear(&map) fn void HashMap.free(&map) { - if (!map.allocator) return; + if (!map.is_initialized()) return; map.clear(); map.free_internal(map.table.ptr); map.table = {}; @@ -578,3 +588,5 @@ macro uint index_for(uint hash, uint capacity) @private { return hash & (capacity - 1); } + +int dummy @local; \ No newline at end of file diff --git a/lib/std/collections/list.c3 b/lib/std/collections/list.c3 index 7d4e3cb0a..7a2662732 100644 --- a/lib/std/collections/list.c3 +++ b/lib/std/collections/list.c3 @@ -9,6 +9,10 @@ def ElementTest = fn bool(Type *type, any context); const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type); const ELEMENT_IS_POINTER = Type.kindof == POINTER; +const Allocator LIST_HEAP_ALLOCATOR = (Allocator)&dummy; + +const List ONHEAP = { .allocator = LIST_HEAP_ALLOCATOR }; + macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT; struct List (Printable) @@ -276,7 +280,7 @@ fn Type List.get(&self, usz index) @inline fn void List.free(&self) { - if (!self.allocator || !self.capacity) return; + if (!self.allocator || self.allocator.ptr == &dummy || !self.capacity) return; self.pre_free(); // Remove sanitizer annotation @@ -341,7 +345,17 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local { if (!min_capacity) return; if (self.capacity >= min_capacity) return; - if (!self.allocator) self.allocator = tmem(); + + // Get a proper allocator + switch (self.allocator.ptr) + { + case &dummy: + self.allocator = mem; + case null: + self.allocator = tmem(); + default: + break; + } self.pre_free(); // Remove sanitizer annotation @@ -538,3 +552,5 @@ fn usz List.compact(&self) @if(ELEMENT_IS_POINTER) } return list_common::list_compact(self); } + +int dummy @local; diff --git a/releasenotes.md b/releasenotes.md index a15bf801d..26d431086 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -29,7 +29,7 @@ - `mem::temp_new` changed to `mem::tnew`. - `mem::temp_alloc` and related changed to `mem::talloc`. - `mem::temp_new_array` changed to `mem::temp_array`. - +- Add `ONHEAP` variants for List/HashMap for initializing global maps on the heap. ## 0.6.8 Change list