diff --git a/lib2/std/collections/anylist.c3 b/lib2/std/collections/anylist.c3 index 18ba0554c..d8a855d6d 100644 --- a/lib2/std/collections/anylist.c3 +++ b/lib2/std/collections/anylist.c3 @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Christoffer Lerno. All rights reserved. +// Copyright (c) 2024-2025 Christoffer Lerno. All rights reserved. // Use of self source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. module std::collections::anylist; @@ -16,16 +16,6 @@ struct AnyList (Printable) } -<* - Use `init` for to use a custom allocator. - - @param initial_capacity "The initial capacity to reserve" -*> -fn AnyList* AnyList.new_init(&self, usz initial_capacity = 16, Allocator allocator = null) -{ - return self.init(allocator ?: allocator::heap(), initial_capacity) @inline; -} - <* @param [&inout] allocator "The allocator to use" @param initial_capacity "The initial capacity to reserve" @@ -52,9 +42,9 @@ fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16) @param initial_capacity "The initial capacity to reserve" *> -fn AnyList* AnyList.temp_init(&self, usz initial_capacity = 16) +fn AnyList* AnyList.tinit(&self, usz initial_capacity = 16) { - return self.init(allocator::temp(), initial_capacity) @inline; + return self.init(tmem(), initial_capacity) @inline; } fn usz! AnyList.to_format(&self, Formatter* formatter) @dynamic @@ -152,13 +142,13 @@ fn any! AnyList.new_pop(&self, Allocator allocator = allocator::heap()) @return! IteratorResult.NO_MORE_ELEMENT @deprecated `use tcopy_pop` *> -fn any! AnyList.temp_pop(&self) => self.copy_pop(allocator::temp()); +fn any! AnyList.temp_pop(&self) => self.copy_pop(tmem()); <* Pop the last value and allocate the copy using the temp allocator @return! IteratorResult.NO_MORE_ELEMENT *> -fn any! AnyList.tcopy_pop(&self) => self.copy_pop(allocator::temp()); +fn any! AnyList.tcopy_pop(&self) => self.copy_pop(tmem()); <* Pop the last value. It must later be released using list.free_element() @@ -222,13 +212,13 @@ fn any! AnyList.copy_pop_first(&self, Allocator allocator = allocator::heap()) <* Same as temp_pop() but pops the first value instead. *> -fn any! AnyList.tcopy_pop_first(&self) => self.copy_pop_first(allocator::temp()); +fn any! AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem()); <* Same as temp_pop() but pops the first value instead. @deprecated `use tcopy_pop_first` *> -fn any! AnyList.temp_pop_first(&self) => self.new_pop_first(allocator::temp()); +fn any! AnyList.temp_pop_first(&self) => self.new_pop_first(tmem()); <* @require index < self.size @@ -466,7 +456,7 @@ fn void AnyList.reserve(&self, usz min_capacity) { if (!min_capacity) return; if (self.capacity >= min_capacity) return; - if (!self.allocator) self.allocator = allocator::heap(); + if (!self.allocator) self.allocator = tmem(); min_capacity = math::next_power_of_2(min_capacity); self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity); self.capacity = min_capacity; diff --git a/lib2/std/collections/bitset.c3 b/lib2/std/collections/bitset.c3 index 6e0619cb0..52ed4091a 100644 --- a/lib2/std/collections/bitset.c3 +++ b/lib2/std/collections/bitset.c3 @@ -1,7 +1,7 @@ <* @require SIZE > 0 *> -module std::collections::bitset(); +module std::collections::bitset{SIZE}; def Type = uint; @@ -75,7 +75,7 @@ import std::collections::list; const BITS = Type.sizeof * 8; -def GrowableBitSetList = List(); +def GrowableBitSetList = List{Type}; struct GrowableBitSet { @@ -86,15 +86,15 @@ struct GrowableBitSet @param initial_capacity @param [&inout] allocator "The allocator to use, defaults to the heap allocator" *> -fn GrowableBitSet* GrowableBitSet.new_init(&self, usz initial_capacity = 1, Allocator allocator = allocator::heap()) +fn GrowableBitSet* GrowableBitSet.init(&self, Allocator allocator, usz initial_capacity = 1) { - self.data.new_init(initial_capacity, allocator); + self.data.init(allocator, initial_capacity); return self; } -fn GrowableBitSet* GrowableBitSet.temp_init(&self, usz initial_capacity = 1) +fn GrowableBitSet* GrowableBitSet.tinit(&self, usz initial_capacity = 1) { - return self.new_init(initial_capacity, allocator::temp()) @inline; + return self.init(tmem(), initial_capacity) @inline; } fn void GrowableBitSet.free(&self) diff --git a/lib2/std/collections/elastic_array.c3 b/lib2/std/collections/elastic_array.c3 index db4e45aeb..a835aa68c 100644 --- a/lib2/std/collections/elastic_array.c3 +++ b/lib2/std/collections/elastic_array.c3 @@ -4,7 +4,7 @@ <* @require MAX_SIZE >= 1 `The size must be at least 1 element big.` *> -module std::collections::elastic_array(); +module std::collections::elastic_array{Type, MAX_SIZE}; import std::io, std::math, std::collections::list_common; def ElementPredicate = fn bool(Type *type); @@ -195,9 +195,9 @@ macro Type[] ElasticArray.to_array(&self, Allocator allocator) fn Type[] ElasticArray.to_tarray(&self) { $if type_is_overaligned(): - return self.to_aligned_array(allocator::temp()); + return self.to_aligned_array(tmem()); $else - return self.to_array(allocator::temp()); + return self.to_array(tmem()); $endif; } diff --git a/lib2/std/collections/enummap.c3 b/lib2/std/collections/enummap.c3 index 268ed2179..1c5437d14 100644 --- a/lib2/std/collections/enummap.c3 +++ b/lib2/std/collections/enummap.c3 @@ -1,7 +1,7 @@ <* @require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enummap" *> -module std::collections::enummap(); +module std::collections::enummap{Enum, ValueType}; import std::io; struct EnumMap (Printable) { @@ -33,11 +33,6 @@ fn String EnumMap.to_string(&self, Allocator allocator) @dynamic return string::format("%s", *self, allocator: allocator); } -fn String EnumMap.to_new_string(&self, Allocator allocator = null) @dynamic -{ - return string::format("%s", *self, allocator: allocator ?: allocator::heap()); -} - fn String EnumMap.to_tstring(&self) @dynamic { return string::tformat("%s", *self); diff --git a/lib2/std/collections/enumset.c3 b/lib2/std/collections/enumset.c3 index 3821ebd71..68cf9d807 100644 --- a/lib2/std/collections/enumset.c3 +++ b/lib2/std/collections/enumset.c3 @@ -5,10 +5,10 @@ <* @require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enumset" *> -module std::collections::enumset(); +module std::collections::enumset{Enum}; import std::io; -def EnumSetType = $typefrom(private::type_for_enum_elements(Enum.elements)) @private; +def EnumSetType = $typefrom(type_for_enum_elements(Enum.elements)) @private; const IS_CHAR_ARRAY = Enum.elements > 128; distinct EnumSet (Printable) = EnumSetType; @@ -141,24 +141,7 @@ fn usz! EnumSet.to_format(&set, Formatter* formatter) @dynamic return n; } -fn String EnumSet.to_new_string(&set, Allocator allocator = allocator::heap()) @dynamic -{ - return string::format("%s", *set, allocator: allocator); -} - -fn String EnumSet.to_string(&set, Allocator allocator) @dynamic -{ - return string::format("%s", *set, allocator: allocator); -} - -fn String EnumSet.to_tstring(&set) @dynamic -{ - return string::tformat("%s", *set); -} - -module std::collections::enumset::private; - -macro typeid type_for_enum_elements(usz $elements) +macro typeid type_for_enum_elements(usz $elements) @local { $switch $case ($elements > 128): diff --git a/lib2/std/collections/hashmap.c3 b/lib2/std/collections/hashmap.c3 index 36588b796..a033555af 100644 --- a/lib2/std/collections/hashmap.c3 +++ b/lib2/std/collections/hashmap.c3 @@ -4,10 +4,25 @@ <* @require $defined((Key){}.hash()) `No .hash function found on the key` *> -module std::collections::map(); +module std::collections::map{Key, Value}; import std::math; import std::io @norecurse; +const uint DEFAULT_INITIAL_CAPACITY = 16; +const uint MAXIMUM_CAPACITY = 1u << 31; +const float DEFAULT_LOAD_FACTOR = 0.75; +const VALUE_IS_EQUATABLE = Value.is_eq; +const bool COPY_KEYS = types::implements_copy(Key); + + +struct Entry +{ + uint hash; + Key key; + Value value; + Entry* next; +} + struct HashMap (Printable) { Entry*[] table; @@ -17,17 +32,6 @@ struct HashMap (Printable) float load_factor; } -<* - @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" -*> -fn HashMap* HashMap.new_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = null) -{ - return self.init(allocator ?: allocator::heap(), capacity, load_factor); -} <* @param [&inout] allocator "The allocator to use" @@ -52,9 +56,9 @@ fn HashMap* HashMap.init(&self, Allocator allocator, uint capacity = DEFAULT_INI @require !self.allocator "Map was already initialized" @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" *> -fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) +fn HashMap* HashMap.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) { - return self.init(allocator::temp(), capacity, load_factor) @inline; + return self.init(tmem(), capacity, load_factor) @inline; } <* @@ -65,15 +69,27 @@ fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, f @require !self.allocator "Map was already initialized" @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" *> -macro HashMap* HashMap.new_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) +macro HashMap* HashMap.init_with_key_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) { - self.new_init(capacity, load_factor, allocator); + self.init(allocator, capacity, load_factor); $for (var $i = 0; $i < $vacount; $i += 2) - self.set($vaarg[$i], $vaarg[$i+1]); + self.set($vaarg[$i], $vaarg[$i + 1]); $endfor return self; } +<* + @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" +*> +macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) +{ + return self.tinit_with_key_values(tmem(), capacity, load_factor); +} + <* @param [in] keys "The keys for the HashMap entries" @param [in] values "The values for the HashMap entries" @@ -84,10 +100,10 @@ macro HashMap* HashMap.new_init_with_key_values(&self, ..., uint capacity = DEFA @require !self.allocator "Map was already initialized" @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" *> -fn HashMap* HashMap.new_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) +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) { assert(keys.len == values.len); - self.new_init(capacity, load_factor, allocator); + self.init(allocator, capacity, load_factor); for (usz i = 0; i < keys.len; i++) { self.set(keys[i], values[i]); @@ -95,41 +111,19 @@ fn HashMap* HashMap.new_init_from_keys_and_values(&self, Key[] keys, Value[] val return self; } -<* - @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" -*> -macro HashMap* HashMap.temp_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) -{ - self.temp_init(capacity, load_factor); - $for (var $i = 0; $i < $vacount; $i += 2) - self.set($vaarg[$i], $vaarg[$i+1]); - $endfor - return self; -} <* @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" *> -fn HashMap* HashMap.temp_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) +fn HashMap* HashMap.tinit_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) { - assert(keys.len == values.len); - self.temp_init(capacity, load_factor); - for (usz i = 0; i < keys.len; i++) - { - self.set(keys[i], values[i]); - } - return self; + return self.init_from_keys_and_values(tmem(), keys, values, capacity, load_factor); } <* @@ -143,21 +137,13 @@ fn bool HashMap.is_initialized(&map) return (bool)map.allocator; } -<* - @param [&in] other_map "The map to copy from." -*> -fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map) -{ - return self.init_from_map(other_map, allocator::heap()) @inline; -} - <* @param [&inout] allocator "The allocator to use" @param [&in] other_map "The map to copy from." *> -fn HashMap* HashMap.init_from_map(&self, HashMap* other_map, Allocator allocator) +fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map) { - self.new_init(other_map.table.len, other_map.load_factor, allocator); + self.init(allocator, other_map.table.len, other_map.load_factor); self.put_all_for_create(other_map); return self; } @@ -165,9 +151,9 @@ fn HashMap* HashMap.init_from_map(&self, HashMap* other_map, Allocator allocator <* @param [&in] other_map "The map to copy from." *> -fn HashMap* HashMap.temp_init_from_map(&map, HashMap* other_map) +fn HashMap* HashMap.tinit_from_map(&map, HashMap* other_map) { - return map.init_from_map(other_map, allocator::temp()) @inline; + return map.init_from_map(tmem(), other_map) @inline; } fn bool HashMap.is_empty(&map) @inline @@ -240,7 +226,7 @@ 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.new_init(); + map.tinit(); } uint hash = rehash(key.hash()); uint index = index_for(hash, map.table.len); @@ -289,31 +275,18 @@ fn void HashMap.free(&map) map.table = {}; } -fn Key[] HashMap.tcopy_keys(&map) +fn Key[] HashMap.tkeys(&self) { - return map.copy_keys(allocator::temp()) @inline; + return self.keys(tmem()) @inline; } -fn Key[] HashMap.key_tlist(&map) @deprecated("Use 'tcopy_keys'") +fn Key[] HashMap.keys(&self, Allocator allocator) { - return map.copy_keys(allocator::temp()) @inline; -} + if (!self.count) return {}; -<* - @deprecated "use copy_keys" -*> -fn Key[] HashMap.key_new_list(&map, Allocator allocator = allocator::heap()) -{ - return map.copy_keys(allocator) @inline; -} - -fn Key[] HashMap.copy_keys(&map, Allocator allocator = allocator::heap()) -{ - if (!map.count) return {}; - - Key[] list = allocator::alloc_array(allocator, Key, map.count); + Key[] list = allocator::alloc_array(allocator, Key, self.count); usz index = 0; - foreach (Entry* entry : map.table) + foreach (Entry* entry : self.table) { while (entry) { @@ -330,53 +303,36 @@ fn Key[] HashMap.copy_keys(&map, Allocator allocator = allocator::heap()) macro HashMap.@each(map; @body(key, value)) { - map.@each_entry(; Entry* entry) { + map.@each_entry(; Entry* entry) + { @body(entry.key, entry.value); }; } macro HashMap.@each_entry(map; @body(entry)) { - if (map.count) + if (!map.count) return; + foreach (Entry* entry : map.table) { - foreach (Entry* entry : map.table) + while (entry) { - while (entry) - { - @body(entry); - entry = entry.next; - } + @body(entry); + entry = entry.next; } } } -<* - @deprecated `use tcopy_values` -*> -fn Value[] HashMap.value_tlist(&map) +fn Value[] HashMap.tvalues(&map) { - return map.copy_values(allocator::temp()) @inline; + return map.values(tmem()) @inline; } -fn Value[] HashMap.tcopy_values(&map) +fn Value[] HashMap.values(&self, Allocator allocator) { - return map.copy_values(allocator::temp()) @inline; -} - -<* - @deprecated `use copy_values` -*> -fn Value[] HashMap.value_new_list(&map, Allocator allocator = allocator::heap()) -{ - return map.copy_values(allocator); -} - -fn Value[] HashMap.copy_values(&map, Allocator allocator = allocator::heap()) -{ - if (!map.count) return {}; - Value[] list = allocator::alloc_array(allocator, Value, map.count); + if (!self.count) return {}; + Value[] list = allocator::alloc_array(allocator, Value, self.count); usz index = 0; - foreach (Entry* entry : map.table) + foreach (Entry* entry : self.table) { while (entry) { @@ -611,3 +567,14 @@ fn Key HashMapKeyIterator.get(&self, usz idx) @operator([]) fn usz HashMapValueIterator.len(self) @operator(len) => self.map.count; fn usz HashMapKeyIterator.len(self) @operator(len) => self.map.count; fn usz HashMapIterator.len(self) @operator(len) => self.map.count; + +fn uint rehash(uint hash) @inline @private +{ + hash ^= (hash >> 20) ^ (hash >> 12); + return hash ^ ((hash >> 7) ^ (hash >> 4)); +} + +macro uint index_for(uint hash, uint capacity) @private +{ + return hash & (capacity - 1); +} diff --git a/lib2/std/collections/linkedlist.c3 b/lib2/std/collections/linkedlist.c3 index 192111e02..921813c0a 100644 --- a/lib2/std/collections/linkedlist.c3 +++ b/lib2/std/collections/linkedlist.c3 @@ -1,7 +1,7 @@ // Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved. // Use of self source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. -module std::collections::linkedlist(); +module std::collections::linkedlist{Type}; const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type); @@ -30,18 +30,9 @@ fn LinkedList* LinkedList.init(&self, Allocator allocator) return self; } -<* - @return "the initialized list" -*> -fn LinkedList* LinkedList.new_init(&self) +fn LinkedList* LinkedList.tinit(&self) { - return self.init(allocator::heap()) @inline; -} - - -fn LinkedList* LinkedList.temp_init(&self) -{ - return self.init(allocator::temp()) @inline; + return self.init(tmem()) @inline; } <* @@ -54,7 +45,7 @@ macro void LinkedList.free_node(&self, Node* node) @private macro Node* LinkedList.alloc_node(&self) @private { - if (!self.allocator) self.allocator = allocator::heap(); + if (!self.allocator) self.allocator = tmem(); return allocator::alloc(self.allocator, Node); } diff --git a/lib2/std/collections/list.c3 b/lib2/std/collections/list.c3 index 8f6a45e00..c2e1c6bb2 100644 --- a/lib2/std/collections/list.c3 +++ b/lib2/std/collections/list.c3 @@ -1,7 +1,7 @@ // Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved. // Use of self source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. -module std::collections::list(); +module std::collections::list{Type}; import std::io, std::math, std::collections::list_common; def ElementPredicate = fn bool(Type *type); @@ -33,28 +33,15 @@ fn List* List.init(&self, Allocator allocator, usz initial_capacity = 16) return self; } -<* - @param initial_capacity "The initial capacity to reserve" - @param [&inout] allocator "The allocator to use, defaults to the heap allocator" -*> -fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap()) -{ - self.allocator = allocator; - self.size = 0; - self.capacity = 0; - self.entries = null; - self.reserve(initial_capacity); - return self; -} <* Initialize the list using the temp allocator. @param initial_capacity "The initial capacity to reserve" *> -fn List* List.temp_init(&self, usz initial_capacity = 16) +fn List* List.tinit(&self, usz initial_capacity = 16) { - return self.init(allocator::temp(), initial_capacity) @inline; + return self.init(tmem(), initial_capacity) @inline; } <* @@ -63,9 +50,9 @@ fn List* List.temp_init(&self, usz initial_capacity = 16) @param [in] values `The values to initialize the list with.` @require self.size == 0 "The List must be empty" *> -fn List* List.new_init_with_array(&self, Type[] values, Allocator allocator = allocator::heap()) +fn List* List.init_with_array(&self, Allocator allocator, Type[] values) { - self.new_init(values.len, allocator) @inline; + self.init(allocator, values.len) @inline; self.add_array(values) @inline; return self; } @@ -76,9 +63,9 @@ fn List* List.new_init_with_array(&self, Type[] values, Allocator allocator = al @param [in] values `The values to initialize the list with.` @require self.size == 0 "The List must be empty" *> -fn List* List.temp_init_with_array(&self, Type[] values) +fn List* List.tinit_with_array(&self, Type[] values) { - self.temp_init(values.len) @inline; + self.tinit(values.len) @inline; self.add_array(values) @inline; return self; } @@ -86,7 +73,7 @@ fn List* List.temp_init_with_array(&self, Type[] values) <* @require self.capacity == 0 "The List must not be allocated" *> -fn void List.init_wrapping_array(&self, Type[] types, Allocator allocator = allocator::heap()) +fn void List.init_wrapping_array(&self, Allocator allocator, Type[] types) { self.allocator = allocator; self.capacity = types.len; @@ -114,16 +101,6 @@ fn usz! List.to_format(&self, Formatter* formatter) @dynamic } } -fn String List.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic -{ - return string::format("%s", *self, allocator: allocator); -} - -fn String List.to_tstring(&self) -{ - return string::tformat("%s", *self); -} - fn void List.push(&self, Type element) @inline { self.reserve(1); @@ -174,25 +151,25 @@ fn void List.add_all(&self, List* other_list) <* IMPORTANT The returned array must be freed using free_aligned. *> -fn Type[] List.to_new_aligned_array(&self, Allocator allocator = allocator::heap()) +fn Type[] List.to_aligned_array(&self, Allocator allocator) { - return list_common::list_to_new_aligned_array(Type, self, allocator); + return list_common::list_to_aligned_array(Type, self, allocator); } <* @require !type_is_overaligned() : "This function is not available on overaligned types" *> -macro Type[] List.to_new_array(&self, Allocator allocator = allocator::heap()) +macro Type[] List.to_array(&self, Allocator allocator) { - return list_common::list_to_new_array(Type, self, allocator); + return list_common::list_to_array(Type, self, allocator); } fn Type[] List.to_tarray(&self) { $if type_is_overaligned(): - return self.to_new_aligned_array(allocator::temp()); + return self.to_aligned_array(tmem()); $else - return self.to_new_array(allocator::temp()); + return self.to_array(tmem()); $endif; } @@ -364,7 +341,7 @@ 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 = allocator::heap(); + if (!self.allocator) self.allocator = tmem(); self.pre_free(); // Remove sanitizer annotation diff --git a/lib2/std/collections/list_common.c3 b/lib2/std/collections/list_common.c3 index dccbf7afa..021fa1fac 100644 --- a/lib2/std/collections/list_common.c3 +++ b/lib2/std/collections/list_common.c3 @@ -3,7 +3,7 @@ module std::collections::list_common; <* IMPORTANT The returned array must be freed using free_aligned. *> -macro list_to_new_aligned_array($Type, self, Allocator allocator) +macro list_to_aligned_array($Type, self, Allocator allocator) { if (!self.size) return ($Type[]){}; $Type[] result = allocator::alloc_array_aligned(allocator, $Type, self.size); @@ -11,7 +11,7 @@ macro list_to_new_aligned_array($Type, self, Allocator allocator) return result; } -macro list_to_new_array($Type, self, Allocator allocator) +macro list_to_array($Type, self, Allocator allocator) { if (!self.size) return ($Type[]){}; $Type[] result = allocator::alloc_array(allocator, $Type, self.size); diff --git a/lib2/std/collections/map.c3 b/lib2/std/collections/map.c3 deleted file mode 100644 index bb5c1077c..000000000 --- a/lib2/std/collections/map.c3 +++ /dev/null @@ -1,508 +0,0 @@ -// Copyright (c) 2023 Christoffer Lerno. All rights reserved. -// Use of this source code is governed by the MIT license -// a copy of which can be found in the LICENSE_STDLIB file. -module std::collections::map(); -import std::math; - -const uint DEFAULT_INITIAL_CAPACITY = 16; -const uint MAXIMUM_CAPACITY = 1u << 31; -const float DEFAULT_LOAD_FACTOR = 0.75; -const VALUE_IS_EQUATABLE = Value.is_eq; -const bool COPY_KEYS = types::implements_copy(Key); - -distinct Map = void*; - -struct MapImpl -{ - Entry*[] table; - Allocator allocator; - uint count; // Number of elements - uint threshold; // Resize limit - float load_factor; -} - -<* - @require capacity > 0 "The capacity must be 1 or higher" - @require load_factor > 0.0 "The load factor must be higher than 0" - @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" -*> -fn Map new(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) -{ - MapImpl* map = allocator::alloc(allocator, MapImpl); - _init(map, capacity, load_factor, allocator); - return (Map)map; -} - -<* - @require capacity > 0 "The capacity must be 1 or higher" - @require load_factor > 0.0 "The load factor must be higher than 0" - @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" -*> -fn Map temp(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) -{ - MapImpl* map = mem::temp_alloc(MapImpl); - _init(map, capacity, load_factor, allocator::temp()); - return (Map)map; -} - -<* - @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 capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" -*> -macro Map new_init_with_key_values(..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) -{ - Map map = new(capacity, load_factor, allocator); - $for (var $i = 0; $i < $vacount; $i += 2) - map.set($vaarg[$i], $vaarg[$i+1]); - $endfor - return map; -} - -<* - @param [in] keys "Array of keys for the Map entries" - @param [in] values "Array of values for the Map 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 capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" -*> -fn Map new_init_from_keys_and_values(Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) -{ - assert(keys.len == values.len); - Map map = new(capacity, load_factor, allocator); - for (usz i = 0; i < keys.len; i++) - { - map.set(keys[i], values[i]); - } - return map; -} - -<* - @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 capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" -*> -macro Map temp_new_with_key_values(..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) -{ - Map map = temp(capacity, load_factor); - $for (var $i = 0; $i < $vacount; $i += 2) - map.set($vaarg[$i], $vaarg[$i+1]); - $endfor - return map; -} - -<* - @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 capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" -*> -fn Map temp_init_from_keys_and_values(Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) -{ - assert(keys.len == values.len); - Map map = temp(capacity, load_factor); - for (usz i = 0; i < keys.len; i++) - { - map.set(keys[i], values[i]); - } - return map; -} - -<* - @param [&in] other_map "The map to copy from." -*> -fn Map new_from_map(Map other_map, Allocator allocator = null) -{ - MapImpl* other_map_impl = (MapImpl*)other_map; - if (!other_map_impl) - { - if (allocator) return new(allocator: allocator); - return null; - } - MapImpl* map = (MapImpl*)new(other_map_impl.table.len, other_map_impl.load_factor, allocator ?: allocator::heap()); - if (!other_map_impl.count) return (Map)map; - foreach (Entry *e : other_map_impl.table) - { - while (e) - { - map._put_for_create(e.key, e.value); - e = e.next; - } - } - return (Map)map; -} - -<* - @param [&in] other_map "The map to copy from." -*> -fn Map temp_from_map(Map other_map) -{ - return new_from_map(other_map, allocator::temp()); -} - -fn bool Map.is_empty(map) @inline -{ - return !map || !((MapImpl*)map).count; -} - -fn usz Map.len(map) @inline -{ - return map ? ((MapImpl*)map).count : 0; -} - -fn Value*! Map.get_ref(self, Key key) -{ - MapImpl *map = (MapImpl*)self; - if (!map || !map.count) return SearchResult.MISSING?; - uint hash = rehash(key.hash()); - for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next) - { - if (e.hash == hash && equals(key, e.key)) return &e.value; - } - return SearchResult.MISSING?; -} - -fn Entry*! Map.get_entry(map, Key key) -{ - MapImpl *map_impl = (MapImpl*)map; - if (!map_impl || !map_impl.count) return SearchResult.MISSING?; - uint hash = rehash(key.hash()); - for (Entry *e = map_impl.table[index_for(hash, map_impl.table.len)]; e != null; e = e.next) - { - if (e.hash == hash && equals(key, e.key)) return e; - } - return SearchResult.MISSING?; -} - -<* - Get the value or update and - @require $assignable(#expr, Value) -*> -macro Value Map.@get_or_set(&self, Key key, Value #expr) -{ - MapImpl *map = (MapImpl*)*self; - if (!map || !map.count) - { - Value val = #expr; - map.set(key, val); - return val; - } - uint hash = rehash(key.hash()); - uint index = index_for(hash, map.table.len); - for (Entry *e = map.table[index]; e != null; e = e.next) - { - if (e.hash == hash && equals(key, e.key)) return e.value; - } - Value val = #expr; - map.add_entry(hash, key, val, index); - return val; -} - -fn Value! Map.get(map, Key key) @operator([]) -{ - return *map.get_ref(key) @inline; -} - -fn bool Map.has_key(map, Key key) -{ - return @ok(map.get_ref(key)); -} - -macro Value Map.set_value_return(&map, Key key, Value value) @operator([]=) -{ - map.set(key, value); - return value; -} - -fn bool Map.set(&self, Key key, Value value) -{ - // If the map isn't initialized, use the defaults to initialize it. - if (!*self) *self = new(); - MapImpl* map = (MapImpl*)*self; - uint hash = rehash(key.hash()); - uint index = index_for(hash, map.table.len); - for (Entry *e = map.table[index]; e != null; e = e.next) - { - if (e.hash == hash && equals(key, e.key)) - { - e.value = value; - return true; - } - } - map._add_entry(hash, key, value, index); - return false; -} - -fn void! Map.remove(map, Key key) @maydiscard -{ - if (!map || !((MapImpl*)map)._remove_entry_for_key(key)) return SearchResult.MISSING?; -} - -fn void Map.clear(self) -{ - MapImpl* map = (MapImpl*)self; - if (!map || !map.count) return; - foreach (Entry** &entry_ref : map.table) - { - Entry* entry = *entry_ref; - if (!entry) continue; - Entry *next = entry.next; - while (next) - { - Entry *to_delete = next; - next = next.next; - map._free_entry(to_delete); - } - map._free_entry(entry); - *entry_ref = null; - } - map.count = 0; -} - -fn void Map.free(self) -{ - if (!self) return; - MapImpl* map = (MapImpl*)self; - self.clear(); - map._free_internal(map.table.ptr); - map.table = {}; - allocator::free(map.allocator, map); -} - -fn Key[] Map.temp_keys_list(map) -{ - return map.new_keys_list(allocator::temp()) @inline; -} - -fn Key[] Map.new_keys_list(self, Allocator allocator = allocator::heap()) -{ - MapImpl* map = (MapImpl*)self; - if (!map || !map.count) return {}; - - Key[] list = allocator::alloc_array(allocator, Key, map.count); - usz index = 0; - foreach (Entry* entry : map.table) - { - while (entry) - { - list[index++] = entry.key; - entry = entry.next; - } - } - return list; -} - -macro Map.@each(map; @body(key, value)) -{ - map.@each_entry(; Entry* entry) { - @body(entry.key, entry.value); - }; -} - -macro Map.@each_entry(self; @body(entry)) -{ - MapImpl *map = (MapImpl*)self; - if (!map || !map.count) return; - foreach (Entry* entry : map.table) - { - while (entry) - { - @body(entry); - entry = entry.next; - } - } -} - -fn Value[] Map.temp_values_list(map) -{ - return map.new_values_list(allocator::temp()) @inline; -} - -fn Value[] Map.new_values_list(self, Allocator allocator = allocator::heap()) -{ - MapImpl* map = (MapImpl*)self; - if (!map || !map.count) return {}; - Value[] list = allocator::alloc_array(allocator, Value, map.count); - usz index = 0; - foreach (Entry* entry : map.table) - { - while (entry) - { - list[index++] = entry.value; - entry = entry.next; - } - } - return list; -} - -fn bool Map.has_value(self, Value v) @if(VALUE_IS_EQUATABLE) -{ - MapImpl* map = (MapImpl*)self; - if (!map || !map.count) return false; - foreach (Entry* entry : map.table) - { - while (entry) - { - if (equals(v, entry.value)) return true; - entry = entry.next; - } - } - return false; -} - -// --- private methods - -fn void MapImpl._add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private -{ - $if COPY_KEYS: - key = key.copy(map.allocator); - $endif - Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] }); - map.table[bucket_index] = entry; - if (map.count++ >= map.threshold) - { - map._resize(map.table.len * 2); - } -} - -fn void MapImpl._resize(&map, uint new_capacity) @private -{ - Entry*[] old_table = map.table; - uint old_capacity = old_table.len; - if (old_capacity == MAXIMUM_CAPACITY) - { - map.threshold = uint.max; - return; - } - Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity); - map._transfer(new_table); - map.table = new_table; - map._free_internal(old_table.ptr); - map.threshold = (uint)(new_capacity * map.load_factor); -} - -fn uint rehash(uint hash) @inline @private -{ - hash ^= (hash >> 20) ^ (hash >> 12); - return hash ^ ((hash >> 7) ^ (hash >> 4)); -} - -macro uint index_for(uint hash, uint capacity) @private -{ - return hash & (capacity - 1); -} - -fn void MapImpl._transfer(&map, Entry*[] new_table) @private -{ - Entry*[] src = map.table; - uint new_capacity = new_table.len; - foreach (uint j, Entry *e : src) - { - if (!e) continue; - do - { - Entry* next = e.next; - uint i = index_for(e.hash, new_capacity); - e.next = new_table[i]; - new_table[i] = e; - e = next; - } - while (e); - } -} - -fn void _init(MapImpl* impl, uint capacity, float load_factor, Allocator allocator) @private -{ - capacity = math::next_power_of_2(capacity); - *impl = { - .allocator = allocator, - .load_factor = load_factor, - .threshold = (uint)(capacity * load_factor), - .table = allocator::new_array(allocator, Entry*, capacity) - }; -} - -fn void MapImpl._put_for_create(&map, Key key, Value value) @private -{ - uint hash = rehash(key.hash()); - uint i = index_for(hash, map.table.len); - for (Entry *e = map.table[i]; e != null; e = e.next) - { - if (e.hash == hash && equals(key, e.key)) - { - e.value = value; - return; - } - } - map._create_entry(hash, key, value, i); -} - -fn void MapImpl._free_internal(&map, void* ptr) @inline @private -{ - allocator::free(map.allocator, ptr); -} - -fn bool MapImpl._remove_entry_for_key(&map, Key key) @private -{ - if (!map.count) return false; - uint hash = rehash(key.hash()); - uint i = index_for(hash, map.table.len); - Entry* prev = map.table[i]; - Entry* e = prev; - while (e) - { - Entry *next = e.next; - if (e.hash == hash && equals(key, e.key)) - { - map.count--; - if (prev == e) - { - map.table[i] = next; - } - else - { - prev.next = next; - } - map._free_entry(e); - return true; - } - prev = e; - e = next; - } - return false; -} - -fn void MapImpl._create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private -{ - Entry *e = map.table[bucket_index]; - $if COPY_KEYS: - key = key.copy(map.allocator); - $endif - Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] }); - map.table[bucket_index] = entry; - map.count++; -} - -fn void MapImpl._free_entry(&self, Entry *entry) @local -{ - $if COPY_KEYS: - allocator::free(self.allocator, entry.key); - $endif - self._free_internal(entry); -} - -struct Entry -{ - uint hash; - Key key; - Value value; - Entry* next; -} diff --git a/lib2/std/collections/maybe.c3 b/lib2/std/collections/maybe.c3 index 948a01eb5..c5854e8da 100644 --- a/lib2/std/collections/maybe.c3 +++ b/lib2/std/collections/maybe.c3 @@ -1,4 +1,4 @@ -module std::collections::maybe(); +module std::collections::maybe{Type}; import std::io; struct Maybe (Printable) diff --git a/lib2/std/collections/object.c3 b/lib2/std/collections/object.c3 index f14dc87d0..a4434bc3b 100644 --- a/lib2/std/collections/object.c3 +++ b/lib2/std/collections/object.c3 @@ -50,7 +50,7 @@ fn usz! Object.to_format(&self, Formatter* formatter) @dynamic usz n = formatter.printf("{")!; @stack_mem(1024; Allocator mem) { - foreach (i, key : self.map.copy_keys(mem)) + foreach (i, key : self.map.keys(mem)) { if (i > 0) n += formatter.printf(",")!; n += formatter.printf(`"%s":`, key)!; @@ -156,7 +156,7 @@ fn void Object.init_map_if_needed(&self) @private if (self.is_empty()) { self.type = ObjectInternalMap.typeid; - self.map.new_init(allocator: self.allocator); + self.map.tinit(); } } @@ -168,7 +168,7 @@ fn void Object.init_array_if_needed(&self) @private if (self.is_empty()) { self.type = ObjectInternalList.typeid; - self.array.new_init(allocator: self.allocator); + self.array.init(self.allocator); } } diff --git a/lib2/std/collections/priorityqueue.c3 b/lib2/std/collections/priorityqueue.c3 index a4d0a3144..599abd3d1 100644 --- a/lib2/std/collections/priorityqueue.c3 +++ b/lib2/std/collections/priorityqueue.c3 @@ -1,7 +1,7 @@ // priorityqueue.c3 // A priority queue using a classic binary heap for C3. // -// Copyright (c) 2022 David Kopec +// Copyright (c) 2022-2025 David Kopec // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -20,35 +20,30 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -module std::collections::priorityqueue(); +module std::collections::priorityqueue{Type}; import std::collections::priorityqueue::private; -distinct PriorityQueue = inline PrivatePriorityQueue(); -distinct PriorityQueueMax = inline PrivatePriorityQueue(); +distinct PriorityQueue = inline List{Type}; +distinct PriorityQueueMax = inline List{Type}; -module std::collections::priorityqueue::private(); +module std::collections::priorityqueue::private{Type, MAX}; import std::collections::list, std::io; -def Heap = List(); - struct PrivatePriorityQueue (Printable) { - Heap heap; + List{Type} heap; } -fn void PrivatePriorityQueue.init(&self, Allocator allocator, usz initial_capacity = 16, ) @inline +fn PrivatePriorityQueue* PrivatePriorityQueue.init(&self, Allocator allocator, usz initial_capacity = 16, ) @inline { - self.heap.new_init(initial_capacity, allocator); + self.heap.init(allocator, initial_capacity); + return self; } -fn void PrivatePriorityQueue.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @inline +fn PrivatePriorityQueue* PrivatePriorityQueue.tinit(&self, usz initial_capacity = 16) @inline { - self.heap.new_init(initial_capacity, allocator); -} - -fn void PrivatePriorityQueue.temp_init(&self, usz initial_capacity = 16) @inline -{ - self.heap.new_init(initial_capacity, allocator::temp()) @inline; + self.init(tmem(), initial_capacity); + return self; } @@ -72,11 +67,14 @@ fn void PrivatePriorityQueue.push(&self, Type element) } } +<* + @require index < self.len : "Index out of range" +*> fn void PrivatePriorityQueue.remove_at(&self, usz index) { if (index == 0) { - self.pop()!!; + self.heap.remove_last(); return; } self.heap.remove_at(index); @@ -124,8 +122,7 @@ fn Type! PrivatePriorityQueue.pop(&self) fn Type! PrivatePriorityQueue.first(&self) { - if (!self.len()) return IteratorResult.NO_MORE_ELEMENT?; - return self.heap.get(0); + return self.heap.first(); } fn void PrivatePriorityQueue.free(&self) @@ -135,12 +132,12 @@ fn void PrivatePriorityQueue.free(&self) fn usz PrivatePriorityQueue.len(&self) @operator(len) { - return self.heap.len(); + return self.heap.len() @inline; } fn bool PrivatePriorityQueue.is_empty(&self) { - return self.heap.is_empty(); + return self.heap.is_empty() @inline; } <* @@ -156,8 +153,3 @@ fn usz! PrivatePriorityQueue.to_format(&self, Formatter* formatter) @dynamic return self.heap.to_format(formatter); } -fn String PrivatePriorityQueue.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic -{ - return self.heap.to_new_string(allocator); -} - diff --git a/lib2/std/collections/range.c3 b/lib2/std/collections/range.c3 index 98917a167..e689856ef 100644 --- a/lib2/std/collections/range.c3 +++ b/lib2/std/collections/range.c3 @@ -1,7 +1,7 @@ <* @require Type.is_ordered : "The type must be ordered" *> -module std::collections::range(); +module std::collections::range{Type}; import std::io; struct Range (Printable) @@ -29,21 +29,6 @@ fn Type Range.get(&self, usz index) @operator([]) return (Type)(self.start + (usz)index); } -fn String Range.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic @deprecated -{ - return string::format("[%s..%s]", self.start, self.end, allocator: allocator); -} - -fn String Range.to_string(&self, Allocator allocator) @dynamic -{ - return string::format("[%s..%s]", self.start, self.end, allocator: allocator); -} - -fn String Range.to_tstring(&self) -{ - return self.to_string(allocator::temp()); -} - fn usz! Range.to_format(&self, Formatter* formatter) @dynamic { return formatter.printf("[%s..%s]", self.start, self.end)!; @@ -71,21 +56,6 @@ fn usz! ExclusiveRange.to_format(&self, Formatter* formatter) @dynamic return formatter.printf("[%s..<%s]", self.start, self.end)!; } -fn String ExclusiveRange.to_new_string(&self, Allocator allocator = null) @dynamic -{ - return self.to_string(allocator ?: allocator::heap()); -} - -fn String ExclusiveRange.to_string(&self, Allocator allocator) @dynamic -{ - return string::format("[%s..<%s]", self.start, self.end, allocator: allocator); -} - -fn String ExclusiveRange.to_tstring(&self) -{ - return self.to_new_string(allocator::temp()); -} - <* @require index < self.len() : "Can't index into an empty range" *> diff --git a/lib2/std/collections/ringbuffer.c3 b/lib2/std/collections/ringbuffer.c3 index 38ae9e723..33f2fce0f 100644 --- a/lib2/std/collections/ringbuffer.c3 +++ b/lib2/std/collections/ringbuffer.c3 @@ -1,11 +1,13 @@ <* - @require values::@is_int(SIZE) &&& SIZE > 0 "The size must be positive integer" + @require Type.kindof == ARRAY : "Required an array type" *> -module std::collections::ringbuffer(); +module std::collections::ringbuffer(); + +def Element = $typeof((Type){}[0]); struct RingBuffer { - Type[SIZE] buf; + Type buf; usz written; usz head; } @@ -15,9 +17,9 @@ fn void RingBuffer.init(&self) @inline *self = {}; } -fn void RingBuffer.push(&self, Type c) +fn void RingBuffer.push(&self, Element c) { - if (self.written < SIZE) + if (self.written < buf.len) { self.buf[self.written] = c; self.written++; @@ -25,14 +27,14 @@ fn void RingBuffer.push(&self, Type c) else { self.buf[self.head] = c; - self.head = (self.head + 1) % SIZE; + self.head = (self.head + 1) % buf.len; } } -fn Type RingBuffer.get(&self, usz index) @operator([]) +fn Element RingBuffer.get(&self, usz index) @operator([]) { - index %= SIZE; - usz avail = SIZE - self.head; + index %= buf.len; + usz avail = buf.len - self.head; if (index < avail) { return self.buf[self.head + index]; @@ -40,25 +42,31 @@ fn Type RingBuffer.get(&self, usz index) @operator([]) return self.buf[index - avail]; } -fn Type! RingBuffer.pop(&self) +fn Element! RingBuffer.pop(&self) { switch { case self.written == 0: return SearchResult.MISSING?; - case self.written < SIZE: + case self.written < buf.len: self.written--; return self.buf[self.written]; default: - self.head = (self.head - 1) % SIZE; + self.head = (self.head - 1) % buf.len; return self.buf[self.head]; } } -fn usz RingBuffer.read(&self, usz index, Type[] buffer) +fn usz! RingBuffer.to_format(&self, Formatter* format) { - index %= SIZE; - if (self.written < SIZE) + // Improve this? + return format.printnf("%s", self.buf); +} + +fn usz RingBuffer.read(&self, usz index, Element[] buffer) +{ + index %= buf.len; + if (self.written < buf.len) { if (index >= self.written) return 0; usz end = self.written - index; @@ -66,7 +74,7 @@ fn usz RingBuffer.read(&self, usz index, Type[] buffer) buffer[:n] = self.buf[index:n]; return n; } - usz end = SIZE - self.head; + usz end = buf.len - self.head; if (index >= end) { index -= end; @@ -75,13 +83,13 @@ fn usz RingBuffer.read(&self, usz index, Type[] buffer) buffer[:n] = self.buf[index:n]; return n; } - if (buffer.len <= SIZE - index) + if (buffer.len <= buf.len - index) { usz n = buffer.len; buffer[:n] = self.buf[self.head + index:n]; return n; } - usz n1 = SIZE - index; + usz n1 = buf.len - index; buffer[:n1] = self.buf[self.head + index:n1]; buffer = buffer[n1..]; index -= n1; @@ -90,10 +98,10 @@ fn usz RingBuffer.read(&self, usz index, Type[] buffer) return n1 + n2; } -fn void RingBuffer.write(&self, Type[] buffer) +fn void RingBuffer.write(&self, Element[] buffer) { usz i; - while (self.written < SIZE && i < buffer.len) + while (self.written < buf.len && i < buffer.len) { self.buf[self.written] = buffer[i++]; self.written++; @@ -101,6 +109,6 @@ fn void RingBuffer.write(&self, Type[] buffer) foreach (c : buffer[i..]) { self.buf[self.head] = c; - self.head = (self.head + 1) % SIZE; + self.head = (self.head + 1) % buf.len; } } \ No newline at end of file diff --git a/lib2/std/collections/tuple.c3 b/lib2/std/collections/tuple.c3 index d8869e844..597605e35 100644 --- a/lib2/std/collections/tuple.c3 +++ b/lib2/std/collections/tuple.c3 @@ -1,4 +1,4 @@ -module std::collections::tuple(); +module std::collections::tuple{Type1, Type2}; struct Tuple { @@ -6,7 +6,7 @@ struct Tuple Type2 second; } -module std::collections::triple(); +module std::collections::triple{Type1, Type2, Type3}; struct Triple { diff --git a/lib2/std/core/allocators/tracking_allocator.c3 b/lib2/std/core/allocators/tracking_allocator.c3 index b2d5de3bd..ad4eb81fe 100644 --- a/lib2/std/core/allocators/tracking_allocator.c3 +++ b/lib2/std/core/allocators/tracking_allocator.c3 @@ -34,7 +34,7 @@ struct TrackingAllocator (Allocator) fn void TrackingAllocator.init(&self, Allocator allocator) { *self = { .inner_allocator = allocator }; - self.map.new_init(allocator: allocator); + self.map.init(allocator); } <* @@ -52,7 +52,7 @@ fn void TrackingAllocator.free(&self) fn usz TrackingAllocator.allocated(&self) => @pool() { usz allocated = 0; - foreach (&allocation : self.map.value_tlist()) allocated += allocation.size; + foreach (&allocation : self.map.tvalues()) allocated += allocation.size; return allocated; } @@ -68,7 +68,7 @@ fn usz TrackingAllocator.total_allocation_count(&self) => self.allocs_total; fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator allocator) { - return self.map.value_tlist(); + return self.map.tvalues(); } <* @@ -127,7 +127,7 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream out) => @pool() usz entries = 0; bool leaks = false; - Allocation[] allocs = self.map.value_tlist(); + Allocation[] allocs = self.map.tvalues(); if (allocs.len) { if (!allocs[0].backtrace[0]) @@ -155,7 +155,7 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream out) => @pool() Backtrace trace = backtrace::BACKTRACE_UNKNOWN; if (allocation.backtrace[3]) { - trace = backtrace::symbolize_backtrace(allocation.backtrace[3:1], allocator::temp()).get(0) ?? backtrace::BACKTRACE_UNKNOWN; + trace = backtrace::symbolize_backtrace(tmem(), allocation.backtrace[3:1]).get(0) ?? backtrace::BACKTRACE_UNKNOWN; } if (trace.function.len) leaks = true; io::fprintfn(out, "%13s %p %s:%d", allocation.size, @@ -194,7 +194,7 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream out) => @pool() break; } } - BacktraceList list = backtrace::symbolize_backtrace(allocation.backtrace[3..(end - 1)], allocator::temp())!; + BacktraceList list = backtrace::symbolize_backtrace(tmem(), allocation.backtrace[3..(end - 1)])!; io::fprintfn(out, "Allocation %d (%d bytes): ", i + 1, allocation.size)!; foreach (trace : list) { diff --git a/lib2/std/core/builtin.c3 b/lib2/std/core/builtin.c3 index c99121688..f9b3c2406 100644 --- a/lib2/std/core/builtin.c3 +++ b/lib2/std/core/builtin.c3 @@ -67,7 +67,7 @@ fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIV void*[256] buffer; void*[] backtraces = backtrace::capture_current(&buffer); backtraces_to_ignore++; - BacktraceList! backtrace = backtrace::symbolize_backtrace(backtraces, allocator::temp()); + BacktraceList! backtrace = backtrace::symbolize_backtrace(allocator::temp(), backtraces); if (catch backtrace) return false; if (backtrace.len() <= backtraces_to_ignore) return false; io::eprint("\nERROR: '"); diff --git a/lib2/std/core/mem_allocator.c3 b/lib2/std/core/mem_allocator.c3 index aecdb270c..9f832fad4 100644 --- a/lib2/std/core/mem_allocator.c3 +++ b/lib2/std/core/mem_allocator.c3 @@ -401,6 +401,15 @@ macro TempAllocator* temp() return thread_temp_allocator; } +macro TempAllocator* tmem() @builtin +{ + if (!thread_temp_allocator) + { + init_default_temp_allocators(); + } + return thread_temp_allocator; +} + fn void init_default_temp_allocators() @private { temp_allocator_pair[0] = create_default_sized_temp_allocator(temp_base_allocator); diff --git a/lib2/std/io/formatter.c3 b/lib2/std/io/formatter.c3 index 85f499545..c685a23c9 100644 --- a/lib2/std/io/formatter.c3 +++ b/lib2/std/io/formatter.c3 @@ -6,8 +6,7 @@ const int PRINTF_NTOA_BUFFER_SIZE = 256; interface Printable { - fn String to_string(Allocator allocator) @optional; - fn String to_new_string(Allocator allocator) @optional @deprecated("Use to_string"); + fn String to_constant_string() @optional; fn usz! to_format(Formatter* formatter) @optional; } @@ -125,7 +124,7 @@ fn usz! Formatter.print_with_function(&self, Printable arg) if (!arg) return self.out_substr("(null)"); return arg.to_format(self); } - if (&arg.to_string) + if (&arg.to_constant_string) { PrintFlags old = self.flags; uint old_width = self.width; @@ -137,10 +136,7 @@ fn usz! Formatter.print_with_function(&self, Printable arg) self.prec = old_prec; } if (!arg) return self.out_substr("(null)"); - @stack_mem(1024; Allocator mem) - { - return self.out_substr(arg.to_string(mem)); - }; + return self.out_substr(arg.to_constant_string()); } return SearchResult.MISSING?; } diff --git a/lib2/std/io/os/ls.c3 b/lib2/std/io/os/ls.c3 index 3dcef7089..c9c9edd0a 100644 --- a/lib2/std/io/os/ls.c3 +++ b/lib2/std/io/os/ls.c3 @@ -4,7 +4,7 @@ import std::io, std::os; fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator) { PathList list; - list.new_init(allocator: allocator); + list.init(allocator); DIRPtr directory = posix::opendir(dir.str_view() ? dir.as_zstr() : (ZString)"."); defer if (directory) posix::closedir(directory); if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?; diff --git a/lib2/std/net/url.c3 b/lib2/std/net/url.c3 index 27ede360b..b41f1ab87 100644 --- a/lib2/std/net/url.c3 +++ b/lib2/std/net/url.c3 @@ -275,7 +275,7 @@ fn UrlQueryValues parse_query(String query, Allocator allocator) { UrlQueryValues vals; vals.map.init(allocator); - vals.key_order.new_init(allocator: allocator); + vals.key_order.init(allocator); Splitter raw_vals = query.tokenize("&"); while (try String rv = raw_vals.next()) @@ -309,7 +309,7 @@ fn UrlQueryValues* UrlQueryValues.add(&self, String key, String value) else { UrlQueryValueList new_list; - new_list.new_init_with_array({ value_copy }, self.allocator); + new_list.init_with_array(self.allocator, { value_copy }); (*self)[key] = new_list; self.key_order.push(key.copy(self.allocator)); } diff --git a/lib2/std/os/macos/darwin.c3 b/lib2/std/os/macos/darwin.c3 index 6c8fe9203..ba0298973 100644 --- a/lib2/std/os/macos/darwin.c3 +++ b/lib2/std/os/macos/darwin.c3 @@ -132,11 +132,11 @@ fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_a }; } -fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator) +fn BacktraceList! symbolize_backtrace(Allocator allocator, void*[] backtrace) { void *load_addr = (void *)load_address()!; BacktraceList list; - list.new_init(backtrace.len, allocator); + list.init(allocator, backtrace.len); defer catch { foreach (trace : list) diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 9255f94fe..529340165 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -2149,7 +2149,7 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_CT_TYPEOF] = { parse_type_expr, NULL, PREC_NONE }, [TOKEN_CT_STRINGIFY] = { parse_ct_stringify, NULL, PREC_NONE }, [TOKEN_CT_EVALTYPE] = { parse_type_expr, NULL, PREC_NONE }, - [TOKEN_LBRACE] = { parse_initializer_list, parse_generic_expr, PREC_NONE }, + [TOKEN_LBRACE] = { parse_initializer_list, parse_generic_expr, PREC_PRIMARY }, [TOKEN_CT_VACOUNT] = { parse_ct_arg, NULL, PREC_NONE }, [TOKEN_CT_VAARG] = { parse_ct_arg, NULL, PREC_NONE }, [TOKEN_CT_VAREF] = { parse_ct_arg, NULL, PREC_NONE }, diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 81af7f441..79ec5ba3b 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -18,14 +18,6 @@ static Ast *parse_decl_stmt_after_type(ParseContext *c, TypeInfo *type) Decl *decl = ast->declare_stmt; switch (c->tok) { - case TOKEN_LBRACE: - if (decl->var.init_expr && decl->var.init_expr->expr_kind == EXPR_UNRESOLVED_IDENTIFIER) - { - print_error_at(decl->var.init_expr->span, - "An identifier would not usually be followed by a '{'. Did you intend write the name of a type here?"); - return poisoned_ast; - } - break; case TOKEN_LBRACKET: if (!decl->var.init_expr) {