diff --git a/lib/std/atomic.c3 b/lib/std/atomic.c3 index 754a34620..c26cf793e 100644 --- a/lib/std/atomic.c3 +++ b/lib/std/atomic.c3 @@ -1,7 +1,7 @@ // Copyright (c) 2023 Eduardo José Gómez Hernández. 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::atomic::types(); +module std::atomic::types{Type}; struct Atomic { diff --git a/lib/std/collections/anylist.c3 b/lib/std/collections/anylist.c3 index e63fa7085..fc369ce81 100644 --- a/lib/std/collections/anylist.c3 +++ b/lib/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) @deprecated("Use init(mem)") -{ - return self.init(allocator ?: allocator::heap(), initial_capacity) @inline; -} - <* @param [&inout] allocator "The allocator to use" @param initial_capacity "The initial capacity to reserve" @@ -47,17 +37,6 @@ fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16) return self; } -<* - Initialize the list using the temp allocator. - - @param initial_capacity "The initial capacity to reserve" -*> -fn AnyList* AnyList.temp_init(&self, usz initial_capacity = 16) @deprecated("Use tinit") -{ - return self.init(allocator::temp(), initial_capacity) @inline; -} - - <* Initialize the list using the temp allocator. @@ -65,7 +44,7 @@ fn AnyList* AnyList.temp_init(&self, usz initial_capacity = 16) @deprecated("Use *> 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 @@ -88,19 +67,6 @@ fn usz! AnyList.to_format(&self, Formatter* formatter) @dynamic } } -fn String AnyList.to_new_string(&self, Allocator allocator = null) @dynamic -{ - return string::format("%s", *self, allocator: allocator ?: allocator::heap()); -} - - -fn String AnyList.to_string(&self, Allocator allocator) @dynamic -{ - return string::format("%s", *self, allocator: allocator); -} - -fn String AnyList.to_tstring(&self) => string::tformat("%s", *self); - <* Push an element on the list by cloning it. *> @@ -141,35 +107,19 @@ macro AnyList.pop(&self, $Type) Pop the last value and allocate the copy using the given allocator. @return! IteratorResult.NO_MORE_ELEMENT *> -fn any! AnyList.copy_pop(&self, Allocator allocator = allocator::heap()) +fn any! AnyList.copy_pop(&self, Allocator allocator) { if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; defer self.free_element(self.entries[self.size]); return allocator::clone_any(allocator, self.entries[--self.size]); } -<* - Pop the last value and allocate the copy using the given allocator. - @return! IteratorResult.NO_MORE_ELEMENT - @deprecated `use copy_pop` -*> -fn any! AnyList.new_pop(&self, Allocator allocator = allocator::heap()) -{ - return self.copy_pop(allocator); -} - -<* - Pop the last value and allocate the copy using the temp allocator - @return! IteratorResult.NO_MORE_ELEMENT - @deprecated `use tcopy_pop` -*> -fn any! AnyList.temp_pop(&self) => self.copy_pop(allocator::temp()); <* 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() @@ -210,17 +160,9 @@ fn any! AnyList.pop_first_retained(&self) return self.entries[0]; } -<* - Same as new_pop() but pops the first value instead. - @deprecated `use copy_pop_first` -*> -fn any! AnyList.new_pop_first(&self, Allocator allocator = allocator::heap()) -{ - return self.copy_pop_first(allocator) @inline; -} <* - Same as new_pop() but pops the first value instead. + Same as copy_pop() but pops the first value instead. *> fn any! AnyList.copy_pop_first(&self, Allocator allocator = allocator::heap()) { @@ -233,13 +175,7 @@ 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()); - -<* - 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.tcopy_pop_first(&self) => self.copy_pop_first(tmem()); <* @require index < self.size @@ -477,7 +413,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/lib/std/collections/bitset.c3 b/lib/std/collections/bitset.c3 index 3301146ea..128f41622 100644 --- a/lib/std/collections/bitset.c3 +++ b/lib/std/collections/bitset.c3 @@ -1,7 +1,7 @@ <* @require SIZE > 0 *> -module std::collections::bitset(); +module std::collections::bitset{SIZE}; def Type = uint; @@ -70,28 +70,18 @@ fn void BitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline <* @require Type.kindof == UNSIGNED_INT *> -module std::collections::growablebitset(); +module std::collections::growablebitset{Type}; import std::collections::list; const BITS = Type.sizeof * 8; -def GrowableBitSetList = List(); +def GrowableBitSetList = List{Type}; struct GrowableBitSet { GrowableBitSetList data; } -<* - @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()) @deprecated("Use init(mem)") -{ - self.data.init(allocator, initial_capacity); - return self; -} - <* @param initial_capacity @param [&inout] allocator "The allocator to use, defaults to the heap allocator" @@ -102,14 +92,9 @@ fn GrowableBitSet* GrowableBitSet.init(&self, Allocator allocator, usz initial_c return self; } -fn GrowableBitSet* GrowableBitSet.temp_init(&self, usz initial_capacity = 1) @deprecated("Use tinit()") -{ - return self.init(allocator::temp(), initial_capacity) @inline; -} - fn GrowableBitSet* GrowableBitSet.tinit(&self, usz initial_capacity = 1) { - return self.init(allocator::temp(), initial_capacity) @inline; + return self.init(tmem(), initial_capacity) @inline; } fn void GrowableBitSet.free(&self) diff --git a/lib/std/collections/elastic_array.c3 b/lib/std/collections/elastic_array.c3 index db4e45aeb..292d48189 100644 --- a/lib/std/collections/elastic_array.c3 +++ b/lib/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); @@ -39,16 +39,6 @@ fn usz! ElasticArray.to_format(&self, Formatter* formatter) @dynamic } } -fn String ElasticArray.to_string(&self, Allocator allocator) @dynamic -{ - return string::format("%s", *self, allocator: allocator); -} - -fn String ElasticArray.to_new_string(&self, Allocator allocator = nul) @dynamic -{ - return string::format("%s", *self, allocator: allocator ?: allocator::heap()); -} - fn String ElasticArray.to_tstring(&self) { return string::tformat("%s", *self); @@ -160,28 +150,12 @@ fn void ElasticArray.add_array(&self, Type[] array) -<* - IMPORTANT The returned array must be freed using free_aligned. -*> -fn Type[] ElasticArray.to_new_aligned_array(&self) -{ - return list_common::list_to_new_aligned_array(Type, self, allocator::heap()); -} - <* IMPORTANT The returned array must be freed using free_aligned. *> fn Type[] ElasticArray.to_aligned_array(&self, Allocator allocator) { - return list_common::list_to_new_aligned_array(Type, self, allocator); -} - -<* - @require !type_is_overaligned() : "This function is not available on overaligned types" -*> -macro Type[] ElasticArray.to_new_array(&self) -{ - return list_common::list_to_array(Type, self, allocator::heap()); + return list_common::list_to_aligned_array(Type, self, allocator); } <* @@ -189,15 +163,15 @@ macro Type[] ElasticArray.to_new_array(&self) *> macro Type[] ElasticArray.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[] 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/lib/std/collections/enummap.c3 b/lib/std/collections/enummap.c3 index 268ed2179..98b7974b0 100644 --- a/lib/std/collections/enummap.c3 +++ b/lib/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) { @@ -28,21 +28,6 @@ fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic return n; } -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); -} - <* @return "The total size of this map, which is the same as the number of enum values" @pure diff --git a/lib/std/collections/enumset.c3 b/lib/std/collections/enumset.c3 index 3821ebd71..68cf9d807 100644 --- a/lib/std/collections/enumset.c3 +++ b/lib/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/lib/std/collections/hashmap.c3 b/lib/std/collections/hashmap.c3 index 26971aa5d..a033555af 100644 --- a/lib/std/collections/hashmap.c3 +++ b/lib/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) @deprecated("Use init(mem)") -{ - return self.init(allocator ?: allocator::heap(), capacity, load_factor); -} <* @param [&inout] allocator "The allocator to use" @@ -46,17 +50,6 @@ fn HashMap* HashMap.init(&self, Allocator allocator, uint capacity = DEFAULT_INI return self; } -<* - @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(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) @deprecated("Use tinit()") -{ - return self.init(allocator::temp(), capacity, load_factor) @inline; -} - <* @require capacity > 0 "The capacity must be 1 or higher" @require load_factor > 0.0 "The load factor must be higher than 0" @@ -65,7 +58,7 @@ fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, f *> 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; } <* @@ -76,48 +69,11 @@ fn HashMap* HashMap.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float @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()) @deprecated("Use init_with_key_values(mem)") +macro HashMap* HashMap.init_with_key_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) { - self.init(capacity, load_factor, allocator); - $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.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()) @deprecated("Use init_from_keys_and_values(mem)") -{ - assert(keys.len == values.len); self.init(allocator, capacity, load_factor); - for (usz i = 0; i < keys.len; i++) - { - self.set(keys[i], values[i]); - } - 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) @deprecated("Use tinit_with_key_values") -{ - self.tinit(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; } @@ -131,11 +87,7 @@ macro HashMap* HashMap.temp_init_with_key_values(&self, ..., uint capacity = DEF *> macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) { - self.tinit(capacity, load_factor); - $for (var $i = 0; $i < $vacount; $i += 2) - self.set($vaarg[$i], $vaarg[$i+1]); - $endfor - return self; + return self.tinit_with_key_values(tmem(), capacity, load_factor); } <* @@ -148,10 +100,10 @@ macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT @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()) @deprecated("Use tinit_from_keys_and_values") +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.tinit(capacity, load_factor); + self.init(allocator, capacity, load_factor); for (usz i = 0; i < keys.len; i++) { self.set(keys[i], values[i]); @@ -159,25 +111,19 @@ fn HashMap* HashMap.temp_init_from_keys_and_values(&self, Key[] keys, Value[] va 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.tinit_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.tinit(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); } <* @@ -191,14 +137,6 @@ 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) @deprecated("Use init_from_map(mem, map)") -{ - return self.init_from_map(allocator::heap(), other_map) @inline; -} - <* @param [&inout] allocator "The allocator to use" @param [&in] other_map "The map to copy from." @@ -210,20 +148,12 @@ fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map return self; } -<* - @param [&in] other_map "The map to copy from." -*> -fn HashMap* HashMap.temp_init_from_map(&map, HashMap* other_map) @deprecated("Use tinit_from_map") -{ - return map.init_from_map(allocator::temp(), other_map) @inline; -} - <* @param [&in] other_map "The map to copy from." *> fn HashMap* HashMap.tinit_from_map(&map, HashMap* other_map) { - return map.init_from_map(allocator::temp(), other_map) @inline; + return map.init_from_map(tmem(), other_map) @inline; } fn bool HashMap.is_empty(&map) @inline @@ -296,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.init(allocator::heap()); + map.tinit(); } uint hash = rehash(key.hash()); uint index = index_for(hash, map.table.len); @@ -345,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) { @@ -386,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) { @@ -667,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/lib/std/collections/linkedlist.c3 b/lib/std/collections/linkedlist.c3 index 3f50a949d..921813c0a 100644 --- a/lib/std/collections/linkedlist.c3 +++ b/lib/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,23 +30,9 @@ fn LinkedList* LinkedList.init(&self, Allocator allocator) return self; } -<* - @return "the initialized list" -*> -fn LinkedList* LinkedList.new_init(&self) @deprecated("Use init(mem)") -{ - return self.init(allocator::heap()) @inline; -} - - -fn LinkedList* LinkedList.temp_init(&self) @deprecated("Use tinit()") -{ - return self.init(allocator::temp()) @inline; -} - fn LinkedList* LinkedList.tinit(&self) { - return self.init(allocator::temp()) @inline; + return self.init(tmem()) @inline; } <* @@ -59,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/lib/std/collections/list.c3 b/lib/std/collections/list.c3 index 010344bd7..c2e1c6bb2 100644 --- a/lib/std/collections/list.c3 +++ b/lib/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,29 +33,6 @@ 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()) @deprecated("Use init(mem)") -{ - 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) @deprecated("Use tinit()") -{ - return self.init(allocator::temp(), initial_capacity) @inline; -} <* Initialize the list using the temp allocator. @@ -64,18 +41,7 @@ fn List* List.temp_init(&self, usz initial_capacity = 16) @deprecated("Use tinit *> fn List* List.tinit(&self, usz initial_capacity = 16) { - return self.init(allocator::temp(), initial_capacity) @inline; -} - -<* - Initialize a new list with an array. - - @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()) @deprecated("Use init_with_array(mem)") -{ - return self.init_with_array(allocator, values); + return self.init(tmem(), initial_capacity) @inline; } <* @@ -91,19 +57,6 @@ fn List* List.init_with_array(&self, Allocator allocator, Type[] values) return self; } -<* - Initialize a temporary list with an array. - - @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) @deprecated("Use tinit_with_array()") -{ - self.tinit(values.len) @inline; - self.add_array(values) @inline; - return self; -} - <* Initialize a temporary list with an array. @@ -120,7 +73,7 @@ fn List* List.tinit_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; @@ -148,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); @@ -208,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; } @@ -398,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/lib/std/collections/list_common.c3 b/lib/std/collections/list_common.c3 index dccbf7afa..021fa1fac 100644 --- a/lib/std/collections/list_common.c3 +++ b/lib/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/lib/std/collections/map.c3 b/lib/std/collections/map.c3 index bb5c1077c..e69de29bb 100644 --- a/lib/std/collections/map.c3 +++ b/lib/std/collections/map.c3 @@ -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/lib/std/collections/maybe.c3 b/lib/std/collections/maybe.c3 index 3357683b4..dd5dea31f 100644 --- a/lib/std/collections/maybe.c3 +++ b/lib/std/collections/maybe.c3 @@ -1,4 +1,4 @@ -module std::collections::maybe(); +module std::collections::maybe{Type}; import std::io; struct Maybe (Printable) @@ -28,16 +28,6 @@ fn Maybe value(Type val) return { .value = val, .has_value = true }; } -fn Maybe Maybe.with_value(Type val) @deprecated("Use maybe::value instead.") @operator(construct) -{ - return { .value = val, .has_value = true }; -} - -fn Maybe Maybe.empty() @deprecated("Use maybe::EMPTY instead.") @operator(construct) -{ - return { }; -} - const Maybe EMPTY = { }; macro Type! Maybe.get(self) diff --git a/lib/std/collections/object.c3 b/lib/std/collections/object.c3 index 34c9ae446..b2ce81704 100644 --- a/lib/std/collections/object.c3 +++ b/lib/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)!; @@ -462,7 +462,7 @@ fn Object* Object.get_or_create_obj(&self, String key) return container; } -def ObjectInternalMap = HashMap() @private; -def ObjectInternalList = List() @private; -def ObjectInternalMapEntry = Entry() @private; +def ObjectInternalMap = HashMap{String, Object*} @private; +def ObjectInternalList = List{Object*} @private; +def ObjectInternalMapEntry = Entry{String, Object*} @private; diff --git a/lib/std/collections/priorityqueue.c3 b/lib/std/collections/priorityqueue.c3 index 6fd1a58dc..4a244292f 100644 --- a/lib/std/collections/priorityqueue.c3 +++ b/lib/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 PrivatePriorityQueue{Type, false}; +distinct PriorityQueueMax = inline PrivatePriorityQueue{Type, true}; -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.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.init(allocator, initial_capacity); -} - -fn void PrivatePriorityQueue.temp_init(&self, usz initial_capacity = 16) @inline -{ - self.heap.init(allocator::temp(), initial_capacity) @inline; + self.init(tmem(), initial_capacity); + return self; } @@ -72,6 +67,9 @@ 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) @@ -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/lib/std/collections/range.c3 b/lib/std/collections/range.c3 index 98917a167..e689856ef 100644 --- a/lib/std/collections/range.c3 +++ b/lib/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/lib/std/collections/ringbuffer.c3 b/lib/std/collections/ringbuffer.c3 index 38ae9e723..d3213d770 100644 --- a/lib/std/collections/ringbuffer.c3 +++ b/lib/std/collections/ringbuffer.c3 @@ -1,11 +1,14 @@ <* - @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{Type}; +import std::io; -struct RingBuffer +def Element = $typeof((Type){}[0]); + +struct RingBuffer (Printable) { - Type[SIZE] buf; + Type buf; usz written; usz head; } @@ -15,9 +18,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 < self.buf.len) { self.buf[self.written] = c; self.written++; @@ -25,14 +28,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) % self.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 %= self.buf.len; + usz avail = self.buf.len - self.head; if (index < avail) { return self.buf[self.head + index]; @@ -40,25 +43,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 < self.buf.len: self.written--; return self.buf[self.written]; default: - self.head = (self.head - 1) % SIZE; + self.head = (self.head - 1) % self.buf.len; return self.buf[self.head]; } } -fn usz RingBuffer.read(&self, usz index, Type[] buffer) +fn usz! RingBuffer.to_format(&self, Formatter* format) @dynamic { - index %= SIZE; - if (self.written < SIZE) + // Improve this? + return format.printf("%s", self.buf); +} + +fn usz RingBuffer.read(&self, usz index, Element[] buffer) +{ + index %= self.buf.len; + if (self.written < self.buf.len) { if (index >= self.written) return 0; usz end = self.written - index; @@ -66,7 +75,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 = self.buf.len - self.head; if (index >= end) { index -= end; @@ -75,13 +84,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 <= self.buf.len - index) { usz n = buffer.len; buffer[:n] = self.buf[self.head + index:n]; return n; } - usz n1 = SIZE - index; + usz n1 = self.buf.len - index; buffer[:n1] = self.buf[self.head + index:n1]; buffer = buffer[n1..]; index -= n1; @@ -90,10 +99,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 < self.buf.len && i < buffer.len) { self.buf[self.written] = buffer[i++]; self.written++; @@ -101,6 +110,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) % self.buf.len; } } \ No newline at end of file diff --git a/lib/std/collections/tuple.c3 b/lib/std/collections/tuple.c3 index d8869e844..597605e35 100644 --- a/lib/std/collections/tuple.c3 +++ b/lib/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/lib/std/compression/qoi.c3 b/lib/std/compression/qoi.c3 index 1b4a1fb09..b896b7e6d 100644 --- a/lib/std/compression/qoi.c3 +++ b/lib/std/compression/qoi.c3 @@ -74,18 +74,10 @@ import std::io; fn usz! write(String filename, char[] input, QOIDesc* desc) => @pool() { // encode data - char[] output = new_encode(input, desc, allocator: allocator::temp())!; + char[] output = encode(tmem(), input, desc)!; - // open file - File! f = file::open(filename, "wb"); - if (catch f) return QOIError.FILE_OPEN_FAILED?; - - // write data to file and close it - usz! written = f.write(output); - if (catch written) return QOIError.FILE_WRITE_FAILED?; - if (catch f.close()) return QOIError.FILE_WRITE_FAILED?; - - return written; + file::save(filename, output)!; + return output.len; } @@ -110,29 +102,20 @@ fn usz! write(String filename, char[] input, QOIDesc* desc) => @pool() @param [&out] desc `The descriptor to fill with the image's info` @param channels `The channels to be used` *> -fn char[]! new_read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) => @pool(allocator) +fn char[]! read(Allocator allocator, String filename, QOIDesc* desc, QOIChannels channels = AUTO) => @pool(allocator) { // read file char[] data = file::load_temp(filename) ?? QOIError.FILE_OPEN_FAILED?!; // pass data to decode function - return new_decode(data, desc, channels, allocator); + return decode(allocator, data, desc, channels); } -fn char[]! read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) @deprecated("Use new_read") -{ - return new_read(filename, desc, channels, allocator); -} // Back to basic non-stdio mode module std::compression::qoi; import std::bits; -fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap()) @deprecated("use encode_new") -{ - return new_encode(input, desc, allocator); -} - <* Encode raw RGB or RGBA pixels into a QOI image in memory. @@ -146,7 +129,7 @@ fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator:: @param [in] input `The raw RGB or RGBA pixels to encode` @param [&in] desc `The descriptor of the image` *> -fn char[]! new_encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap()) @nodiscard +fn char[]! encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard { // check info in desc if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_PARAMETERS?; @@ -278,10 +261,6 @@ fn char[]! new_encode(char[] input, QOIDesc* desc, Allocator allocator = allocat } -fn char[]! decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) -{ - return new_decode(data, desc, channels, allocator); -} <* Decode a QOI image from memory. @@ -304,7 +283,7 @@ fn char[]! decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Alloc @param [&out] desc `The descriptor to fill with the image's info` @param channels `The channels to be used` *> -fn char[]! new_decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) @nodiscard +fn char[]! decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels channels = AUTO) @nodiscard { // check input data if (data.len < Header.sizeof + END_OF_STREAM.len) return QOIError.INVALID_DATA?; diff --git a/lib/std/core/allocators/heap_allocator.c3 b/lib/std/core/allocators/heap_allocator.c3 index ccd18c250..3ea8e126a 100644 --- a/lib/std/core/allocators/heap_allocator.c3 +++ b/lib/std/core/allocators/heap_allocator.c3 @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved. +// Copyright (c) 2021-2025 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. diff --git a/lib/std/core/allocators/tracking_allocator.c3 b/lib/std/core/allocators/tracking_allocator.c3 index 84cffa334..9f1366496 100644 --- a/lib/std/core/allocators/tracking_allocator.c3 +++ b/lib/std/core/allocators/tracking_allocator.c3 @@ -13,7 +13,7 @@ struct Allocation void*[MAX_BACKTRACE] backtrace; } -def AllocMap = HashMap(); +def AllocMap = HashMap { uptr, Allocation }; // A simple tracking allocator. // It tracks allocations using a hash map but @@ -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/lib/std/core/array.c3 b/lib/std/core/array.c3 index abb808e34..6dd9cdc67 100644 --- a/lib/std/core/array.c3 +++ b/lib/std/core/array.c3 @@ -26,7 +26,7 @@ macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0) if (xlen < 1) xlen = $typeof((*array_ptr)[0]).len + xlen; if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen; var $ElementType = $typeof((*array_ptr)[0][0]); - return Slice2d(<$ElementType>) { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen }; + return Slice2d{$ElementType} { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen }; } @@ -56,7 +56,7 @@ macro rindex_of(array, element) @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type" @ensure result.len == arr1.len + arr2.len *> -macro concat(arr1, arr2, Allocator allocator) @nodiscard +macro concat(Allocator allocator, arr1, arr2) @nodiscard { var $Type = $typeof(arr1[0]); $Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len); @@ -70,22 +70,6 @@ macro concat(arr1, arr2, Allocator allocator) @nodiscard } return result; } -<* - Concatenate two arrays or slices, returning a slice containing the concatenation of them. - - @param [in] arr1 - @param [in] arr2 - @param [&inout] allocator "The allocator to use, default is the heap allocator" - @require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY - @require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY - @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type" - @ensure return.len == arr1.len + arr2.len -*> -macro concat_new(arr1, arr2, Allocator allocator = allocator::heap()) @nodiscard -{ - return concat(arr1, arr2, allocator); -} - <* Concatenate two arrays or slices, returning a slice containing the concatenation of them, allocated using the temp allocator. @@ -97,9 +81,9 @@ macro concat_new(arr1, arr2, Allocator allocator = allocator::heap()) @nodiscard @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type" @ensure return.len == arr1.len + arr2.len *> -macro tconcat(arr1, arr2) @nodiscard => concat(arr1, arr2, allocator::temp()); +macro tconcat(arr1, arr2) @nodiscard => concat(allocator::temp(), arr1, arr2); -module std::core::array::slice(); +module std::core::array::slice{Type}; struct Slice2d { diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index e0e39e80d..4e01a6b7e 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -4,6 +4,31 @@ module std::core::builtin; import libc, std::hash, std::io, std::os::backtrace; +<* + EMPTY_MACRO_SLOT is a value used for implementing optional arguments for macros in an efficient + way. It relies on the fact that distinct types are not implicitly convertable. + + You can use `@is_empty_macro_slot()` and `@is_valid_macro_slot()` to figure out whether + the argument has been used or not. + + An example: + +```c3 +macro foo(a, #b = EMPTY_MACRO_SLOT) +{ + $if @is_valid_macro_slot(#b): + return invoke_foo2(a, #b); + $else + return invoke_foo1(a); + $endif +} +*> +const EmptySlot EMPTY_MACRO_SLOT @builtin = null; + +distinct EmptySlot = void*; +macro @is_empty_macro_slot(#arg) @const @builtin => @typeis(#arg, EmptySlot); +macro @is_valid_macro_slot(#arg) @const @builtin => !@typeis(#arg, EmptySlot); + /* Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds. */ @@ -67,7 +92,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(tmem(), backtraces); if (catch backtrace) return false; if (backtrace.len() <= backtraces_to_ignore) return false; io::eprint("\nERROR: '"); @@ -413,10 +438,6 @@ macro uint String.hash(String c) => (uint)fnv32a::encode(c); macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c); macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr)); -distinct EmptySlot = void*; -const EmptySlot EMPTY_MACRO_SLOT @builtin = null; -macro @is_empty_macro_slot(#arg) @builtin => @typeis(#arg, EmptySlot); -macro @is_valid_macro_slot(#arg) @builtin => !@typeis(#arg, EmptySlot); const MAX_FRAMEADDRESS = 128; <* diff --git a/lib/std/core/dstring.c3 b/lib/std/core/dstring.c3 index 74d456370..0bb453343 100644 --- a/lib/std/core/dstring.c3 +++ b/lib/std/core/dstring.c3 @@ -6,19 +6,6 @@ distinct DStringOpaque = void; const usz MIN_CAPACITY @private = 16; -<* - @require !self.data() "String already initialized" -*> -fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)") -{ - if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; - StringData* data = allocator::alloc_with_padding(allocator, StringData, capacity)!!; - data.allocator = allocator; - data.len = 0; - data.capacity = capacity; - return *self = (DString)data; -} - <* @require !self.data() "String already initialized" *> @@ -32,35 +19,25 @@ fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY) return *self = (DString)data; } -<* - @require !self.data() "String already initialized" -*> -fn DString DString.temp_init(&self, usz capacity = MIN_CAPACITY) @deprecated("Use tinit()") -{ - self.init(allocator::temp(), capacity) @inline; - return *self; -} - <* @require !self.data() "String already initialized" *> fn DString DString.tinit(&self, usz capacity = MIN_CAPACITY) { - self.init(allocator::temp(), capacity) @inline; - return *self; + return self.init(tmem(), capacity) @inline; } -fn DString new_with_capacity(usz capacity, Allocator allocator = allocator::heap()) +fn DString new_with_capacity(Allocator allocator, usz capacity) { return (DString){}.init(allocator, capacity); } -fn DString temp_with_capacity(usz capacity) => new_with_capacity(capacity, allocator::temp()) @inline; +fn DString temp_with_capacity(usz capacity) => new_with_capacity(tmem(), capacity) @inline; -fn DString new(String c = "", Allocator allocator = allocator::heap()) +fn DString new(Allocator allocator, String c = "") { usz len = c.len; - StringData* data = (StringData*)new_with_capacity(len, allocator); + StringData* data = (StringData*)new_with_capacity(allocator, len); if (len) { data.len = len; @@ -69,7 +46,7 @@ fn DString new(String c = "", Allocator allocator = allocator::heap()) return (DString)data; } -fn DString temp_new(String s = "") => new(s, allocator::temp()) @inline; +fn DString temp(String s = "") => new(tmem(), s) @inline; fn void DString.replace_char(self, char ch, char replacement) @@ -121,15 +98,6 @@ fn void DString.replace(&self, String needle, String replacement) }; } -fn DString DString.new_concat(self, DString b, Allocator allocator = allocator::heap()) @deprecated("Use concat(mem)") -{ - DString string; - string.init(allocator, self.len() + b.len()); - string.append(self); - string.append(b); - return string; -} - fn DString DString.concat(self, Allocator allocator, DString b) { DString string; @@ -139,7 +107,7 @@ fn DString DString.concat(self, Allocator allocator, DString b) return string; } -fn DString DString.temp_concat(self, DString b) => self.concat(allocator::temp(), b); +fn DString DString.tconcat(self, DString b) => self.concat(tmem(), b); fn ZString DString.zstr_view(&self) { @@ -249,23 +217,18 @@ fn usz DString.append_char32(&self, Char32 c) return n; } -fn DString DString.tcopy(&self) => self.copy(allocator::temp()); +fn DString DString.tcopy(&self) => self.copy(tmem()); -fn DString DString.copy(self, Allocator allocator = null) +fn DString DString.copy(self, Allocator allocator) { - if (!self) - { - if (allocator) return new_with_capacity(0, allocator); - return (DString)null; - } + if (!self) return new(allocator); StringData* data = self.data(); - if (!allocator) allocator = allocator::heap(); - DString new_string = new_with_capacity(data.capacity, allocator); + DString new_string = new_with_capacity(allocator, data.capacity); mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len); return new_string; } -fn ZString DString.copy_zstr(self, Allocator allocator = allocator::heap()) +fn ZString DString.copy_zstr(self, Allocator allocator) { usz str_len = self.len(); if (!str_len) @@ -279,12 +242,12 @@ fn ZString DString.copy_zstr(self, Allocator allocator = allocator::heap()) return (ZString)zstr; } -fn String DString.copy_str(self, Allocator allocator = allocator::heap()) +fn String DString.copy_str(self, Allocator allocator) { return (String)self.copy_zstr(allocator)[:self.len()]; } -fn String DString.tcopy_str(self) => self.copy_str(allocator::temp()) @inline; +fn String DString.tcopy_str(self) => self.copy_str(tmem()) @inline; fn bool DString.equals(self, DString other_string) { @@ -334,7 +297,7 @@ fn void DString.append_chars(&self, String str) if (!other_len) return; if (!*self) { - *self = new(str); + *self = temp(str); return; } self.reserve(other_len); @@ -343,9 +306,9 @@ fn void DString.append_chars(&self, String str) data.len += other_len; } -fn Char32[] DString.copy_utf32(&self, Allocator allocator = allocator::heap()) +fn Char32[] DString.copy_utf32(&self, Allocator allocator) { - return self.str_view().to_utf32(allocator) @inline!!; + return self.str_view().to_utf32_copy(allocator) @inline!!; } fn void DString.append_string(&self, DString str) @@ -376,7 +339,7 @@ fn void DString.append_char(&self, char c) { if (!*self) { - *self = new_with_capacity(MIN_CAPACITY); + *self = temp_with_capacity(MIN_CAPACITY); } self.reserve(1); StringData* data = self.data(); @@ -574,7 +537,7 @@ macro void DString.insert_at(&self, usz index, value) fn usz! DString.appendf(&self, String format, args...) @maydiscard { - if (!self.data()) self.init(mem, format.len + 20); + if (!self.data()) self.tinit(format.len + 20); @pool(self.data().allocator) { Formatter formatter; @@ -585,7 +548,7 @@ fn usz! DString.appendf(&self, String format, args...) @maydiscard fn usz! DString.appendfn(&self, String format, args...) @maydiscard { - if (!self.data()) self.init(mem, format.len + 20); + if (!self.data()) self.tinit(format.len + 20); @pool(self.data().allocator) { Formatter formatter; @@ -596,15 +559,15 @@ fn usz! DString.appendfn(&self, String format, args...) @maydiscard }; } -fn DString new_join(String[] s, String joiner, Allocator allocator = allocator::heap()) +fn DString join(Allocator allocator, String[] s, String joiner) { - if (!s.len) return (DString)null; + if (!s.len) return new(allocator); usz total_size = joiner.len * s.len; foreach (String* &str : s) { total_size += str.len; } - DString res = new_with_capacity(total_size, allocator); + DString res = new_with_capacity(allocator, total_size); res.append(s[0]); foreach (String* &str : s[1..]) { @@ -644,7 +607,7 @@ fn void DString.reserve(&self, usz addition) StringData* data = self.data(); if (!data) { - *self = dstring::new_with_capacity(addition); + *self = dstring::temp_with_capacity(addition); return; } usz len = data.len + addition; diff --git a/lib/std/core/mem_allocator.c3 b/lib/std/core/mem_allocator.c3 index aecdb270c..9f832fad4 100644 --- a/lib/std/core/mem_allocator.c3 +++ b/lib/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/lib/std/core/private/main_stub.c3 b/lib/std/core/private/main_stub.c3 index f64ded9e2..1cdf2d4f8 100644 --- a/lib/std/core/private/main_stub.c3 +++ b/lib/std/core/private/main_stub.c3 @@ -80,7 +80,7 @@ macro String[] wargs_strings(int argc, Char16** argv) @private { Char16* arg = argv[i]; Char16[] argstring = arg[:_strlen(arg)]; - list[i] = string::new_from_utf16(argstring) ?? "?".copy(); + list[i] = string::new_from_utf16(mem, argstring) ?? "?".copy(mem); } return list[:argc]; } diff --git a/lib/std/core/runtime_benchmark.c3 b/lib/std/core/runtime_benchmark.c3 index e9eb8d611..f8f56a7d7 100644 --- a/lib/std/core/runtime_benchmark.c3 +++ b/lib/std/core/runtime_benchmark.c3 @@ -1,8 +1,7 @@ module std::core::runtime; import libc, std::time, std::io, std::sort; -def BenchmarkFn = fn void!() @if($$OLD_TEST); -def BenchmarkFn = fn void() @if(!$$OLD_TEST); +def BenchmarkFn = fn void(); struct BenchmarkUnit { @@ -10,7 +9,7 @@ struct BenchmarkUnit BenchmarkFn func; } -fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator = allocator::heap()) +fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator) { BenchmarkFn[] fns = $$BENCHMARK_FNS; String[] names = $$BENCHMARK_NAMES; @@ -39,80 +38,7 @@ fn void set_benchmark_max_iterations(uint value) @builtin benchmark_max_iterations = value; } -fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if($$OLD_TEST) -{ - int benchmarks_passed = 0; - int benchmark_count = benchmarks.len; - usz max_name; - - foreach (&unit : benchmarks) - { - if (max_name < unit.name.len) max_name = unit.name.len; - } - - usz len = max_name + 9; - - DString name = dstring::temp_with_capacity(64); - name.append_repeat('-', len / 2); - name.append(" BENCHMARKS "); - name.append_repeat('-', len - len / 2); - - io::printn(name); - - name.clear(); - - long sys_clock_started; - long sys_clock_finished; - long sys_clocks; - Clock clock; - anyfault err; - - foreach(unit : benchmarks) - { - defer name.clear(); - name.appendf("Benchmarking %s ", unit.name); - name.append_repeat('.', max_name - unit.name.len + 2); - io::printf("%s ", name.str_view()); - - for (uint i = 0; i < benchmark_warmup_iterations; i++) - { - err = @catch(unit.func()) @inline; - @volatile_load(err); - } - - clock = std::time::clock::now(); - sys_clock_started = $$sysclock(); - - for (uint i = 0; i < benchmark_max_iterations; i++) - { - err = @catch(unit.func()) @inline; - @volatile_load(err); - } - - sys_clock_finished = $$sysclock(); - NanoDuration nano_seconds = clock.mark(); - sys_clocks = sys_clock_finished - sys_clock_started; - - if (err) - { - io::printfn("[failed] Failed due to: %s", err); - continue; - } - - io::printfn("[ok] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations); - benchmarks_passed++; - } - - io::printfn("\n%d benchmark%s run.\n", benchmark_count, benchmark_count > 1 ? "s" : ""); - io::printfn("Benchmarks Result: %s. %d passed, %d failed.", - benchmarks_passed < benchmark_count ? "FAILED" : "ok", - benchmarks_passed, - benchmark_count - benchmarks_passed); - - return benchmark_count == benchmarks_passed; -} - -fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if(!$$OLD_TEST) +fn bool run_benchmarks(BenchmarkUnit[] benchmarks) { usz max_name; @@ -170,5 +96,5 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if(!$$OLD_TEST) fn bool default_benchmark_runner(String[] args) => @pool() { - return run_benchmarks(benchmark_collection_create(allocator::temp())); + return run_benchmarks(benchmark_collection_create(tmem())); } diff --git a/lib/std/core/runtime_test.c3 b/lib/std/core/runtime_test.c3 index 4d2476eb5..94b8b8b77 100644 --- a/lib/std/core/runtime_test.c3 +++ b/lib/std/core/runtime_test.c3 @@ -7,8 +7,7 @@ import std::core::mem::allocator @public; import libc, std::time, std::io, std::sort; import std::os::env; -def TestFn = fn void!() @if($$OLD_TEST); -def TestFn = fn void() @if(!$$OLD_TEST); +def TestFn = fn void(); TestContext* test_context @private; @@ -47,7 +46,7 @@ struct TestUnit TestFn func; } -fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap()) +fn TestUnit[] test_collection_create(Allocator allocator) { TestFn[] fns = $$TEST_FNS; String[] names = $$TEST_NAMES; @@ -75,7 +74,8 @@ fn int cmp_test_unit(TestUnit a, TestUnit b) fn bool terminal_has_ansi_codes() @local => @pool() { - if (try v = env::get_var_temp("TERM")) + + if (try v = env::tget_var("TERM")) { if (v.contains("xterm") || v.contains("vt100") || v.contains("screen")) return true; } @@ -278,15 +278,7 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private mute_output(); mem.clear(); if (check_leaks) allocator::thread_allocator = &mem; - $if(!$$OLD_TEST): - unit.func(); - $else - if (catch err = unit.func()) - { - io::printf("[FAIL] Failed due to: %s", err); - continue; - } - $endif + unit.func(); // track cleanup that may take place in teardown_fn if (context.teardown_fn) { @@ -297,7 +289,7 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private unmute_output(false); // all good, discard output if (mem.has_leaks()) { - if(context.is_quiet_mode) io::printf("\n%s ", context.current_test_name); + if (context.is_quiet_mode) io::printf("\n%s ", context.current_test_name); io::print(context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]"); io::printn(" LEAKS DETECTED!"); mem.print_report(); @@ -337,6 +329,6 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private fn bool default_test_runner(String[] args) => @pool() { assert(test_context == null, "test suite is already running"); - return run_tests(args, test_collection_create(allocator::temp())); + return run_tests(args, test_collection_create(tmem())); } diff --git a/lib/std/core/string.c3 b/lib/std/core/string.c3 index 0a849ecc9..2980b56fc 100644 --- a/lib/std/core/string.c3 +++ b/lib/std/core/string.c3 @@ -52,20 +52,13 @@ fn ZString tformat_zstr(String fmt, args...) @param [inout] allocator `The allocator to use` @param [in] fmt `The formatting string` *> -fn String format(String fmt, args..., Allocator allocator) => @pool(allocator) +fn String format(Allocator allocator, String fmt, args...) => @pool(allocator) { DString str = dstring::temp_with_capacity(fmt.len + args.len * 8); str.appendf(fmt, ...args); return str.copy_str(allocator); } -<* - Return a heap allocated String created using the formatting function. - - @param [in] fmt `The formatting string` -*> -fn String new_format(String fmt, args..., Allocator allocator = null) => format(fmt, ...args, allocator: allocator ?: allocator::heap()); - <* Return a temporary String created using the formatting function. @@ -78,19 +71,6 @@ fn String tformat(String fmt, args...) return str.str_view(); } -<* - Return a new ZString created using the formatting function. - - @param [in] fmt `The formatting string` - @param [inout] allocator `The allocator to use` -*> -fn ZString new_format_zstr(String fmt, args..., Allocator allocator = allocator::heap()) => @pool(allocator) -{ - DString str = dstring::temp_with_capacity(fmt.len + args.len * 8); - str.appendf(fmt, ...args); - return str.copy_zstr(allocator); -} - <* Check if a character is in a set. @@ -105,7 +85,7 @@ macro bool char_in_set(char c, String set) return false; } -fn String join_new(String[] s, String joiner, Allocator allocator = allocator::heap()) +fn String join(Allocator allocator, String[] s, String joiner) { if (!s) { @@ -246,7 +226,7 @@ fn String String.strip_end(string, String needle) @require needle.len > 0 "The needle must be at least 1 character long" @ensure return.len > 0 *> -fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = allocator::heap(), bool skip_empty = false) +fn String[] String.split(s, Allocator allocator, String needle, usz max = 0, bool skip_empty = false) { usz capacity = 16; usz i = 0; @@ -281,18 +261,6 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = al return holder[:i]; } -<* - Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }, using the heap allocator - to store the parts. - - @param [in] s - @param [in] needle - @param max "Max number of elements, 0 means no limit, defaults to 0" - @param skip_empty "True to skip empty elements" - @require needle.len > 0 "The needle must be at least 1 character long" - @ensure return.len > 0 -*> -fn String[] String.new_split(s, String needle, usz max = 0, bool skip_empty) => s.split(needle, max, allocator::heap(), skip_empty) @inline; <* This function is identical to String.split, but implicitly uses the @@ -303,7 +271,7 @@ fn String[] String.new_split(s, String needle, usz max = 0, bool skip_empty) => @param max "Max number of elements, 0 means no limit, defaults to 0" @param skip_empty "True to skip empty elements" *> -fn String[] String.tsplit(s, String needle, usz max = 0, bool skip_empty = false) => s.split(needle, max, allocator::temp(), skip_empty) @inline; +fn String[] String.tsplit(s, String needle, usz max = 0, bool skip_empty = false) => s.split(tmem(), needle, max, skip_empty) @inline; fault SplitResult { BUFFER_EXCEEDED } @@ -521,7 +489,7 @@ fn usz ZString.len(str) } -fn ZString String.zstr_copy(s, Allocator allocator = allocator::heap()) +fn ZString String.zstr_copy(s, Allocator allocator) { usz len = s.len; char* str = allocator::malloc(allocator, len + 1); @@ -530,7 +498,7 @@ fn ZString String.zstr_copy(s, Allocator allocator = allocator::heap()) return (ZString)str; } -fn String String.concat(s1, String s2, Allocator allocator = allocator::heap()) +fn String String.concat(s1, Allocator allocator, String s2) { usz full_len = s1.len + s2.len; char* str = allocator::malloc(allocator, full_len + 1); @@ -541,17 +509,17 @@ fn String String.concat(s1, String s2, Allocator allocator = allocator::heap()) return (String)str[:full_len]; } -fn String String.tconcat(s1, String s2) => s1.concat(s2, allocator::temp()); +fn String String.tconcat(s1, String s2) => s1.concat(tmem(), s2); -fn ZString String.zstr_tcopy(s) => s.zstr_copy(allocator::temp()) @inline; +fn ZString String.zstr_tcopy(s) => s.zstr_copy(tmem()) @inline; <* Copy this string, by duplicating the string, always adding a zero byte sentinel, so that it safely can be converted to a ZString by a cast. *> -fn String String.copy(s, Allocator allocator = allocator::heap()) +fn String String.copy(s, Allocator allocator) { usz len = s.len; char* str = allocator::malloc(allocator, len + 1); @@ -560,23 +528,23 @@ fn String String.copy(s, Allocator allocator = allocator::heap()) return (String)str[:len]; } -fn void String.free(&s, Allocator allocator = allocator::heap()) +fn void String.free(&s, Allocator allocator) { if (!s.ptr) return; allocator::free(allocator, s.ptr); *s = ""; } -fn String String.tcopy(s) => s.copy(allocator::temp()) @inline; +fn String String.tcopy(s) => s.copy(tmem()) @inline; -fn String ZString.copy(z, Allocator allocator = allocator::heap()) +fn String ZString.copy(z, Allocator allocator) { return z.str_view().copy(allocator) @inline; } fn String ZString.tcopy(z) { - return z.str_view().copy(allocator::temp()) @inline; + return z.str_view().copy(tmem()) @inline; } <* @@ -585,7 +553,7 @@ fn String ZString.tcopy(z) @return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence" @return! AllocationFailure "If allocation of the string fails" *> -fn Char16[]! String.to_new_utf16(s, Allocator allocator = allocator::heap()) +fn Char16[]! String.to_utf16_copy(s, Allocator allocator) { usz len16 = conv::utf16len_for_utf8(s); Char16* data = allocator::alloc_array_try(allocator, Char16, len16 + 1)!; @@ -594,26 +562,16 @@ fn Char16[]! String.to_new_utf16(s, Allocator allocator = allocator::heap()) return data[:len16]; } -<* - Convert an UTF-8 string to UTF-16 - @return "The UTF-16 string as a slice, allocated using the given allocator" - @return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence" - @return! AllocationFailure "If allocation of the string fails" -*> -fn Char16[]! String.to_temp_utf16(s) +fn Char16[]! String.to_utf16_tcopy(s) => s.to_utf16_copy(tmem()); + +fn WString! String.to_wstring_copy(s, Allocator allocator) { - return s.to_new_utf16(allocator::temp()); + return (WString)s.to_utf16_copy(allocator).ptr; } -fn WString! String.to_wstring(s, Allocator allocator) -{ - return (WString)s.to_new_utf16(allocator).ptr; -} +fn WString! String.to_wstring_tcopy(s) => s.to_wstring_copy(tmem()); -fn WString! String.to_temp_wstring(s) => s.to_wstring(allocator::temp()); -fn WString! String.to_new_wstring(s) => s.to_wstring(allocator::heap()); - -fn Char32[]! String.to_utf32(s, Allocator allocator) +fn Char32[]! String.to_utf32_copy(s, Allocator allocator) { usz codepoints = conv::utf8_codepoints(s); Char32* data = allocator::alloc_array_try(allocator, Char32, codepoints + 1)!; @@ -622,30 +580,27 @@ fn Char32[]! String.to_utf32(s, Allocator allocator) return data[:codepoints]; } -fn Char32[]! String.to_new_utf32(s) => s.to_utf32(allocator::heap()) @inline; -fn Char32[]! String.to_temp_utf32(s) => s.to_utf32(allocator::temp()) @inline; - <* - Convert a string to ASCII lower case. + Convert a string to ASCII lower case in place. @param [inout] s @pure *> -fn void String.convert_ascii_to_lower(s) +fn void String.convert_to_lower(s) { foreach (&c : s) if (c.is_upper() @pure) *c += 'a' - 'A'; } -fn String String.new_ascii_to_lower(s, Allocator allocator = allocator::heap()) +fn String String.to_lower_copy(s, Allocator allocator) { String copy = s.copy(allocator); - copy.convert_ascii_to_lower(); + copy.convert_to_lower(); return copy; } -fn String String.temp_ascii_to_lower(s) +fn String String.to_lower_tcopy(s) { - return s.new_ascii_to_lower(allocator::temp()); + return s.to_lower_copy(tmem()); } <* @@ -654,7 +609,7 @@ fn String String.temp_ascii_to_lower(s) @param [inout] s @pure *> -fn void String.convert_ascii_to_upper(s) +fn void String.convert_to_upper(s) { foreach (&c : s) if (c.is_lower() @pure) *c -= 'a' - 'A'; } @@ -667,10 +622,10 @@ fn void String.convert_ascii_to_upper(s) @return `a new String converted to ASCII upper case.` *> -fn String String.new_ascii_to_upper(s, Allocator allocator = allocator::heap()) +fn String String.to_upper_copy(s, Allocator allocator) { String copy = s.copy(allocator); - copy.convert_ascii_to_upper(); + copy.convert_to_upper(); return copy; } @@ -683,12 +638,12 @@ fn StringIterator String.iterator(s) @param [in] s @return `a temporary String converted to ASCII upper case.` *> -fn String String.temp_ascii_to_upper(s) +fn String String.to_upper_tcopy(s) { - return s.new_ascii_to_upper(allocator::temp()); + return s.to_upper_copy(tmem()); } -fn String! new_from_utf32(Char32[] utf32, Allocator allocator = allocator::heap()) +fn String! new_from_utf32(Allocator allocator, Char32[] utf32) { usz len = conv::utf8len_for_utf32(utf32); char* data = allocator::malloc_try(allocator, len + 1)!; @@ -698,7 +653,7 @@ fn String! new_from_utf32(Char32[] utf32, Allocator allocator = allocator::heap( return (String)data[:len]; } -fn String! new_from_utf16(Char16[] utf16, Allocator allocator = allocator::heap()) +fn String! new_from_utf16(Allocator allocator, Char16[] utf16) { usz len = conv::utf8len_for_utf16(utf16); char* data = allocator::malloc_try(allocator, len + 1)!; @@ -708,16 +663,16 @@ fn String! new_from_utf16(Char16[] utf16, Allocator allocator = allocator::heap( return (String)data[:len]; } -fn String! new_from_wstring(WString wstring, Allocator allocator = allocator::heap()) +fn String! new_from_wstring(Allocator allocator, WString wstring) { usz utf16_len; while (wstring[utf16_len] != 0) utf16_len++; Char16[] utf16 = wstring[:utf16_len]; - return new_from_utf16(utf16, allocator); + return new_from_utf16(allocator, utf16); } -fn String! temp_from_wstring(WString wstring) => new_from_wstring(wstring, allocator::temp()) @inline; -fn String! temp_from_utf16(Char16[] utf16) => new_from_utf16(utf16, allocator::temp()) @inline; +fn String! temp_from_wstring(WString wstring) => new_from_wstring(tmem(), wstring) @inline; +fn String! temp_from_utf16(Char16[] utf16) => new_from_utf16(tmem(), utf16) @inline; fn usz String.utf8_codepoints(s) { @@ -865,15 +820,15 @@ fn String! Splitter.next(&self) } } -macro String new_struct_to_str(x, Allocator allocator = allocator::heap()) +macro String new_from_struct(Allocator allocator, x) { DString s; @stack_mem(512; Allocator mem) { - s.init(mem); + s.init(allocator: mem); io::fprint(&s, x)!!; return s.copy_str(allocator); }; } -macro String temp_struct_to_str(x) => new_struct_to_str(x, allocator::temp()); +macro String temp_from_struct(x) => new_from_struct(tmem(), x); diff --git a/lib/std/encoding/base32.c3 b/lib/std/encoding/base32.c3 index 856db8b96..1900f2bcb 100644 --- a/lib/std/encoding/base32.c3 +++ b/lib/std/encoding/base32.c3 @@ -20,7 +20,7 @@ const char DEFAULT_PAD = '='; @require padding < 0xFF "Invalid padding character" @return "The encoded string." *> -fn String! encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) +fn String! encode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) { char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding)); return encode_buffer(src, dst, padding, alphabet); @@ -34,16 +34,14 @@ fn String! encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, B @require padding < 0xFF "Invalid padding character" @return "The decoded data." *> -fn char[]! decode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) +fn char[]! decode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) { char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding)); return decode_buffer(src, dst, padding, alphabet); } -fn String! encode_new(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::heap(), padding, alphabet); -fn String! encode_temp(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet); -fn char[]! decode_new(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::heap(), padding, alphabet); -fn char[]! decode_temp(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::temp(), padding, alphabet); +fn String! tencode(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(tmem(), code, padding, alphabet); +fn char[]! tdecode(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(tmem(), code, padding, alphabet); <* Calculate the length in bytes of the decoded data. diff --git a/lib/std/encoding/base64.c3 b/lib/std/encoding/base64.c3 index 96828ab1e..9659439c9 100644 --- a/lib/std/encoding/base64.c3 +++ b/lib/std/encoding/base64.c3 @@ -43,22 +43,20 @@ const Base64Alphabet URL = { const STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; -fn String encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) +fn String encode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) { char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding)); return encode_buffer(src, dst, padding, alphabet); } -fn char[]! decode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) +fn char[]! decode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) { char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding))!; return decode_buffer(src, dst, padding, alphabet); } -fn String encode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::heap(), padding, alphabet); -fn String encode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet); -fn char[]! decode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::heap(), padding, alphabet); -fn char[]! decode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::temp(), padding, alphabet); +fn String tencode(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(tmem(), code, padding, alphabet); +fn char[]! tdecode(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(tmem(), code, padding, alphabet); <* diff --git a/lib/std/encoding/csv.c3 b/lib/std/encoding/csv.c3 index b0f9760d0..b24380c29 100644 --- a/lib/std/encoding/csv.c3 +++ b/lib/std/encoding/csv.c3 @@ -38,26 +38,20 @@ fn void CsvReader.init(&self, InStream stream, String separator = ",") self.stream = stream; self.separator = separator; } - -fn CsvRow! CsvReader.read_new_row(self) -{ - return self.read_row(allocator::heap()) @inline; -} - <* @param [&inout] allocator *> fn CsvRow! CsvReader.read_row(self, Allocator allocator) { - String row = io::readline(self.stream, allocator: allocator)!; + String row = io::readline(allocator, self.stream)!; defer catch allocator::free(allocator, row); - String[] list = row.split(self.separator, allocator: allocator); + String[] list = row.split(allocator, self.separator); return { list, row, allocator }; } -fn CsvRow! CsvReader.read_temp_row(self) +fn CsvRow! CsvReader.tread_row(self) { - return self.read_row(allocator::temp()) @inline; + return self.read_row(tmem()) @inline; } <* @@ -83,13 +77,13 @@ macro void! CsvReader.@each_row(self, int rows = int.max; @body(String[] row)) @ { @stack_mem(512; Allocator mem) { - String! s = io::readline(stream, mem); + String! s = io::readline(mem, stream); if (catch err = s) { if (err == IoError.EOF) return; return err?; } - @body(s.split(sep, allocator: mem)); + @body(s.split(mem, sep)); }; } } \ No newline at end of file diff --git a/lib/std/encoding/hex.c3 b/lib/std/encoding/hex.c3 index d46653dbb..38ef23a81 100644 --- a/lib/std/encoding/hex.c3 +++ b/lib/std/encoding/hex.c3 @@ -13,22 +13,21 @@ fn char[]! decode_buffer(char[] code, char[] buffer) return buffer[:decode_bytes(code, buffer)!]; } -fn String encode(char[] code, Allocator allocator) +fn String encode(Allocator allocator, char[] code) { char[] data = allocator::alloc_array(allocator, char, encode_len(code.len)); return (String)data[:encode_bytes(code, data)]; } -fn char[]! decode(char[] code, Allocator allocator) +fn char[]! decode(Allocator allocator, char[] code) { char[] data = allocator::alloc_array(allocator, char, decode_len(code.len)); return data[:decode_bytes(code, data)!]; } -fn String encode_new(char[] code) @inline => encode(code, allocator::heap()); -fn String encode_temp(char[] code) @inline => encode(code, allocator::temp()); -fn char[]! decode_new(char[] code) @inline => decode(code, allocator::heap()); -fn char[]! decode_temp(char[] code) @inline => decode(code, allocator::temp()); +fn String tencode(char[] code) @inline => encode(tmem(), code); +fn char[]! tdecode(char[] code) @inline => decode(tmem(), code); + <* Calculate the size of the encoded data. diff --git a/lib/std/encoding/json.c3 b/lib/std/encoding/json.c3 index 4cea7cebf..648dc98fb 100644 --- a/lib/std/encoding/json.c3 +++ b/lib/std/encoding/json.c3 @@ -15,21 +15,21 @@ fault JsonParsingError INVALID_NUMBER, } -fn Object*! parse_string(String s, Allocator allocator = allocator::heap()) +fn Object*! parse_string(Allocator allocator, String s) { - return parse((ByteReader){}.init(s), allocator); + return parse(allocator, (ByteReader){}.init(s)); } -fn Object*! temp_parse_string(String s) +fn Object*! tparse_string(String s) { - return parse((ByteReader){}.init(s), allocator::temp()); + return parse(tmem(), (ByteReader){}.init(s)); } -fn Object*! parse(InStream s, Allocator allocator = allocator::heap()) +fn Object*! parse(Allocator allocator, InStream s) { - @stack_mem(512; Allocator mem) + @stack_mem(512; Allocator smem) { - JsonContext context = { .last_string = dstring::new_with_capacity(64, mem), .stream = s, .allocator = allocator }; + JsonContext context = { .last_string = dstring::new_with_capacity(smem, 64), .stream = s, .allocator = allocator }; @pool(allocator) { return parse_any(&context); @@ -37,9 +37,9 @@ fn Object*! parse(InStream s, Allocator allocator = allocator::heap()) }; } -fn Object*! temp_parse(InStream s) +fn Object*! tparse(InStream s) { - return parse(s, allocator::temp()); + return parse(tmem(), s); } // -- Implementation follows -- @@ -106,7 +106,7 @@ fn JsonTokenType! lex_number(JsonContext *context, char c) @local { @stack_mem(256; Allocator mem) { - DString t = dstring::new_with_capacity(32, allocator: mem); + DString t = dstring::new_with_capacity(mem, 32); bool negate = c == '-'; if (negate) { @@ -159,7 +159,7 @@ fn Object*! parse_map(JsonContext* context) @local @stack_mem(256; Allocator mem) { - DString temp_key = dstring::new_with_capacity(32, mem); + DString temp_key = dstring::new_with_capacity(mem, 32); while (token != JsonTokenType.RBRACE) { if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?; diff --git a/lib/std/experimental/FrameScheduler.c3 b/lib/std/experimental/FrameScheduler.c3 index e44fe18a1..c8bb7b1bf 100644 --- a/lib/std/experimental/FrameScheduler.c3 +++ b/lib/std/experimental/FrameScheduler.c3 @@ -1,4 +1,4 @@ -module std::experimental::scheduler(); +module std::experimental::scheduler{Event}; import std::collections, std::thread, std::time; struct DelayedSchedulerEvent @local @@ -19,9 +19,9 @@ fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other) @loca struct FrameScheduler { - PriorityQueue() delayed_events; - List() events; - List() pending_events; + PriorityQueue{DelayedSchedulerEvent} delayed_events; + List{Event} events; + List{Event} pending_events; bool pending; Mutex mtx; } diff --git a/lib/std/hash/hmac.c3 b/lib/std/hash/hmac.c3 index a9be8dbe8..2a511410d 100644 --- a/lib/std/hash/hmac.c3 +++ b/lib/std/hash/hmac.c3 @@ -1,4 +1,4 @@ -module std::hash::hmac(); +module std::hash::hmac{HashAlg, HASH_BYTES, BLOCK_BYTES}; import std::crypto; struct Hmac diff --git a/lib/std/hash/md5.c3 b/lib/std/hash/md5.c3 index 4267fab89..df6321798 100644 --- a/lib/std/hash/md5.c3 +++ b/lib/std/hash/md5.c3 @@ -13,9 +13,9 @@ struct Md5 uint[16] block; } -def HmacMd5 = Hmac(); -def hmac = hmac::hash(); -def pbkdf2 = hmac::pbkdf2(); +def HmacMd5 = Hmac{Md5, HASH_BYTES, BLOCK_BYTES}; +def hmac = hmac::hash{Md5, HASH_BYTES, BLOCK_BYTES}; +def pbkdf2 = hmac::pbkdf2{Md5, HASH_BYTES, BLOCK_BYTES}; fn char[HASH_BYTES] hash(char[] data) { diff --git a/lib/std/hash/sha1.c3 b/lib/std/hash/sha1.c3 index 4ddb27b60..4dad804f1 100644 --- a/lib/std/hash/sha1.c3 +++ b/lib/std/hash/sha1.c3 @@ -18,9 +18,9 @@ struct Sha1 char[BLOCK_BYTES] buffer; } -def HmacSha1 = Hmac(); -def hmac = hmac::hash(); -def pbkdf2 = hmac::pbkdf2(); +def HmacSha1 = Hmac{Sha1, HASH_BYTES, BLOCK_BYTES}; +def hmac = hmac::hash{Sha1, HASH_BYTES, BLOCK_BYTES}; +def pbkdf2 = hmac::pbkdf2{Sha1, HASH_BYTES, BLOCK_BYTES}; fn char[HASH_BYTES] hash(char[] data) { diff --git a/lib/std/hash/sha256.c3 b/lib/std/hash/sha256.c3 index b18200c96..a5e86ff3b 100644 --- a/lib/std/hash/sha256.c3 +++ b/lib/std/hash/sha256.c3 @@ -34,9 +34,9 @@ struct Sha256 char[BLOCK_SIZE] buffer; } -def HmacSha256 = Hmac(); -def hmac = hmac::hash(); -def pbkdf2 = hmac::pbkdf2(); +def HmacSha256 = Hmac{Sha256, HASH_SIZE, BLOCK_SIZE}; +def hmac = hmac::hash{Sha256, HASH_SIZE, BLOCK_SIZE}; +def pbkdf2 = hmac::pbkdf2{Sha256, HASH_SIZE, BLOCK_SIZE}; fn char[HASH_SIZE] hash(char[] data) { diff --git a/lib/std/io/file.c3 b/lib/std/io/file.c3 index 0e27ca96a..37361b3ea 100644 --- a/lib/std/io/file.c3 +++ b/lib/std/io/file.c3 @@ -21,7 +21,7 @@ fn File! open_path(Path path, String mode) fn bool exists(String file) => @pool() { - return path::exists(path::temp_new(file)) ?? false; + return os::native_file_or_dir_exists(file); } fn File from_handle(CFile file) @@ -34,12 +34,20 @@ fn bool is_file(String path) return os::native_is_file(path); } +fn bool is_dir(String path) +{ + return os::native_is_dir(path); +} + fn usz! get_size(String path) { return os::native_file_size(path); } -fn void! delete(String filename) => os::native_remove(filename) @inline; +fn void! delete(String filename) +{ + return os::native_remove(filename) @inline; +} <* diff --git a/lib/std/io/formatter.c3 b/lib/std/io/formatter.c3 index 85f499545..9ce3f29ae 100644 --- a/lib/std/io/formatter.c3 +++ b/lib/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; } @@ -28,8 +27,7 @@ macro bool is_struct_with_default_print($Type) { return $Type.kindof == STRUCT &&& !$defined($Type.to_format) - &&& !$defined($Type.to_new_string) - &&& !$defined($Type.to_string); + &&& !$defined($Type.to_constant_string); } <* @@ -125,7 +123,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 +135,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/lib/std/io/io.c3 b/lib/std/io/io.c3 index b04ab12e3..40cde0846 100644 --- a/lib/std/io/io.c3 +++ b/lib/std/io/io.c3 @@ -55,7 +55,7 @@ fault IoError @param [inout] allocator `the allocator to use.` @return `The string containing the data read.` *> -macro String! readline(stream = io::stdin(), Allocator allocator = allocator::heap()) +macro String! readline(Allocator allocator, stream = io::stdin()) { bool $is_stream = @typeis(stream, InStream); $if $is_stream: @@ -100,7 +100,7 @@ macro String! readline(stream = io::stdin(), Allocator allocator = allocator::he *> macro String! treadline(stream = io::stdin()) { - return readline(stream, allocator::temp()) @inline; + return readline(tmem(), stream) @inline; } <* diff --git a/lib/std/io/os/chdir.c3 b/lib/std/io/os/chdir.c3 index 1356edcb2..c2a57ac52 100644 --- a/lib/std/io/os/chdir.c3 +++ b/lib/std/io/os/chdir.c3 @@ -21,7 +21,7 @@ macro void! native_chdir(Path path) @pool() { // TODO improve with better error handling. - if (win32::setCurrentDirectoryW(path.str_view().to_temp_utf16()!!)) return; + if (win32::setCurrentDirectoryW(path.str_view().to_utf16_tcopy()!!)) return; }; return IoError.GENERAL_ERROR?; $default: diff --git a/lib/std/io/os/file_libc.c3 b/lib/std/io/os/file_libc.c3 index 72a428317..8aaa8d94f 100644 --- a/lib/std/io/os/file_libc.c3 +++ b/lib/std/io/os/file_libc.c3 @@ -8,7 +8,7 @@ import libc; fn void*! native_fopen(String filename, String mode) @inline => @pool() { $if env::WIN32: - void* file = libc::_wfopen(filename.to_temp_wstring(), mode.to_temp_wstring())!; + void* file = libc::_wfopen(filename.to_wstring_tcopy(), mode.to_wstring_tcopy())!; $else void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy()); $endif @@ -18,7 +18,7 @@ fn void*! native_fopen(String filename, String mode) @inline => @pool() fn void! native_remove(String filename) => @pool() { $if env::WIN32: - CInt result = libc::_wremove(filename.to_temp_wstring())!; + CInt result = libc::_wremove(filename.to_wstring_tcopy())!; $else CInt result = libc::remove(filename.zstr_tcopy()); $endif @@ -42,7 +42,7 @@ fn void! native_remove(String filename) => @pool() fn void*! native_freopen(void* file, String filename, String mode) @inline => @pool() { $if env::WIN32: - file = libc::_wfreopen(filename.to_temp_wstring(), mode.to_temp_wstring(), file)!; + file = libc::_wfreopen(filename.to_wstring_tcopy(), mode.to_wstring_tcopy(), file)!; $else file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file); $endif diff --git a/lib/std/io/os/fileinfo.c3 b/lib/std/io/os/fileinfo.c3 index f24fafb4c..ab6d3a8e8 100644 --- a/lib/std/io/os/fileinfo.c3 +++ b/lib/std/io/os/fileinfo.c3 @@ -40,7 +40,7 @@ fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || e fn usz! native_file_size(String path) @if(env::WIN32) => @pool() { Win32_FILE_ATTRIBUTE_DATA data; - win32::getFileAttributesExW(path.to_temp_wstring()!, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data); + win32::getFileAttributesExW(path.to_wstring_tcopy()!, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data); Win32_LARGE_INTEGER size; size.lowPart = data.nFileSizeLow; size.highPart = data.nFileSizeHigh; @@ -74,7 +74,7 @@ fn bool native_file_or_dir_exists(String path) $case env::WIN32: @pool() { - return (bool)win32::pathFileExistsW(path.to_temp_utf16()) ?? false; + return (bool)win32::pathFileExistsW(path.to_utf16_tcopy()) ?? false; }; $case env::POSIX: @pool() diff --git a/lib/std/io/os/getcwd.c3 b/lib/std/io/os/getcwd.c3 index 9e58c7093..69f649612 100644 --- a/lib/std/io/os/getcwd.c3 +++ b/lib/std/io/os/getcwd.c3 @@ -17,7 +17,7 @@ macro String! getcwd(Allocator allocator = allocator::heap()) free = true; } Char16[] str16 = res[:win32::wcslen(res)]; - return string::new_from_utf16(str16, allocator); + return string::new_from_utf16(allocator, str16); $case env::POSIX: const usz DEFAULT_BUFFER = 256; diff --git a/lib/std/io/os/ls.c3 b/lib/std/io/os/ls.c3 index 7a51865e8..4bc446801 100644 --- a/lib/std/io/os/ls.c3 +++ b/lib/std/io/os/ls.c3 @@ -15,7 +15,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al if (!name || name == "." || name == "..") continue; if (entry.d_type == posix::DT_LNK && no_symlinks) continue; if (entry.d_type == posix::DT_DIR && no_dirs) continue; - Path path = path::new(name, allocator)!!; + Path path = path::new(allocator, name)!!; list.push(path); } return list; @@ -31,7 +31,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al @pool(allocator) { - WString result = dir.str_view().tconcat(`\*`).to_temp_wstring()!!; + WString result = dir.str_view().tconcat(`\*`).to_wstring_tcopy()!!; Win32_WIN32_FIND_DATAW find_data; Win32_HANDLE find = win32::findFirstFileW(result, &find_data); if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?; @@ -43,7 +43,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al { String filename = string::temp_from_wstring((WString)&find_data.cFileName)!; if (filename == ".." || filename == ".") continue; - list.push(path::new(filename, allocator)!); + list.push(path::new(allocator, filename)!); }; } while (win32::findNextFileW(find, &find_data)); return list; diff --git a/lib/std/io/os/mkdir.c3 b/lib/std/io/os/mkdir.c3 index 1a2b4b5f4..385c00338 100644 --- a/lib/std/io/os/mkdir.c3 +++ b/lib/std/io/os/mkdir.c3 @@ -29,7 +29,7 @@ macro bool! native_mkdir(Path path, MkdirPermissions permissions) @pool() { // TODO security attributes - if (win32::createDirectoryW(path.str_view().to_temp_utf16()!!, null)) return true; + if (win32::createDirectoryW(path.str_view().to_utf16_tcopy()!!, null)) return true; switch (win32::getLastError()) { case win32::ERROR_ACCESS_DENIED: diff --git a/lib/std/io/os/rmdir.c3 b/lib/std/io/os/rmdir.c3 index 80f8f5dc6..970b1d319 100644 --- a/lib/std/io/os/rmdir.c3 +++ b/lib/std/io/os/rmdir.c3 @@ -26,7 +26,7 @@ macro bool! native_rmdir(Path path) $case env::WIN32: @pool() { - if (win32::removeDirectoryW(path.str_view().to_temp_utf16()!!)) return true; + if (win32::removeDirectoryW(path.str_view().to_utf16_tcopy()!!)) return true; switch (win32::getLastError()) { case win32::ERROR_ACCESS_DENIED: diff --git a/lib/std/io/os/rmtree.c3 b/lib/std/io/os/rmtree.c3 index 500be1bb9..866e89e34 100644 --- a/lib/std/io/os/rmtree.c3 +++ b/lib/std/io/os/rmtree.c3 @@ -16,7 +16,7 @@ fn void! native_rmtree(Path dir) { String name = ((ZString)&entry.name).str_view(); if (!name || name == "." || name == "..") continue; - Path new_path = dir.temp_append(name)!; + Path new_path = dir.tappend(name)!; if (entry.d_type == posix::DT_DIR) { native_rmtree(new_path)!; @@ -39,7 +39,7 @@ fn void! native_rmtree(Path path) { Win32_WIN32_FIND_DATAW find_data; String s = path.str_view().tconcat("\\*"); - Win32_HANDLE find = win32::findFirstFileW(s.to_temp_utf16(), &find_data)!; + Win32_HANDLE find = win32::findFirstFileW(s.to_utf16_tcopy(), &find_data)!; if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?; defer win32::findClose(find); @@ -47,16 +47,16 @@ fn void! native_rmtree(Path path) { @pool() { - String filename = string::new_from_wstring((WString)&find_data.cFileName, allocator::temp())!; + String filename = string::new_from_wstring(tmem(), (WString)&find_data.cFileName)!; if (filename == "." || filename == "..") continue; - Path file_path = path.temp_append(filename)!; + Path file_path = path.tappend(filename)!; if (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY) { native_rmtree(file_path)!; } else { - win32::deleteFileW(file_path.str_view().to_temp_wstring()!!); + win32::deleteFileW(file_path.str_view().to_wstring_tcopy()!!); } }; } while (win32::findNextFileW(find, &find_data) != 0); diff --git a/lib/std/io/os/temp_directory.c3 b/lib/std/io/os/temp_directory.c3 index 1a0659d99..75c50449d 100644 --- a/lib/std/io/os/temp_directory.c3 +++ b/lib/std/io/os/temp_directory.c3 @@ -1,23 +1,23 @@ module std::io::os @if(env::LIBC); import std::io::path, std::os; -fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(!env::WIN32) +fn Path! native_temp_directory(Allocator allocator) @if(!env::WIN32) { foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" }) { - String tmpdir = env::get_var(env) ?? ""; - if (tmpdir) return path::new(tmpdir, allocator); + String tmpdir = env::tget_var(env) ?? ""; + if (tmpdir) return path::new(allocator, tmpdir); } - return path::new("/tmp", allocator); + return path::new(allocator, "/tmp"); } -fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(env::WIN32) => @pool(allocator) +fn Path! native_temp_directory(Allocator allocator) @if(env::WIN32) => @pool(allocator) { Win32_DWORD len = win32::getTempPathW(0, null); if (!len) return IoError.GENERAL_ERROR?; Char16[] buff = mem::temp_alloc_array(Char16, len + (usz)1); if (!win32::getTempPathW(len, buff)) return IoError.GENERAL_ERROR?; - return path::new(string::temp_from_utf16(buff[:len]), allocator); + return path::new(allocator, string::temp_from_utf16(buff[:len])); } module std::io::os @if(env::NO_LIBC); diff --git a/lib/std/io/path.c3 b/lib/std/io/path.c3 index 39f419866..2420b5161 100644 --- a/lib/std/io/path.c3 +++ b/lib/std/io/path.c3 @@ -2,12 +2,12 @@ module std::io::path; import std::collections::list, std::io::os; import std::os::win32; -const PathEnv DEFAULT_PATH_ENV = env::WIN32 ? PathEnv.WIN32 : PathEnv.POSIX; +const PathEnv DEFAULT_ENV = env::WIN32 ? PathEnv.WIN32 : PathEnv.POSIX; const char PREFERRED_SEPARATOR_WIN32 = '\\'; const char PREFERRED_SEPARATOR_POSIX = '/'; const char PREFERRED_SEPARATOR = env::WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX; -def PathList = List(); +def PathList = List { Path }; fault PathResult { @@ -21,6 +21,7 @@ struct PathImp (Printable) { String path_string; PathEnv env; + Allocator allocator; } enum PathEnv @@ -29,16 +30,11 @@ enum PathEnv POSIX } -fn Path! new_cwd(Allocator allocator = allocator::heap()) => @pool(allocator) -{ - return new(os::getcwd(allocator::temp()), allocator); -} - -fn Path! getcwd(Allocator allocator = allocator::heap()) @deprecated("Use new_cwd()") +fn Path! cwd(Allocator allocator) { @pool(allocator) { - return new(os::getcwd(allocator::temp()), allocator); + return new(allocator, os::getcwd(tmem())); }; } @@ -46,38 +42,38 @@ fn bool is_dir(Path path) => os::native_is_dir(path.str_view()); fn bool is_file(Path path) => os::native_is_file(path.str_view()); fn usz! file_size(Path path) => os::native_file_size(path.str_view()); fn bool exists(Path path) => os::native_file_or_dir_exists(path.str_view()); -fn Path! temp_cwd() => new_cwd(allocator::temp()) @inline; -fn Path! tgetcwd() @deprecated("Use temp_cwd()") => new_cwd(allocator::temp()) @inline; -fn void! chdir(Path path) => os::native_chdir(path) @inline; -fn Path! temp_directory(Allocator allocator = allocator::heap()) => os::native_temp_directory(allocator); +fn Path! tcwd() => cwd(tmem()) @inline; + +<* + @require @is_pathlike(path) : "Expected a Path or String to chdir" +*> +macro void! chdir(path) +{ + $if @typeis(path, String): + @pool() + { + return os::native_chdir(temp(path)); + }; + $else + return os::native_chdir(path) @inline; + $endif +} + +fn Path! temp_directory(Allocator allocator) => os::native_temp_directory(allocator); + fn void! delete(Path path) => os::native_remove(path.str_view()) @inline; -macro bool is_separator(char c, PathEnv path_env = DEFAULT_PATH_ENV) +macro bool @is_pathlike(#path) => @typeis(#path, String) || @typeis(#path, Path); + +macro bool is_separator(char c, PathEnv path_env = DEFAULT_ENV) { return c == '/' || (c == '\\' && path_env == PathEnv.WIN32); } -macro bool is_posix_separator(char c) -{ - return c == '/' || c == '\\'; -} +macro bool is_posix_separator(char c) => c == '/'; +macro bool is_win32_separator(char c) => c == '/' || c == '\\'; -macro bool is_win32_separator(char c) -{ - return c == '/' || c == '\\'; -} - -fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap()) @deprecated("use new_ls") -{ - return new_ls(dir, no_dirs, no_symlinks, mask, allocator); - -} -fn PathList! temp_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "") -{ - return new_ls(dir, no_dirs, no_symlinks, mask, allocator::temp()) @inline; -} - -fn PathList! new_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap()) +fn PathList! ls(Allocator allocator, Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "") { $if $defined(os::native_ls): return os::native_ls(dir, no_dirs, no_symlinks, mask, allocator); @@ -97,35 +93,35 @@ enum MkdirPermissions Create a directory on a given path, optionally recursive. @param path `The path to create` + @require @is_pathlike(path) : "Expected a Path or String to chdir" @param recursive `If directories in between should be created if they're missing, defaults to false` @param permissions `The permissions to set on the directory` *> -fn bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL) +macro bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL) { - if (!path.path_string.len) return PathResult.INVALID_PATH?; - if (is_dir(path)) return false; - if (exists(path)) return IoError.FILE_NOT_DIR?; - - if (recursive) - { - if (try parent = path.parent()) mkdir(parent, true, permissions)!; - } - if (!is_dir(path.parent()) ?? false) return IoError.CANNOT_READ_DIR?; - - return os::native_mkdir(path, permissions); + $if @typeis(path, String): + @pool() { return _mkdir(temp(path), recursive, permissions); }; + $else + return _mkdir(path, recursive, permissions); + $endif } + <* Tries to delete directory, which must be empty. @param path `The path to delete` + @require @is_pathlike(path) : "Expected a Path or String to chdir" @return `true if there was a directory to delete, false otherwise` @return! PathResult.INVALID_PATH `if the path was invalid` *> -fn bool! rmdir(Path path) +macro bool! rmdir(path) { - if (!path.path_string.len) return PathResult.INVALID_PATH?; - return os::native_rmdir(path); + $if @typeis(path, String): + @pool() { return _rmdir(temp(path)); }; + $else + return _mkdir(path); + $endif } <* @@ -146,9 +142,9 @@ fn void! rmtree(Path path) @return! PathResult.INVALID_PATH `if the path was invalid` *> -fn Path! new(String path, Allocator allocator = allocator::heap(), PathEnv path_env = DEFAULT_PATH_ENV) +fn Path! new(Allocator allocator, String path, PathEnv path_env = DEFAULT_ENV) { - return { normalize(path.copy(allocator), path_env), path_env }; + return { normalize(path.copy(allocator), path_env), path_env, allocator }; } <* @@ -156,24 +152,24 @@ fn Path! new(String path, Allocator allocator = allocator::heap(), PathEnv path_ @return! PathResult.INVALID_PATH `if the path was invalid` *> -fn Path! temp_new(String path, PathEnv path_env = DEFAULT_PATH_ENV) +fn Path! temp(String path, PathEnv path_env = DEFAULT_ENV) { - return new(path, allocator::temp(), path_env); + return new(tmem(), path, path_env); } -fn Path! new_win32_wstring(WString path, Allocator allocator = allocator::heap()) => @pool(allocator) +fn Path! from_win32_wstring(Allocator allocator, WString path) => @pool(allocator) { - return path::new(string::temp_from_wstring(path)!, allocator: allocator); + return path::new(allocator, string::temp_from_wstring(path)!); } -fn Path! new_windows(String path, Allocator allocator = allocator::heap()) +fn Path! for_windows(Allocator allocator, String path) { - return new(path, allocator, WIN32); + return new(allocator, path, WIN32); } -fn Path! new_posix(String path, Allocator allocator = allocator::heap()) +fn Path! for_posix(Allocator allocator, String path) { - return new(path, allocator, POSIX); + return new(allocator, path, POSIX); } fn bool Path.equals(self, Path p2) @@ -181,19 +177,14 @@ fn bool Path.equals(self, Path p2) return self.env == p2.env && self.path_string == p2.path_string; } -fn Path! Path.append(self, String filename, Allocator allocator = allocator::heap()) @deprecated("Use path.new_append(...)") -{ - return self.new_append(filename, allocator) @inline; -} - <* Append the string to the current path. @param [in] filename *> -fn Path! Path.new_append(self, String filename, Allocator allocator = allocator::heap()) +fn Path! Path.append(self, Allocator allocator, String filename) { - if (!self.path_string.len) return new(filename, allocator, self.env)!; + if (!self.path_string.len) return new(allocator, filename, self.env)!; assert(!is_separator(self.path_string[^1], self.env)); @pool(allocator) @@ -202,35 +193,37 @@ fn Path! Path.new_append(self, String filename, Allocator allocator = allocator: dstr.append(self.path_string); dstr.append(PREFERRED_SEPARATOR); dstr.append(filename); - return { normalize(dstr.copy_str(allocator), self.env), self.env }; + return new(allocator, dstr.str_view(), self.env); }; } -fn Path! Path.temp_append(self, String filename) => self.new_append(filename, allocator::temp()); +fn Path! Path.tappend(self, String filename) => self.append(tmem(), filename); -fn Path! Path.tappend(self, String filename) @deprecated("Use path.temp_append(...)") => self.new_append(filename, allocator::temp()); - -fn usz Path.start_of_base_name(self) @local +fn usz! start_of_base_name(String str, PathEnv path_env) @local { - String path_str = self.path_string; - if (!path_str.len) return 0; - if (self.env == PathEnv.WIN32) + if (!str.len) return 0; + usz! start_slash = str.rindex_of_char('/'); + if (path_env != PathEnv.WIN32) return start_slash + 1 ?? 0; + if (try index = str.rindex_of_char('\\')) { - if (try index = path_str.rindex_of_char('\\')) - { - // c:\ style path, we're done! - if (path_str[0] != '\\') return index + 1; - // Handle \\server\foo - // Find the \ before "foo" - usz last_index = 2 + path_str[2..].index_of_char('\\')!!; - // If they don't match, we're done - assert(last_index <= index, "Invalid normalized, path %d vs %s in %s", last_index, index, path_str); - if (last_index != index) return index + 1; - // Otherwise just default to the volume length. - } - return volume_name_len(path_str, self.env)!!; + if (try start_slash && start_slash > index) return start_slash + 1; + // c:\ style path, we're done! + if (str[0] != '\\') return index + 1; + // Handle \\server\foo + // Find the \ before "foo" + usz last_index = 2 + str[2..].index_of_char('\\')!; + // If they don't match, we're done + if (last_index > index) return PathResult.INVALID_PATH?; + if (last_index != index) return index + 1; + // Otherwise just default to the volume length. } - return path_str.rindex_of_char('/') + 1 ?? 0; + return volume_name_len(str, path_env)!!; +} + + +fn bool! String.is_absolute_path(self) => @pool() +{ + return temp(self).is_absolute(); } fn bool! Path.is_absolute(self) @@ -242,55 +235,69 @@ fn bool! Path.is_absolute(self) return path_start < path_str.len && is_separator(path_str[path_start], self.env); } -fn Path! Path.absolute(self, Allocator allocator = allocator::heap()) @deprecated("Use path.new_absolute()") + +fn Path! String.to_absolute_path(self, Allocator allocator) => @pool(allocator) { - return self.new_absolute(allocator) @inline; + return temp(self).absolute(allocator); } <* - @require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths" + @require self.env == DEFAULT_ENV : "This method is only available on native paths" *> -fn Path! Path.new_absolute(self, Allocator allocator = allocator::heap()) +fn Path! Path.absolute(self, Allocator allocator) { String path_str = self.str_view(); if (!path_str.len) return PathResult.INVALID_PATH?; - if (self.is_absolute()!) return new(path_str, allocator, self.env); + if (self.is_absolute()!) return new(allocator, path_str, self.env); if (path_str == ".") { @pool(allocator) { - String cwd = os::getcwd(allocator::temp())!; - return new(cwd, allocator, self.env); + String cwd = os::getcwd(tmem())!; + return new(allocator, cwd, self.env); }; } - $if DEFAULT_PATH_ENV == WIN32: + $if DEFAULT_ENV == WIN32: @pool(allocator) { const usz BUFFER_LEN = 4096; WString buffer = (WString)mem::temp_alloc_array(Char16, BUFFER_LEN); - buffer = win32::_wfullpath(buffer, path_str.to_temp_wstring()!, BUFFER_LEN); + buffer = win32::_wfullpath(buffer, path_str.to_wstring_tcopy()!, BUFFER_LEN); if (!buffer) return PathResult.INVALID_PATH?; - return { string::new_from_wstring(buffer, allocator), WIN32 }; + return { string::new_from_wstring(allocator, buffer), WIN32, allocator }; }; $else - String cwd = os::getcwd(allocator::temp())!; - return (Path){ cwd, self.env }.new_append(path_str, allocator)!; + String cwd = os::getcwd(tmem())!; + return (Path){ cwd, self.env, tmem() }.append(allocator, path_str)!; $endif } +fn String! String.file_basename(self, Allocator allocator) => @pool(allocator) +{ + return temp(self).basename().copy(allocator); +} + +fn String! String.file_tbasename(self) => self.file_basename(tmem()); + fn String Path.basename(self) { - usz basename_start = self.start_of_base_name(); + usz basename_start = start_of_base_name(self.path_string, self.env)!!; String path_str = self.path_string; if (basename_start == path_str.len) return ""; return path_str[basename_start..]; } +fn String! String.path_tdirname(self) => self.path_dirname(tmem()); + +fn String! String.path_dirname(self, Allocator allocator) => @pool(allocator) +{ + return temp(self).dirname().copy(allocator); +} fn String Path.dirname(self) { - usz basename_start = self.start_of_base_name(); String path_str = self.path_string; + usz basename_start = start_of_base_name(path_str, self.env)!!; if (basename_start == 0) return "."; usz start = volume_name_len(path_str, self.env)!!; if (basename_start <= start + 1) @@ -304,6 +311,7 @@ fn String Path.dirname(self) return path_str[:basename_start - 1]; } + <* Test if the path has the given extension, so given the path /foo/bar.c3 this would be true matching the extension "c3" @@ -337,6 +345,16 @@ fn String Path.volume_name(self) return self.path_string[:len]; } +fn Path! String.to_path(self, Allocator allocator) +{ + return new(allocator, self); +} + +fn Path! String.to_tpath(self) +{ + return new(tmem(), self); +} + fn usz! volume_name_len(String path, PathEnv path_env) @local { usz len = path.len; @@ -387,13 +405,13 @@ fn Path! Path.parent(self) { if (is_separator(c, self.env)) { - return { self.path_string[:i], self.env }; + return { self.path_string[:i], self.env, null }; } } return PathResult.NO_PARENT?; } -fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV) +fn String! normalize(String path_str, PathEnv path_env = DEFAULT_ENV) { if (!path_str.len) return path_str; usz path_start = volume_name_len(path_str, path_env)!; @@ -544,19 +562,19 @@ def PathWalker = fn bool! (Path, bool is_dir, void*); <* Walk the path recursively. PathWalker is run on every file and directory found. Return true to abort the walk. - @require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths" + @require self.env == DEFAULT_ENV : "This method is only available on native paths" *> fn bool! Path.walk(self, PathWalker w, void* data) { const PATH_MAX = 512; @stack_mem(PATH_MAX; Allocator allocator) { - Path abs = self.new_absolute(allocator)!; - PathList files = new_ls(abs, allocator: allocator)!; + Path abs = self.absolute(allocator)!; + PathList files = ls(allocator, abs)!; foreach (f : files) { if (f.str_view() == "." || f.str_view() == "..") continue; - f = abs.new_append(f.str_view(), allocator)!; + f = abs.append(allocator, f.str_view())!; bool is_directory = is_dir(f); if (w(f, is_directory, data)!) return true; if (is_directory && f.walk(w, data)!) return true; @@ -565,6 +583,35 @@ fn bool! Path.walk(self, PathWalker w, void* data) return false; } +def TraverseCallback = fn bool! (Path, bool is_dir, any data); + +<* + Walk the path recursively. TraverseCallback is run for every file and + directory found. Return true to abort the walk. + @require path.env == DEFAULT_ENV : "This method is only available on native paths" +*> +fn bool! traverse(Path path, TraverseCallback callback, any data) +{ + const PATH_MAX = 512; + @stack_mem(PATH_MAX; Allocator allocator) + { + Path abs = path.absolute(allocator)!; + PathList files = ls(allocator, abs)!; + foreach (f : files) + { + if (f.str_view() == "." || f.str_view() == "..") continue; + @stack_mem(128; Allocator smem) + { + f = abs.append(smem, f.str_view())!; + bool is_directory = is_dir(f); + if (callback(f, is_directory, data)!) return true; + if (is_directory && traverse(f, callback, data)!) return true; + }; + } + }; + return false; +} + fn String Path.str_view(self) @inline { return self.path_string; @@ -576,26 +623,19 @@ fn bool Path.has_suffix(self, String str) return self.str_view().ends_with(str); } -fn void Path.free_with_allocator(self, Allocator allocator) -{ - allocator::free(allocator, self.path_string.ptr); -} - +<* + @require self.allocator != null : "This Path should never be freed" +*> fn void Path.free(self) { - free(self.path_string.ptr); + allocator::free(self.allocator, self.path_string.ptr); } - fn usz! Path.to_format(&self, Formatter* formatter) @dynamic { return formatter.print(self.str_view()); } -fn String Path.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic -{ - return self.str_view().copy(allocator); -} const bool[256] RESERVED_PATH_CHAR_POSIX = { [0] = true, @@ -619,9 +659,29 @@ macro bool is_reserved_win32_path_char(char c) return RESERVED_PATH_CHAR_WIN32[c]; } -macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_PATH_ENV) +macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_ENV) { return path_env == PathEnv.WIN32 ? RESERVED_PATH_CHAR_WIN32[c] : RESERVED_PATH_CHAR_POSIX[c]; -} \ No newline at end of file +} +fn bool! _mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL) @private +{ + if (!path.path_string.len) return PathResult.INVALID_PATH?; + if (is_dir(path)) return false; + if (exists(path)) return IoError.FILE_NOT_DIR?; + + if (recursive) + { + if (try parent = path.parent()) mkdir(parent, true, permissions)!; + } + if (!is_dir(path.parent()) ?? false) return IoError.CANNOT_READ_DIR?; + + return os::native_mkdir(path, permissions); +} + +fn bool! _rmdir(Path path) @private +{ + if (!path.path_string.len) return PathResult.INVALID_PATH?; + return os::native_rmdir(path); +} diff --git a/lib/std/io/stream.c3 b/lib/std/io/stream.c3 index 19da0a2e4..0f083229d 100644 --- a/lib/std/io/stream.c3 +++ b/lib/std/io/stream.c3 @@ -77,22 +77,6 @@ macro usz! read_all(stream, char[] buffer) return n; } -<* - @require @is_instream(stream) -*> -macro char[]! read_new_fully(stream, Allocator allocator = allocator::heap()) @deprecated("Use read_fully(mem)") -{ - usz len = available(stream)!; - char* data = allocator::malloc_try(allocator, len)!; - defer catch allocator::free(allocator, data); - usz read = 0; - while (read < len) - { - read += stream.read(data[read:len - read])!; - } - return data[:len]; -} - <* @require @is_instream(stream) *> diff --git a/lib/std/io/stream/bytebuffer.c3 b/lib/std/io/stream/bytebuffer.c3 index 919184f8d..c1964fa30 100644 --- a/lib/std/io/stream/bytebuffer.c3 +++ b/lib/std/io/stream/bytebuffer.c3 @@ -24,27 +24,9 @@ fn ByteBuffer* ByteBuffer.init(&self, Allocator allocator, usz max_read, usz ini return self; } -<* - ByteBuffer provides a streamable read/write buffer. - max_read defines how many bytes might be kept before its internal buffer is shrinked. - @require self.bytes.len == 0 "Buffer already initialized." -*> -fn ByteBuffer* ByteBuffer.new_init(&self, usz max_read, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)") -{ - *self = { .allocator = allocator, .max_read = max_read }; - initial_capacity = max(initial_capacity, 16); - self.grow(initial_capacity); - return self; -} - fn ByteBuffer* ByteBuffer.tinit(&self, usz max_read, usz initial_capacity = 16) { - return self.init(allocator::temp(), max_read, initial_capacity); -} - -fn ByteBuffer* ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = 16) @deprecated("Use tinit()") -{ - return self.init(allocator::temp(), max_read, initial_capacity); + return self.init(tmem(), max_read, initial_capacity); } <* diff --git a/lib/std/io/stream/bytewriter.c3 b/lib/std/io/stream/bytewriter.c3 index c5386a3e1..377aaab7e 100644 --- a/lib/std/io/stream/bytewriter.c3 +++ b/lib/std/io/stream/bytewriter.c3 @@ -8,18 +8,6 @@ struct ByteWriter (OutStream) Allocator allocator; } -<* - @param [&inout] self - @param [&inout] allocator - @require self.bytes.len == 0 "Init may not run on already initialized data" - @ensure (bool)allocator, self.index == 0 -*> -fn ByteWriter* ByteWriter.new_init(&self, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)") -{ - *self = { .bytes = {}, .allocator = allocator }; - return self; -} - <* @param [&inout] self @param [&inout] allocator @@ -39,17 +27,7 @@ fn ByteWriter* ByteWriter.init(&self, Allocator allocator) *> fn ByteWriter* ByteWriter.tinit(&self) { - return self.init(allocator::temp()) @inline; -} - -<* - @param [&inout] self - @require self.bytes.len == 0 "Init may not run on already initialized data" - @ensure self.index == 0 -*> -fn ByteWriter* ByteWriter.temp_init(&self) @deprecated("Use tinit") -{ - return self.init(allocator::temp()) @inline; + return self.init(tmem()) @inline; } fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data) diff --git a/lib/std/io/stream/multireader.c3 b/lib/std/io/stream/multireader.c3 index f26813a54..6cfdd1eb5 100644 --- a/lib/std/io/stream/multireader.c3 +++ b/lib/std/io/stream/multireader.c3 @@ -12,20 +12,6 @@ struct MultiReader (InStream) } -<* - @param [&inout] self - @param [&inout] allocator - @require self.readers.len == 0 "Init may not run on already initialized data" - @ensure self.index == 0 -*> -fn MultiReader* MultiReader.new_init(&self, InStream... readers, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)") -{ - InStream []copy = allocator::new_array(allocator, InStream, readers.len); - copy[..] = readers[..]; - *self = { .readers = copy, .allocator = allocator }; - return self; -} - <* @param [&inout] self @param [&inout] allocator @@ -40,16 +26,6 @@ fn MultiReader* MultiReader.init(&self, Allocator allocator, InStream... readers return self; } -<* - @param [&inout] self - @require self.readers.len == 0 "Init may not run on already initialized data" - @ensure self.index == 0 -*> -fn MultiReader* MultiReader.temp_init(&self, InStream... readers) @deprecated("Use tinit()") -{ - return self.init(allocator::temp(), ...readers); -} - <* @param [&inout] self @require self.readers.len == 0 "Init may not run on already initialized data" @@ -57,7 +33,7 @@ fn MultiReader* MultiReader.temp_init(&self, InStream... readers) @deprecated("U *> fn MultiReader* MultiReader.tinit(&self, InStream... readers) { - return self.init(allocator::temp(), ...readers); + return self.init(tmem(), ...readers); } fn void MultiReader.free(&self) diff --git a/lib/std/io/stream/multiwriter.c3 b/lib/std/io/stream/multiwriter.c3 index 5536bee08..ec1dbc4c1 100644 --- a/lib/std/io/stream/multiwriter.c3 +++ b/lib/std/io/stream/multiwriter.c3 @@ -23,30 +23,6 @@ fn MultiWriter* MultiWriter.init(&self, Allocator allocator, OutStream... writer return self; } -<* - @param [&inout] self - @param [&inout] allocator - @require writers.len > 0 - @require self.writers.len == 0 "Init may not run on already initialized data" -*> -fn MultiWriter* MultiWriter.new_init(&self, OutStream... writers, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)") -{ - OutStream[] copy = allocator::new_array(allocator, OutStream, writers.len); - copy[..] = writers[..]; - *self = { .writers = copy, .allocator = allocator }; - return self; -} - -<* - @param [&inout] self - @require writers.len > 0 - @require self.writers.len == 0 "Init may not run on already initialized data" -*> -fn MultiWriter* MultiWriter.temp_init(&self, OutStream... writers) @deprecated("Use tinit") -{ - return self.init(allocator::temp(), ...writers); -} - <* @param [&inout] self @require writers.len > 0 @@ -54,7 +30,7 @@ fn MultiWriter* MultiWriter.temp_init(&self, OutStream... writers) @deprecated(" *> fn MultiWriter* MultiWriter.tinit(&self, OutStream... writers) { - return self.init(allocator::temp(), ...writers); + return self.init(tmem(), ...writers); } fn void MultiWriter.free(&self) diff --git a/lib/std/math/math.c3 b/lib/std/math/math.c3 index 93a70a607..a07a04854 100644 --- a/lib/std/math/math.c3 +++ b/lib/std/math/math.c3 @@ -90,33 +90,33 @@ fault MatrixError MATRIX_INVERSE_DOESNT_EXIST, } -def Complexf = Complex(); -def Complex = Complex(); -def COMPLEX_IDENTITY = complex::IDENTITY() @builtin; -def COMPLEXF_IDENTITY = complex::IDENTITY() @builtin; +def Complexf = Complex{float}; +def Complex = Complex{double}; +def COMPLEX_IDENTITY = complex::IDENTITY{double} @builtin; +def COMPLEXF_IDENTITY = complex::IDENTITY{float} @builtin; -def Quaternionf = Quaternion(); -def Quaternion = Quaternion(); -def QUATERNION_IDENTITY = quaternion::IDENTITY() @builtin; -def QUATERNIONF_IDENTITY = quaternion::IDENTITY() @builtin; +def Quaternionf = Quaternion{float}; +def Quaternion = Quaternion{double}; +def QUATERNION_IDENTITY = quaternion::IDENTITY{double} @builtin; +def QUATERNIONF_IDENTITY = quaternion::IDENTITY{float} @builtin; -def Matrix2f = Matrix2x2(); -def Matrix2 = Matrix2x2(); -def Matrix3f = Matrix3x3(); -def Matrix3 = Matrix3x3(); -def Matrix4f = Matrix4x4(); -def Matrix4 = Matrix4x4(); -def matrix4_ortho = matrix::ortho() @builtin; -def matrix4_perspective = matrix::perspective() @builtin; -def matrix4f_ortho = matrix::ortho() @builtin; -def matrix4f_perspective = matrix::perspective() @builtin; +def Matrix2f = Matrix2x2{float}; +def Matrix2 = Matrix2x2{double}; +def Matrix3f = Matrix3x3{float}; +def Matrix3 = Matrix3x3{double}; +def Matrix4f = Matrix4x4{float}; +def Matrix4 = Matrix4x4{double}; +def matrix4_ortho = matrix::ortho{double} @builtin; +def matrix4_perspective = matrix::perspective{double} @builtin; +def matrix4f_ortho = matrix::ortho{float} @builtin; +def matrix4f_perspective = matrix::perspective{float} @builtin; -def MATRIX2_IDENTITY = matrix::IDENTITY2() @builtin; -def MATRIX2F_IDENTITY = matrix::IDENTITY2() @builtin; -def MATRIX3_IDENTITY = matrix::IDENTITY3() @builtin; -def MATRIX3F_IDENTITY = matrix::IDENTITY3() @builtin; -def MATRIX4_IDENTITY = matrix::IDENTITY4() @builtin; -def MATRIX4F_IDENTITY = matrix::IDENTITY4() @builtin; +def MATRIX2_IDENTITY = matrix::IDENTITY2{double} @builtin; +def MATRIX2F_IDENTITY = matrix::IDENTITY2{float} @builtin; +def MATRIX3_IDENTITY = matrix::IDENTITY3{double} @builtin; +def MATRIX3F_IDENTITY = matrix::IDENTITY3{float} @builtin; +def MATRIX4_IDENTITY = matrix::IDENTITY4{double} @builtin; +def MATRIX4F_IDENTITY = matrix::IDENTITY4{float} @builtin; <* diff --git a/lib/std/math/math_complex.c3 b/lib/std/math/math_complex.c3 index 7843fec2e..df3a39543 100644 --- a/lib/std/math/math_complex.c3 +++ b/lib/std/math/math_complex.c3 @@ -1,4 +1,4 @@ -module std::math::complex(); +module std::math::complex{Real}; union Complex { diff --git a/lib/std/math/math_matrix.c3 b/lib/std/math/math_matrix.c3 index c9486ea0b..afd060aff 100644 --- a/lib/std/math/math_matrix.c3 +++ b/lib/std/math/math_matrix.c3 @@ -1,4 +1,4 @@ -module std::math::matrix(); +module std::math::matrix{Real}; import std::math::vector; struct Matrix2x2 diff --git a/lib/std/math/math_nolibc/fabs.c3 b/lib/std/math/math_nolibc/fabs.c3 index d434ca8af..38c96e8fe 100644 --- a/lib/std/math/math_nolibc/fabs.c3 +++ b/lib/std/math/math_nolibc/fabs.c3 @@ -1,15 +1,15 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn double _fabs(double x) @weak @extern("fabs") @nostrip -{ - ulong ix = bitcast(x, ulong); - ix &= ~(1ul << 63); - return bitcast(ix, double); -} - -fn float _fabsf(float x) @weak @extern("fabsf") @nostrip -{ - uint ix = bitcast(x, uint); - ix &= 0x7fffffff; - return bitcast(ix, float); -} +module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); + +fn double _fabs(double x) @weak @extern("fabs") @nostrip +{ + ulong ix = bitcast(x, ulong); + ix &= ~(1ul << 63); + return bitcast(ix, double); +} + +fn float _fabsf(float x) @weak @extern("fabsf") @nostrip +{ + uint ix = bitcast(x, uint); + ix &= 0x7fffffff; + return bitcast(ix, float); +} diff --git a/lib/std/math/math_nolibc/frexp.c3 b/lib/std/math/math_nolibc/frexp.c3 index e0565bba8..20702f869 100644 --- a/lib/std/math/math_nolibc/frexp.c3 +++ b/lib/std/math/math_nolibc/frexp.c3 @@ -1,59 +1,59 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn double frexp(double x, int* exp) @extern("frexp") -{ - uint hx = x.high_word(); - uint ix = hx & 0x7fffffff; - uint lx = x.low_word(); - - if (ix >= 0x7ff00000 || (ix | lx) == 0) - { - *exp = 0; - return x; - } - - // exponent extraction and normalization - int e = (int)((ix >> 20) & 0x7ff); - if (e == 0) - { - // subnormal number - x *= 0x1p64; - hx = x.high_word(); - e = (int)((hx >> 20) & 0x7ff) - 64; - } - *exp = e - 1022; - - // set exponent to -1 (fraction in [0.5, 1)) - hx = (hx & 0x800fffff) | 0x3fe00000; - { - ulong rep = ((ulong)hx << 32) | lx; - return bitcast(rep, double); - } -} - -fn float frexpf(float x, int* exp) @extern("frexpf") -{ - uint ix = x.word(); - uint hx = ix & 0x7fffffff; - - if (hx >= 0x7f800000 || hx == 0) - { - *exp = 0; - return x; - } - - // exponent extraction and normalization - int e = (int)((hx >> 23) & 0xff); - if (e == 0) - { - // subnormal number - x *= 0x1p64f; - ix = x.word(); - e = (int)((ix >> 23) & 0xff) - 64; - } - *exp = e - 126; - - // set exponent to -1 (fraction in [0.5, 1)) - ix = (ix & 0x807fffff) | 0x3f000000; - return bitcast(ix, float); -} +module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); + +fn double frexp(double x, int* exp) @extern("frexp") +{ + uint hx = x.high_word(); + uint ix = hx & 0x7fffffff; + uint lx = x.low_word(); + + if (ix >= 0x7ff00000 || (ix | lx) == 0) + { + *exp = 0; + return x; + } + + // exponent extraction and normalization + int e = (int)((ix >> 20) & 0x7ff); + if (e == 0) + { + // subnormal number + x *= 0x1p64; + hx = x.high_word(); + e = (int)((hx >> 20) & 0x7ff) - 64; + } + *exp = e - 1022; + + // set exponent to -1 (fraction in [0.5, 1)) + hx = (hx & 0x800fffff) | 0x3fe00000; + { + ulong rep = ((ulong)hx << 32) | lx; + return bitcast(rep, double); + } +} + +fn float frexpf(float x, int* exp) @extern("frexpf") +{ + uint ix = x.word(); + uint hx = ix & 0x7fffffff; + + if (hx >= 0x7f800000 || hx == 0) + { + *exp = 0; + return x; + } + + // exponent extraction and normalization + int e = (int)((hx >> 23) & 0xff); + if (e == 0) + { + // subnormal number + x *= 0x1p64f; + ix = x.word(); + e = (int)((ix >> 23) & 0xff) - 64; + } + *exp = e - 126; + + // set exponent to -1 (fraction in [0.5, 1)) + ix = (ix & 0x807fffff) | 0x3f000000; + return bitcast(ix, float); +} diff --git a/lib/std/math/math_nolibc/ldexp.c3 b/lib/std/math/math_nolibc/ldexp.c3 index 4b0388718..3a40a9a46 100644 --- a/lib/std/math/math_nolibc/ldexp.c3 +++ b/lib/std/math/math_nolibc/ldexp.c3 @@ -1,67 +1,67 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn double ldexp(double x, int exp) @extern("ldexp") -{ - uint hx = x.high_word(); - int hexp = (int)((hx & 0x7ff00000) >> 20); - - - if (hexp == 0x7ff) return x; - if (hexp == 0) - { - // subnormal number handling - x *= 0x1p64; - hx = x.high_word(); - hexp = (int)((hx & 0x7ff00000) >> 20) - 64; - } - - // new exponent calculation - hexp += exp; - - if (hexp > 0x7fe) return x * double.inf; - if (hexp < 1) - { - x *= 0x1p-1022; - hexp += 1022; - if (hexp < 1) x *= 0x1p-1022; - return x; - } - - // set new exponent - hx = ((ulong)hx & 0x800fffff) | ((ulong)hexp << 20); - { - ulong rep = ((ulong)hx << 32) | x.low_word(); - return bitcast(rep, double); - } -} - -fn float ldexpf(float x, int exp) @extern("ldexpf") -{ - uint ix = x.word(); - int hexp = (int)((ix & 0x7f800000) >> 23); - - if (hexp == 0xff) return x; - if (hexp == 0) - { - // subnormal number handling - x *= 0x1p64f; - ix = x.word(); - hexp = (int)((ix & 0x7f800000) >> 23) - 64; - } - - // new exponent calculation - hexp += exp; - - if (hexp > 0xfe) return x * float.inf; - if (hexp < 1) - { - x *= 0x1p-126f; - hexp += 126; - if (hexp < 1) x *= 0x1p-126f; - return x; - } - - // set new exponent - ix = (ix & 0x807fffff) | (hexp << 23); - return bitcast(ix, float); -} +module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); + +fn double ldexp(double x, int exp) @extern("ldexp") +{ + uint hx = x.high_word(); + int hexp = (int)((hx & 0x7ff00000) >> 20); + + + if (hexp == 0x7ff) return x; + if (hexp == 0) + { + // subnormal number handling + x *= 0x1p64; + hx = x.high_word(); + hexp = (int)((hx & 0x7ff00000) >> 20) - 64; + } + + // new exponent calculation + hexp += exp; + + if (hexp > 0x7fe) return x * double.inf; + if (hexp < 1) + { + x *= 0x1p-1022; + hexp += 1022; + if (hexp < 1) x *= 0x1p-1022; + return x; + } + + // set new exponent + hx = ((ulong)hx & 0x800fffff) | ((ulong)hexp << 20); + { + ulong rep = ((ulong)hx << 32) | x.low_word(); + return bitcast(rep, double); + } +} + +fn float ldexpf(float x, int exp) @extern("ldexpf") +{ + uint ix = x.word(); + int hexp = (int)((ix & 0x7f800000) >> 23); + + if (hexp == 0xff) return x; + if (hexp == 0) + { + // subnormal number handling + x *= 0x1p64f; + ix = x.word(); + hexp = (int)((ix & 0x7f800000) >> 23) - 64; + } + + // new exponent calculation + hexp += exp; + + if (hexp > 0xfe) return x * float.inf; + if (hexp < 1) + { + x *= 0x1p-126f; + hexp += 126; + if (hexp < 1) x *= 0x1p-126f; + return x; + } + + // set new exponent + ix = (ix & 0x807fffff) | (hexp << 23); + return bitcast(ix, float); +} diff --git a/lib/std/math/math_quaternion.c3 b/lib/std/math/math_quaternion.c3 index 226c76231..18a7940ef 100644 --- a/lib/std/math/math_quaternion.c3 +++ b/lib/std/math/math_quaternion.c3 @@ -1,4 +1,4 @@ -module std::math::quaternion(); +module std::math::quaternion{Real}; import std::math::vector; union Quaternion { diff --git a/lib/std/math/math_vector.c3 b/lib/std/math/math_vector.c3 index 48c2bd80a..1d686a8cc 100644 --- a/lib/std/math/math_vector.c3 +++ b/lib/std/math/math_vector.c3 @@ -66,8 +66,8 @@ fn Vec3 Vec3.refract(self, Vec3 n, double r) => refract3(self, n, r); fn void ortho_normalize(Vec3f* v1, Vec3f* v2) => ortho_normalize3(v1, v2); fn void ortho_normalized(Vec3* v1, Vec3* v2) => ortho_normalize3(v1, v2); -fn Matrix4f matrix4f_look_at(Vec3f eye, Vec3f target, Vec3f up) @deprecated => matrix::look_at()(eye, target, up); -fn Matrix4 matrix4_look_at(Vec3 eye, Vec3 target, Vec3 up) @deprecated => matrix::look_at()(eye, target, up); +fn Matrix4f matrix4f_look_at(Vec3f eye, Vec3f target, Vec3f up) @deprecated => matrix::look_at{float}(eye, target, up); +fn Matrix4 matrix4_look_at(Vec3 eye, Vec3 target, Vec3 up) @deprecated => matrix::look_at{double}(eye, target, up); fn Vec3f Vec3f.rotate_quat(self, Quaternionf q) => rotate_by_quat3(self, q); fn Vec3 Vec3.rotate_quat(self, Quaternion q) => rotate_by_quat3(self, q); diff --git a/lib/std/math/uuid.c3 b/lib/std/math/uuid.c3 index 8d309c2c6..f5648bc27 100644 --- a/lib/std/math/uuid.c3 +++ b/lib/std/math/uuid.c3 @@ -35,7 +35,7 @@ fn usz! Uuid.to_format(&self, Formatter* formatter) @dynamic (*self)[10], (*self)[11], (*self)[12], (*self)[13], (*self)[14], (*self)[15]); } -fn String Uuid.to_string(&self, Allocator allocator) @dynamic +fn String Uuid.to_string(&self, Allocator allocator) { - return string::new_format("%s", *self, allocator: allocator); + return string::format(allocator, "%s", *self); } \ No newline at end of file diff --git a/lib7/std/net/http.c3 b/lib/std/net/http.c3 similarity index 100% rename from lib7/std/net/http.c3 rename to lib/std/net/http.c3 diff --git a/lib/std/net/inetaddr.c3 b/lib/std/net/inetaddr.c3 index b146d14fa..1f82bb2ad 100644 --- a/lib/std/net/inetaddr.c3 +++ b/lib/std/net/inetaddr.c3 @@ -56,19 +56,14 @@ fn usz! InetAddress.to_format(InetAddress* addr, Formatter* formatter) @dynamic return formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!; } -fn String InetAddress.to_new_string(InetAddress* addr, Allocator allocator = allocator::heap()) @dynamic +fn String InetAddress.to_string(&self, Allocator allocator) { - if (addr.is_ipv6) - { - char[8 * 5 + 1] buffer; - String res = (String)io::bprintf(&buffer, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", - addr.ipv6.a, addr.ipv6.b, addr.ipv6.c, addr.ipv6.d, - addr.ipv6.e, addr.ipv6.f, addr.ipv6.g, addr.ipv6.h)!!; - return res.copy(allocator); - } - char[3 * 4 + 3 + 1] buffer; - String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!!; - return res.copy(allocator); + return string::format(allocator, "%s", *self); +} + +fn String InetAddress.to_tstring(&self) +{ + return string::format(tmem(), "%s", *self); } fn InetAddress! ipv6_from_str(String s) diff --git a/lib/std/net/net.c3 b/lib/std/net/net.c3 index b1dc258fb..7a5f600d5 100644 --- a/lib/std/net/net.c3 +++ b/lib/std/net/net.c3 @@ -58,14 +58,9 @@ fn uint! ipv4toint(String s) return out; } -fn String! int_to_new_ipv4(uint val, Allocator allocator = allocator::heap()) +fn String! int_to_ipv4(uint val, Allocator allocator) { char[3 * 4 + 3 + 1] buffer; String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)!; return res.copy(allocator); } - -fn String! int_to_temp_ipv4(uint val) -{ - return int_to_new_ipv4(val, allocator::temp()); -} diff --git a/lib/std/net/url.c3 b/lib/std/net/url.c3 index b41f1ab87..e88da8e24 100644 --- a/lib/std/net/url.c3 +++ b/lib/std/net/url.c3 @@ -49,7 +49,7 @@ struct Url(Printable) @require url_string.len > 0 "the url_string must be len 1 or more" @return "the parsed Url" *> -fn Url! temp_parse(String url_string) => new_parse(url_string, allocator::temp()); +fn Url! tparse(String url_string) => parse(tmem(), url_string); <* Parse a URL string into a Url struct. @@ -58,7 +58,7 @@ fn Url! temp_parse(String url_string) => new_parse(url_string, allocator::temp() @require url_string.len > 0 "the url_string must be len 1 or more" @return "the parsed Url" *> -fn Url! new_parse(String url_string, Allocator allocator = allocator::heap()) +fn Url! parse(Allocator allocator, String url_string) { url_string = url_string.trim(); if (!url_string) return UrlParsingResult.EMPTY?; @@ -76,7 +76,7 @@ fn Url! new_parse(String url_string, Allocator allocator = allocator::heap()) // Handle schemes without authority like 'mailto:' if (!pos) return UrlParsingResult.INVALID_SCHEME?; url.scheme = url_string[:pos].copy(allocator); - url.path = decode(url_string[pos + 1 ..], PATH, allocator) ?? UrlParsingResult.INVALID_PATH?!; + url.path = decode(allocator, url_string[pos + 1 ..], PATH) ?? UrlParsingResult.INVALID_PATH?!; return url; } @@ -98,8 +98,8 @@ fn Url! new_parse(String url_string, Allocator allocator = allocator::heap()) if (!username.len) return UrlParsingResult.INVALID_USER?; url.host = - url.username = decode(username, HOST, allocator) ?? UrlParsingResult.INVALID_USER?!; - if (userpass.len) url.password = decode(userpass[1], USERPASS, allocator) ?? UrlParsingResult.INVALID_PASSWORD?!; + url.username = decode(allocator, username, HOST) ?? UrlParsingResult.INVALID_USER?!; + if (userpass.len) url.password = decode(allocator, userpass[1], USERPASS) ?? UrlParsingResult.INVALID_PASSWORD?!; }; authority = authority[userinfo.len + 1 ..]; } @@ -131,7 +131,7 @@ fn Url! new_parse(String url_string, Allocator allocator = allocator::heap()) } }; } - url.host = decode(host, HOST, allocator) ?? UrlParsingResult.INVALID_HOST?!; + url.host = decode(allocator, host, HOST) ?? UrlParsingResult.INVALID_HOST?!; url_string = url_string[authority_end ..]; } @@ -142,12 +142,12 @@ fn Url! new_parse(String url_string, Allocator allocator = allocator::heap()) if (@ok(query_index) || @ok(fragment_index)) { usz path_end = min(query_index ?? url_string.len, fragment_index ?? url_string.len); - url.path = decode(url_string[:path_end], PATH, allocator) ?? UrlParsingResult.INVALID_PATH?!; + url.path = decode(allocator, url_string[:path_end], PATH) ?? UrlParsingResult.INVALID_PATH?!; url_string = url_string[path_end ..]; } else { - url.path = decode(url_string, PATH, allocator) ?? UrlParsingResult.INVALID_PATH?!; + url.path = decode(allocator, url_string, PATH) ?? UrlParsingResult.INVALID_PATH?!; url_string = ""; } @@ -165,86 +165,86 @@ fn Url! new_parse(String url_string, Allocator allocator = allocator::heap()) // Parse fragment if (url_string.starts_with("#")) { - url.fragment = decode(url_string[1..], FRAGMENT, allocator) ?? UrlParsingResult.INVALID_FRAGMENT?!; + url.fragment = decode(allocator, url_string[1..], FRAGMENT) ?? UrlParsingResult.INVALID_FRAGMENT?!; } return url; } -<* - Stringify a Url struct. - - @param [in] self - @param [inout] allocator - @return "Url as a string" -*> -fn String Url.to_string(&self, Allocator allocator = allocator::heap()) @dynamic => @pool(allocator) +fn usz! Url.to_format(&self, Formatter* f) @dynamic { - DString builder = dstring::temp_new(); - + usz len; // Add scheme if it exists if (self.scheme != "") { - builder.append_chars(self.scheme); - builder.append_char(':'); - if (self.host.len > 0) builder.append_chars("//"); + len += f.print(self.scheme)!; + len += f.print(":")!; + if (self.host.len > 0) len += f.print("//")!; } // Add username and password if they exist - if (self.username != "") + if (self.username) { - String username = temp_encode(self.username, USERPASS); - builder.append_chars(username); - - if (self.password != "") + @stack_mem(64; Allocator smem) { - builder.append_char(':'); - - String password = temp_encode(self.password, USERPASS); - builder.append_chars(password); + len += f.print(encode(smem, self.username, USERPASS))!; + }; + if (self.password) + { + len += f.print(":")!; + @stack_mem(64; Allocator smem) + { + len += f.print(encode(smem, self.password, USERPASS))!; + }; } - builder.append_char('@'); + len += f.print("@")!; } // Add host - String host = temp_encode(self.host, HOST); - builder.append_chars(host); + @stack_mem(128; Allocator smem) + { + len += f.print(encode(smem, self.host, HOST))!; + }; // Add port - if (self.port != 0) - { - builder.append_char(':'); - builder.appendf("%d", self.port); - } + if (self.port) len += f.printf(":%d", self.port)!; // Add path - String path = temp_encode(self.path, PATH); - builder.append_chars(path); + @stack_mem(256; Allocator smem) + { + len += f.print(encode(smem, self.path, PATH))!; + }; // Add query if it exists (note that `query` is expected to // be already properly encoded). - if (self.query != "") + if (self.query) { - builder.append_char('?'); - builder.append_chars(self.query); + len += f.print("?")!; + len += f.print(self.query)!; } // Add fragment if it exists - if (self.fragment != "") + if (self.fragment) { - builder.append_char('#'); - - String fragment = temp_encode(self.fragment, FRAGMENT); - builder.append_chars(fragment); + @stack_mem(256; Allocator smem) + { + len += f.print("#")!; + len += f.print(encode(smem, self.fragment, FRAGMENT))!; + }; } - - return builder.copy_str(allocator); + return len; } -def UrlQueryValueList = List(); +fn String Url.to_string(&self, Allocator allocator) +{ + return string::format(allocator, "%s", *self); +} + + +def UrlQueryValueList = List{String}; struct UrlQueryValues { - inline HashMap() map; + inline HashMap{String, UrlQueryValueList} map; UrlQueryValueList key_order; } @@ -254,15 +254,7 @@ struct UrlQueryValues @param [in] query @return "a UrlQueryValues HashMap" *> -fn UrlQueryValues temp_parse_query(String query) => parse_query(query, allocator::temp()); - -<* - Parse the query parameters of the Url into a UrlQueryValues map. - - @param [in] query - @return "a UrlQueryValues HashMap" -*> -fn UrlQueryValues new_parse_query(String query) => parse_query(query, allocator::heap()); +fn UrlQueryValues parse_query_to_temp(String query) => parse_query(tmem(), query); <* Parse the query parameters of the Url into a UrlQueryValues map. @@ -271,7 +263,7 @@ fn UrlQueryValues new_parse_query(String query) => parse_query(query, allocator: @param [inout] allocator @return "a UrlQueryValues HashMap" *> -fn UrlQueryValues parse_query(String query, Allocator allocator) +fn UrlQueryValues parse_query(Allocator allocator, String query) { UrlQueryValues vals; vals.map.init(allocator); @@ -283,8 +275,8 @@ fn UrlQueryValues parse_query(String query, Allocator allocator) @pool(allocator) { String[] parts = rv.tsplit("=", 2); - String key = temp_decode(parts[0], QUERY) ?? parts[0]; - vals.add(key, parts.len == 1 ? key : (temp_decode(parts[1], QUERY) ?? parts[1])); + String key = tdecode(parts[0], QUERY) ?? parts[0]; + vals.add(key, parts.len == 1 ? key : (tdecode(parts[1], QUERY) ?? parts[1])); }; } return vals; @@ -317,41 +309,35 @@ fn UrlQueryValues* UrlQueryValues.add(&self, String key, String value) } -<* - Stringify UrlQueryValues into an encoded query string. - @param [in] self - @param [inout] allocator - @return "a percent-encoded query string" -*> -fn String UrlQueryValues.to_string(&self, Allocator allocator = allocator::heap()) @dynamic => @pool(allocator) +fn usz! UrlQueryValues.to_format(&self, Formatter* f) @dynamic { - DString builder = dstring::temp_new(); - + usz len; usz i; foreach (key: self.key_order) { - String encoded_key = temp_encode(key, QUERY); - - UrlQueryValueList! values = self.map.get(key); - if (catch values) continue; - - foreach (value: values) + @stack_mem(128; Allocator mem) { - if (i > 0) builder.append_char('&'); - - builder.append_chars(encoded_key); - builder.append_char('='); - - String encoded_value = temp_encode(value, QUERY); - builder.append_chars(encoded_value); - i++; - } - }; - - return builder.copy_str(allocator); + String encoded_key = encode(mem, key, QUERY); + UrlQueryValueList! values = self.map.get(key); + if (catch values) continue; + foreach (value : values) + { + if (i > 0) len += f.print("&")!; + len += f.print(encoded_key)!; + len += f.print("=")!; + @stack_mem(256; Allocator smem) + { + len += f.print(encode(smem, value, QUERY))!; + }; + i++; + } + }; + } + return len; } + fn void UrlQueryValues.free(&self) { self.map.@each(;String key, UrlQueryValueList values) diff --git a/lib/std/net/url_encoding.c3 b/lib/std/net/url_encoding.c3 index 314a85b0e..2bef12573 100644 --- a/lib/std/net/url_encoding.c3 +++ b/lib/std/net/url_encoding.c3 @@ -67,7 +67,7 @@ fn usz encode_len(String s, UrlEncodingMode mode) @inline @param [inout] allocator @return "Percent-encoded String" *> -fn String encode(String s, UrlEncodingMode mode, Allocator allocator) => @pool(allocator) +fn String encode(Allocator allocator, String s, UrlEncodingMode mode) => @pool(allocator) { usz n = encode_len(s, mode); DString builder = dstring::temp_with_capacity(n); @@ -83,8 +83,8 @@ fn String encode(String s, UrlEncodingMode mode, Allocator allocator) => @pool(a // add encoded char case should_encode(c, mode): builder.append_char('%'); - String hex = hex::encode_temp(s[i:1]); - builder.append(hex.temp_ascii_to_upper()); + String hex = hex::tencode(s[i:1]); + builder.append(hex.to_upper_tcopy()); // use char, no encoding needed default: @@ -95,15 +95,6 @@ fn String encode(String s, UrlEncodingMode mode, Allocator allocator) => @pool(a return builder.copy_str(allocator); } -<* - Encode the string s for a given encoding mode. - Returned string must be freed. - - @param s "String to encode" - @param mode "Url encoding mode" - @return "Percent-encoded String" -*> -fn String new_encode(String s, UrlEncodingMode mode) => encode(s, mode, allocator::heap()); <* Encode string s for a given encoding mode, stored on the temp allocator. @@ -112,7 +103,7 @@ fn String new_encode(String s, UrlEncodingMode mode) => encode(s, mode, allocato @param mode "Url encoding mode" @return "Percent-encoded String" *> -fn String temp_encode(String s, UrlEncodingMode mode) => encode(s, mode, allocator::temp()); +fn String tencode(String s, UrlEncodingMode mode) => encode(tmem(), s, mode); <* Calculate the length of the percent-decoded string. @@ -143,7 +134,7 @@ fn usz! decode_len(String s, UrlEncodingMode mode) @inline @param [inout] allocator @return "Percent-decoded String" *> -fn String! decode(String s, UrlEncodingMode mode, Allocator allocator) => @pool(allocator) +fn String! decode(Allocator allocator, String s, UrlEncodingMode mode) => @pool(allocator) { usz n = decode_len(s, mode)!; DString builder = dstring::temp_with_capacity(n); @@ -154,7 +145,7 @@ fn String! decode(String s, UrlEncodingMode mode, Allocator allocator) => @pool { // decode encoded char case '%': - char[] hex = hex::decode_temp(s[i+1:2])!; + char[] hex = hex::tdecode(s[i+1:2])!; builder.append(hex); i += 2; @@ -171,15 +162,6 @@ fn String! decode(String s, UrlEncodingMode mode, Allocator allocator) => @pool return builder.copy_str(allocator); } -<* - Decode string s for a given encoding mode. - Returned string must be freed. - - @param s "String to decode" - @param mode "Url encoding mode" - @return "Percent-decoded String" -*> -fn String! new_decode(String s, UrlEncodingMode mode) => decode(s, mode, allocator::heap()); <* Decode string s for a given encoding mode, stored on the temp allocator. @@ -188,4 +170,4 @@ fn String! new_decode(String s, UrlEncodingMode mode) => decode(s, mode, alloca @param mode "Url encoding mode" @return "Percent-decoded String" *> -fn String! temp_decode(String s, UrlEncodingMode mode) => decode(s, mode, allocator::temp()); +fn String! tdecode(String s, UrlEncodingMode mode) => decode(tmem(), s, mode); diff --git a/lib/std/os/backtrace.c3 b/lib/std/os/backtrace.c3 index f7c6f0bf1..39ddce575 100644 --- a/lib/std/os/backtrace.c3 +++ b/lib/std/os/backtrace.c3 @@ -55,7 +55,7 @@ fn void Backtrace.free(&self) allocator::free(self.allocator, self.file); } -fn Backtrace* Backtrace.init(&self, uptr offset, String function, String object_file, String file = "", uint line = 0, Allocator allocator) +fn Backtrace* Backtrace.init(&self, Allocator allocator, uptr offset, String function, String object_file, String file = "", uint line = 0) { if (!allocator) { @@ -91,13 +91,13 @@ fn void*[] capture_current(void*[] buffer) $endswitch } -def BacktraceList = List(); +def BacktraceList = List{Backtrace}; def symbolize_backtrace = linux::symbolize_backtrace @if(env::LINUX); def symbolize_backtrace = win32::symbolize_backtrace @if(env::WIN32); def symbolize_backtrace = darwin::symbolize_backtrace @if(env::DARWIN); -fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator) @if(!env::NATIVE_STACKTRACE) +fn BacktraceList! symbolize_backtrace(Allocator allocator, void*[] backtrace) @if(!env::NATIVE_STACKTRACE) { return {}; } \ No newline at end of file diff --git a/lib/std/os/env.c3 b/lib/std/os/env.c3 index c4b23e88d..1d60b0f9a 100644 --- a/lib/std/os/env.c3 +++ b/lib/std/os/env.c3 @@ -9,7 +9,7 @@ import std::io::path, libc, std::os; @require name.len > 0 @return! SearchResult.MISSING *> -fn String! get_var(String name, Allocator allocator = allocator::heap()) => @pool(allocator) +fn String! get_var(Allocator allocator, String name) => @pool(allocator) { $switch @@ -20,7 +20,7 @@ fn String! get_var(String name, Allocator allocator = allocator::heap()) => @poo // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getenvironmentvariable const usz BUFSIZE = 1024; WString buff = (WString)tcalloc(BUFSIZE * 2 + 2); - WString wstr = name.to_temp_wstring()!; + WString wstr = name.to_wstring_tcopy()!; usz len = win32::getEnvironmentVariableW(wstr, buff, BUFSIZE); if (len == 0) return SearchResult.MISSING?; if (len > BUFSIZE) @@ -28,15 +28,15 @@ fn String! get_var(String name, Allocator allocator = allocator::heap()) => @poo buff = (WString)tmalloc(len * 2 + 2); win32::getEnvironmentVariableW(wstr, buff, (Win32_DWORD)len); } - return string::new_from_wstring(buff, allocator); + return string::new_from_wstring(allocator, buff); $default: return ""; $endswitch } -fn String! get_var_temp(String name) +fn String! tget_var(String name) { - return get_var(name, allocator::temp()); + return get_var(tmem(), name); } <* @@ -48,14 +48,14 @@ fn bool set_var(String name, String value, bool overwrite = true) => @pool() { $switch $case env::WIN32: - WString wname = name.to_temp_wstring()!!; + WString wname = name.to_wstring_tcopy()!!; if (!overwrite) { Char16[8] buff; if (win32::getEnvironmentVariableW(wname, &buff, 8) > 0) return true; } // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setenvironmentvariable - return (win32::setEnvironmentVariableW(wname, value.to_temp_wstring()) ?? 1) == 0; + return (win32::setEnvironmentVariableW(wname, value.to_wstring_tcopy()) ?? 1) == 0; $case env::LIBC && !env::WIN32: return libc::setenv(name.zstr_tcopy(), value.zstr_tcopy(), (int)overwrite) == 0; $default: @@ -74,30 +74,26 @@ fn String! get_home_dir(Allocator using = allocator::heap()) $else home = "USERPROFILE"; $endif - return get_var(home, using); + return get_var(using, home); } -fn Path! get_config_dir(Allocator allocator = allocator::heap()) @deprecated("use new_get_config_dir()") -{ - return new_get_config_dir(allocator) @inline; -} <* Returns the current user's config directory. *> -fn Path! new_get_config_dir(Allocator allocator = allocator::heap()) => @pool(allocator) +fn Path! get_config_dir(Allocator allocator) => @pool(allocator) { $if env::WIN32: - return path::new(get_var_temp("AppData"), allocator); + return path::new(allocator, tget_var("AppData")); $else $if env::DARWIN: - String s = get_var_temp("HOME")!; + String s = tget_var("HOME")!; const DIR = "Library/Application Support"; $else - String s = get_var_temp("XDG_CONFIG_HOME") ?? get_var_temp("HOME")!; + String s = tget_var("XDG_CONFIG_HOME") ?? tget_var("HOME")!; const DIR = ".config"; $endif - return path::temp_new(s).new_append(DIR, allocator: allocator); + return path::temp(s).append(allocator, DIR); $endif } @@ -110,7 +106,7 @@ fn bool clear_var(String name) => @pool() { $switch $case env::WIN32: - WString wname = name.to_temp_wstring()!!; + WString wname = name.to_wstring_tcopy()!!; return win32::setEnvironmentVariableW(wname, null) == 0; $case env::LIBC && !env::WIN32: return libc::unsetenv(name.zstr_tcopy()) == 0; @@ -119,16 +115,11 @@ fn bool clear_var(String name) => @pool() $endswitch } -fn String! executable_path(Allocator allocator = allocator::heap()) @deprecated("use new_executable_path()") -{ - return new_executable_path(allocator) @inline; -} - -fn String! new_executable_path(Allocator allocator = allocator::heap()) +fn String! executable_path(Allocator allocator) { $if env::DARWIN: return darwin::executable_path(allocator); $else return SearchResult.MISSING?; $endif -} \ No newline at end of file +} diff --git a/lib/std/os/linux/linux.c3 b/lib/std/os/linux/linux.c3 index 83fd1a81b..50bf56107 100644 --- a/lib/std/os/linux/linux.c3 +++ b/lib/std/os/linux/linux.c3 @@ -128,17 +128,17 @@ fn ulong! elf_module_image_base(String path) @local return 0; } -fn void! backtrace_add_from_exec(BacktraceList* list, void* addr, Allocator allocator) @local +fn void! backtrace_add_from_exec(Allocator allocator, BacktraceList* list, void* addr) @local { char[] buf = mem::temp_alloc_array(char, 1024); String exec_path = process::execute_stdout_to_buffer(buf, {"realpath", "-e", string::tformat("/proc/%d/exe", posix::getpid())})!; String obj_name = exec_path.copy(allocator); String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", exec_path, string::tformat("0x%x", addr)})!; - return backtrace_add_addr2line(list, addr, addr2line, obj_name, "???", allocator); + return backtrace_add_addr2line(allocator, list, addr, addr2line, obj_name, "???"); } -fn void! backtrace_add_from_dlinfo(BacktraceList* list, void* addr, Linux_Dl_info* info, Allocator allocator) @local +fn void! backtrace_add_from_dlinfo(Allocator allocator, BacktraceList* list, void* addr, Linux_Dl_info* info) @local { char[] buf = mem::temp_alloc_array(char, 1024); @@ -146,10 +146,10 @@ fn void! backtrace_add_from_dlinfo(BacktraceList* list, void* addr, Linux_Dl_inf ZString obj_path = info.dli_fname; String sname = info.dli_sname ? info.dli_sname.str_view() : "???"; String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::tformat("0x%x", obj_addr - 1)})!; - return backtrace_add_addr2line(list, addr, addr2line, info.dli_fname.str_view(), sname, allocator); + return backtrace_add_addr2line(allocator, list, addr, addr2line, info.dli_fname.str_view(), sname); } -fn Backtrace! backtrace_line_parse(String string, String obj_name, String func_name, bool is_inlined, Allocator allocator) +fn Backtrace! backtrace_line_parse(Allocator allocator, String string, String obj_name, String func_name, bool is_inlined) { String[] parts = string.trim().tsplit(" at "); if (parts.len != 2) return SearchResult.MISSING?; @@ -171,14 +171,14 @@ fn Backtrace! backtrace_line_parse(String string, String obj_name, String func_n .is_inline = is_inlined }; } -fn void! backtrace_add_addr2line(BacktraceList* list, void* addr, String addr2line, String obj_name, String func_name, Allocator allocator) @local +fn void! backtrace_add_addr2line(Allocator allocator, BacktraceList* list, void* addr, String addr2line, String obj_name, String func_name) @local { String[] inline_parts = addr2line.tsplit("(inlined by)"); usz last = inline_parts.len - 1; foreach (i, part : inline_parts) { bool is_inline = i != last; - Backtrace! trace = backtrace_line_parse(part, obj_name, func_name, is_inline, allocator); + Backtrace! trace = backtrace_line_parse(allocator, part, obj_name, func_name, is_inline); if (catch trace) { list.push({ @@ -196,7 +196,7 @@ fn void! backtrace_add_addr2line(BacktraceList* list, void* addr, String addr2li } } -fn void! backtrace_add_element(BacktraceList *list, void* addr, Allocator allocator = allocator::heap()) @local +fn void! backtrace_add_element(Allocator allocator, BacktraceList *list, void* addr) @local { if (!addr) { @@ -209,13 +209,13 @@ fn void! backtrace_add_element(BacktraceList *list, void* addr, Allocator alloca Linux_Dl_info info; if (dladdr(addr, &info) == 0) { - return backtrace_add_from_exec(list, addr, allocator); + return backtrace_add_from_exec(allocator, list, addr); } - return backtrace_add_from_dlinfo(list, addr, &info, allocator); + return backtrace_add_from_dlinfo(allocator, list, addr, &info); }; } -fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator) +fn BacktraceList! symbolize_backtrace(Allocator allocator, void*[] backtrace) { BacktraceList list; list.init(allocator, backtrace.len); @@ -231,7 +231,7 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator) { foreach (addr : backtrace) { - backtrace_add_element(&list, addr, allocator)!; + backtrace_add_element(allocator, &list, addr)!; } }; return list; diff --git a/lib/std/os/macos/darwin.c3 b/lib/std/os/macos/darwin.c3 index 8d5cc3716..f4805855f 100644 --- a/lib/std/os/macos/darwin.c3 +++ b/lib/std/os/macos/darwin.c3 @@ -80,7 +80,7 @@ fn uptr! load_address() @local { Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT"); if (!cmd) return BacktraceFault.SEGMENT_NOT_FOUND?; - String path = env::new_executable_path(allocator::temp()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!; + String path = env::executable_path(tmem()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!; uint dyld_count = darwin::_dyld_image_count(); for (uint i = 0; i < dyld_count; i++) { @@ -132,7 +132,7 @@ 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; diff --git a/lib/std/os/subprocess.c3 b/lib/std/os/subprocess.c3 index 9227bdac2..2d374970e 100644 --- a/lib/std/os/subprocess.c3 +++ b/lib/std/os/subprocess.c3 @@ -109,7 +109,7 @@ fn WString convert_command_line_win32(String[] command_line) @inline @if(env::WI str.append('"'); } str.append('\0'); - return str.str_view().to_temp_wstring()!!; + return str.str_view().to_wstring_tcopy()!!; } <* @@ -149,7 +149,7 @@ fn SubProcess! create(String[] command_line, SubProcessOptions options = {}, Str env.append("\0"); } env.append("\0"); - used_environment = env.str_view().to_temp_wstring()!; + used_environment = env.str_view().to_wstring_tcopy()!; } int fd = win32::_open_osfhandle((iptr)wr, 0); if (fd != -1) diff --git a/lib/std/os/win32/process.c3 b/lib/std/os/win32/process.c3 index 39606e2f3..05fe742ea 100644 --- a/lib/std/os/win32/process.c3 +++ b/lib/std/os/win32/process.c3 @@ -154,7 +154,7 @@ struct Symbol Win32_DWORD64 displacement; -fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator) +fn BacktraceList! symbolize_backtrace(Allocator allocator, void*[] backtrace) { BacktraceList list; list.init(allocator, backtrace.len); @@ -163,12 +163,12 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator) defer symCleanup(process); foreach (addr : backtrace) { - list.push(resolve_backtrace(addr, process, allocator) ?? backtrace::BACKTRACE_UNKNOWN); + list.push(resolve_backtrace(allocator, addr, process) ?? backtrace::BACKTRACE_UNKNOWN); } return list; } -fn Backtrace! resolve_backtrace(void* addr, Win32_HANDLE process, Allocator allocator) +fn Backtrace! resolve_backtrace(Allocator allocator, void* addr, Win32_HANDLE process) { Symbol symbol; //Win32_DWORD image_type = load_modules()!; @@ -193,10 +193,10 @@ fn Backtrace! resolve_backtrace(void* addr, Win32_HANDLE process, Allocator allo ZString zname = (ZString)&name; if (!symGetLineFromAddr64(process, (Win32_ULONG64)addr - 1, &offset, &line)) { - backtrace.init((uptr)addr, zname.str_view(), module_name.str_view(), allocator: allocator); + backtrace.init(allocator, (uptr)addr, zname.str_view(), module_name.str_view()); return backtrace; } String filename = ((ZString)line.fileName).str_view(); - backtrace.init((uptr)addr, zname.str_view(), module_name.str_view(), file: filename, line: line.lineNumber, allocator: allocator); + backtrace.init(allocator, (uptr)addr, zname.str_view(), module_name.str_view(), file: filename, line: line.lineNumber); return backtrace; } diff --git a/lib/std/sort/countingsort.c3 b/lib/std/sort/countingsort.c3 index 38598059e..09ab21a32 100644 --- a/lib/std/sort/countingsort.c3 +++ b/lib/std/sort/countingsort.c3 @@ -11,20 +11,20 @@ Sort list using the counting sort algorithm. macro countingsort(list, key_fn = EMPTY_MACRO_SLOT) @builtin { usz len = sort::len_from_list(list); - cs::csort(<$typeof(list), $typeof(key_fn)>)(list, 0, len, key_fn, ~((uint)0)); + cs::csort{$typeof(list), $typeof(key_fn)}(list, 0, len, key_fn, ~((uint)0)); } macro insertionsort_indexed(list, start, end, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin { - is::isort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, (usz)start, (usz)end, cmp, context); + is::isort{$typeof(list), $typeof(cmp), $typeof(context)}(list, (usz)start, (usz)end, cmp, context); } macro quicksort_indexed(list, start, end, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin { - qs::qsort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, (isz)start, (isz)(end-1), cmp, context); + qs::qsort{$typeof(list), $typeof(cmp), $typeof(context)}(list, (isz)start, (isz)(end-1), cmp, context); } -module std::sort::cs(); +module std::sort::cs{Type, KeyFn}; def Counts = usz[256] @private; def Ranges = usz[257] @private; diff --git a/lib/std/sort/insertionsort.c3 b/lib/std/sort/insertionsort.c3 index 6e1ed6953..f3106067c 100644 --- a/lib/std/sort/insertionsort.c3 +++ b/lib/std/sort/insertionsort.c3 @@ -10,14 +10,14 @@ macro insertionsort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @b { $if @typekind(list) == POINTER &&& (@typekind(*list) == ARRAY || @typekind(*list) == VECTOR): $typeof((*list)[0])[] list2 = list; - is::isort(<$typeof(list2), $typeof(cmp), $typeof(context)>)(list2, 0, list.len, cmp, context); + is::isort{$typeof(list2), $typeof(cmp), $typeof(context)}(list2, 0, list.len, cmp, context); $else usz len = sort::len_from_list(list); - is::isort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len, cmp, context); + is::isort{$typeof(list), $typeof(cmp), $typeof(context)}(list, 0, (isz)len, cmp, context); $endif } -module std::sort::is(); +module std::sort::is{Type, CmpFn, Context}; def ElementType = $typeof(((Type){})[0]); diff --git a/lib/std/sort/quicksort.c3 b/lib/std/sort/quicksort.c3 index 1dcb22d85..ca7e77eb2 100644 --- a/lib/std/sort/quicksort.c3 +++ b/lib/std/sort/quicksort.c3 @@ -11,10 +11,10 @@ macro quicksort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @built { $if @typekind(list) == POINTER &&& (@typekind(*list) == ARRAY || @typekind(*list) == VECTOR): $typeof((*list)[0])[] list2 = list; - qs::qsort(<$typeof(list2), $typeof(cmp), $typeof(context)>)(list2, 0, (isz)list.len - 1, cmp, context); + qs::qsort{$typeof(list2), $typeof(cmp), $typeof(context)}(list2, 0, (isz)list.len - 1, cmp, context); $else usz len = sort::len_from_list(list); - qs::qsort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len - 1, cmp, context); + qs::qsort{$typeof(list), $typeof(cmp), $typeof(context)}(list, 0, (isz)len - 1, cmp, context); $endif } @@ -30,10 +30,10 @@ list will be partially sorted. macro quickselect(list, isz k, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin { usz len = sort::len_from_list(list); - return qs::qselect(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len - 1, k, cmp, context); + return qs::qselect{$typeof(list), $typeof(cmp), $typeof(context)}(list, 0, (isz)len - 1, k, cmp, context); } -module std::sort::qs(); +module std::sort::qs{Type, CmpFn, Context}; def ElementType = $typeof(((Type){})[0]); diff --git a/lib7/std/text/i18n.c3 b/lib/std/text/i18n.c3 similarity index 100% rename from lib7/std/text/i18n.c3 rename to lib/std/text/i18n.c3 diff --git a/lib/std/threads/buffered_channel.c3 b/lib/std/threads/buffered_channel.c3 index 97407b16e..2d53c1f4d 100644 --- a/lib/std/threads/buffered_channel.c3 +++ b/lib/std/threads/buffered_channel.c3 @@ -1,4 +1,4 @@ -module std::thread::channel(); +module std::thread::channel{Type}; distinct BufferedChannel = void*; @@ -21,11 +21,6 @@ struct BufferedChannelImpl @private Type[?] buf; } -fn void! BufferedChannel.new_init(&self, usz size = 1) @deprecated("Use init(mem)") -{ - return self.init(mem, size); -} - fn void! BufferedChannel.init(&self, Allocator allocator, usz size = 1) { BufferedChannelImpl* channel = allocator::new_with_padding(allocator, BufferedChannelImpl, Type.sizeof * size)!; diff --git a/lib7/std/threads/event/event_thread.c3 b/lib/std/threads/event/event_thread.c3 similarity index 100% rename from lib7/std/threads/event/event_thread.c3 rename to lib/std/threads/event/event_thread.c3 diff --git a/lib/std/threads/pool.c3 b/lib/std/threads/pool.c3 index df80a7d30..55afb6c07 100644 --- a/lib/std/threads/pool.c3 +++ b/lib/std/threads/pool.c3 @@ -1,4 +1,4 @@ -module std::thread::pool(); +module std::thread::pool{SIZE}; import std::thread; struct ThreadPool diff --git a/lib/std/threads/unbuffered_channel.c3 b/lib/std/threads/unbuffered_channel.c3 index c2fd0674e..a17bf748d 100644 --- a/lib/std/threads/unbuffered_channel.c3 +++ b/lib/std/threads/unbuffered_channel.c3 @@ -1,4 +1,4 @@ -module std::thread::channel(); +module std::thread::channel{Type}; distinct UnbufferedChannel = void*; @@ -18,8 +18,6 @@ struct UnbufferedChannelImpl @private ConditionVariable read_cond; } -fn void! UnbufferedChannel.new_init(&self) @deprecated("Use init") => self.init(allocator::heap()); - fn void! UnbufferedChannel.init(&self, Allocator allocator) { UnbufferedChannelImpl* channel = allocator::alloc(allocator, UnbufferedChannelImpl); diff --git a/lib/std/time/format.c3 b/lib/std/time/format.c3 index 1c43f44ba..41ec56b6f 100644 --- a/lib/std/time/format.c3 +++ b/lib/std/time/format.c3 @@ -20,59 +20,54 @@ enum DateTimeFormat TIMEONLY, // "15:04:05" } -fn String format(DateTimeFormat type, TzDateTime dt, Allocator allocator) +fn String format(Allocator allocator, DateTimeFormat type, TzDateTime dt) { switch (type) { case ANSIC: - return string::format("%s %s %2d %02d:%02d:%02d %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, dt.year, allocator: allocator); + return string::format(allocator, "%s %s %2d %02d:%02d:%02d %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, dt.year); case UNIXDATE: - return string::format("%s %s %2d %02d:%02d:%02d GMT %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, dt.year, allocator: allocator); + return string::format(allocator, "%s %s %2d %02d:%02d:%02d GMT %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, dt.year); case RUBYDATE: - return string::format("%s %s %2d %02d:%02d:%02d %s %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix(dt.gmt_offset), dt.year, allocator: allocator); + return string::format(allocator, "%s %s %2d %02d:%02d:%02d %s %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix(dt.gmt_offset), dt.year); case RFC822: dt = dt.to_gmt_offset(0); // For named representations of the timezone we always go for GMT, which is required by some RFCs - return string::format("%02d %s %02d %02d:%02d GMT", dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min, allocator: allocator); + return string::format(allocator, "%02d %s %02d %02d:%02d GMT", dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min); case RFC822Z: - return string::format("%02d %s %02d %02d:%02d %s", dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min, temp_numeric_tzsuffix(dt.gmt_offset), allocator: allocator); + return string::format(allocator, "%02d %s %02d %02d:%02d %s", dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min, temp_numeric_tzsuffix(dt.gmt_offset)); case RFC850: dt = dt.to_gmt_offset(0); // For named representations of the timezone we always go for GMT, which is required by some RFCs - return string::format("%s, %02d-%s-%02d %02d:%02d:%02d GMT", dt.weekday.name, dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min, dt.sec, allocator: allocator); + return string::format(allocator, "%s, %02d-%s-%02d %02d:%02d:%02d GMT", dt.weekday.name, dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min, dt.sec); case RFC1123: dt = dt.to_gmt_offset(0); // For named representations of the timezone we always go for GMT, which is required by some RFCs - return string::format("%s, %02d %s %d %02d:%02d:%02d GMT", dt.weekday.abbrev, dt.day, dt.month.abbrev, dt.year, dt.hour, dt.min, dt.sec, allocator: allocator); + return string::format(allocator, "%s, %02d %s %d %02d:%02d:%02d GMT", dt.weekday.abbrev, dt.day, dt.month.abbrev, dt.year, dt.hour, dt.min, dt.sec); case RFC1123Z: - return string::format("%s, %02d %s %d %02d:%02d:%02d %s", dt.weekday.abbrev, dt.day, dt.month.abbrev, dt.year, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix(dt.gmt_offset), allocator: allocator); + return string::format(allocator, "%s, %02d %s %d %02d:%02d:%02d %s", dt.weekday.abbrev, dt.day, dt.month.abbrev, dt.year, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix(dt.gmt_offset)); case RFC3339: dt = dt.to_gmt_offset(0); - return string::format("%04d-%02d-%02dT%02d:%02d:%02dZ", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, allocator: allocator); + return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02dZ", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec); case RFC3339Z: - return string::format("%04d-%02d-%02dT%02d:%02d:%02d%s", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix_colon(dt.gmt_offset), allocator: allocator); + return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d%s", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix_colon(dt.gmt_offset)); case RFC3339MS: dt = dt.to_gmt_offset(0); - return string::format("%04d-%02d-%02dT%02d:%02d:%02d.%dZ", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec, allocator: allocator); + return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d.%dZ", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec); case RFC3339ZMS: - return string::format("%04d-%02d-%02dT%02d:%02d:%02d.%d%s", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec, temp_numeric_tzsuffix_colon(dt.gmt_offset), allocator: allocator); + return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d.%d%s", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec, temp_numeric_tzsuffix_colon(dt.gmt_offset)); case DATETIME: - return string::format("%04d-%02d-%02d %02d:%02d:%02d", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, allocator: allocator); + return string::format(allocator, "%04d-%02d-%02d %02d:%02d:%02d", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec); case DATEONLY: - return string::format("%04d-%02d-%02d", dt.year, dt.month + 1, dt.day, allocator: allocator); + return string::format(allocator, "%04d-%02d-%02d", dt.year, dt.month + 1, dt.day); case TIMEONLY: - return string::format("%02d:%02d:%02d", dt.hour, dt.min, dt.sec, allocator: allocator); + return string::format(allocator, "%02d:%02d:%02d", dt.hour, dt.min, dt.sec); } } -fn String new_format(DateTimeFormat dt_format, TzDateTime dt) => format(dt_format, dt, allocator::heap()); -fn String temp_format(DateTimeFormat dt_format, TzDateTime dt) => format(dt_format, dt, allocator::temp()); +fn String tformat(DateTimeFormat dt_format, TzDateTime dt) => format(tmem(), dt_format, dt); -fn String TzDateTime.format(self, DateTimeFormat dt_format, Allocator allocator) => format(dt_format, self, allocator); -fn String TzDateTime.new_format(self, DateTimeFormat dt_format) => format(dt_format, self, allocator::heap()); -fn String TzDateTime.temp_format(self, DateTimeFormat dt_format) => format(dt_format, self, allocator::temp()); +fn String TzDateTime.format(self, Allocator allocator, DateTimeFormat dt_format) => format(allocator, dt_format, self); // .with_gmt_offset(0) instead of .to_local() is used to avoid surprises when user is formatting to a representation without a timezone -fn String DateTime.format(self, DateTimeFormat dt_format, Allocator allocator) => format(dt_format, self.with_gmt_offset(0), allocator); -fn String DateTime.new_format(self, DateTimeFormat dt_format) => format(dt_format, self.with_gmt_offset(0), allocator::heap()); -fn String DateTime.temp_format(self, DateTimeFormat dt_format) => format(dt_format, self.with_gmt_offset(0), allocator::temp()); +fn String DateTime.format(self, Allocator allocator, DateTimeFormat dt_format) => format(allocator, dt_format, self.with_gmt_offset(0)); <* Returns the timezone offset in the format of "+HHMM" or "-HHMM" diff --git a/lib7/std/ascii.c3 b/lib7/std/ascii.c3 deleted file mode 100644 index 412e09b95..000000000 --- a/lib7/std/ascii.c3 +++ /dev/null @@ -1,77 +0,0 @@ -module std::ascii; - -macro bool in_range_m(c, start, len) => (uint)(c - start) < len; -macro bool is_lower_m(c) => in_range_m(c, 0x61, 26); -macro bool is_upper_m(c) => in_range_m(c, 0x41, 26); -macro bool is_digit_m(c) => in_range_m(c, 0x30, 10); -macro bool is_bdigit_m(c) => in_range_m(c, 0x30, 2); -macro bool is_odigit_m(c) => in_range_m(c, 0x30, 8); -macro bool is_xdigit_m(c) => in_range_m(c | 32, 0x61, 6) || is_digit_m(c); -macro bool is_alpha_m(c) => in_range_m(c | 32, 0x61, 26); -macro bool is_print_m(c) => in_range_m(c, 0x20, 95); -macro bool is_graph_m(c) => in_range_m(c, 0x21, 94); -macro bool is_space_m(c) => in_range_m(c, 0x9, 5) || c == 0x20; -macro bool is_alnum_m(c) => is_alpha_m(c) || is_digit_m(c); -macro bool is_punct_m(c) => !is_alnum_m(c) && is_graph_m(c); -macro bool is_blank_m(c) => c == 0x20 || c == 0x9; -macro bool is_cntrl_m(c) => c < 0x20 || c == 0x7f; -macro to_lower_m(c) => is_upper_m(c) ? c + 0x20 : c; -macro to_upper_m(c) => is_lower_m(c) ? c - 0x20 : c; - -fn bool in_range(char c, char start, char len) => in_range_m(c, start, len); -fn bool is_lower(char c) => is_lower_m(c); -fn bool is_upper(char c) => is_upper_m(c); -fn bool is_digit(char c) => is_digit_m(c); -fn bool is_bdigit(char c) => is_bdigit_m(c); -fn bool is_odigit(char c) => is_odigit_m(c); -fn bool is_xdigit(char c) => is_xdigit_m(c); -fn bool is_alpha(char c) => is_alpha_m(c); -fn bool is_print(char c) => is_print_m(c); -fn bool is_graph(char c) => is_graph_m(c); -fn bool is_space(char c) => is_space_m(c); -fn bool is_alnum(char c) => is_alnum_m(c); -fn bool is_punct(char c) => is_punct_m(c); -fn bool is_blank(char c) => is_blank_m(c); -fn bool is_cntrl(char c) => is_cntrl_m(c); -fn char to_lower(char c) => (char)to_lower_m(c); -fn char to_upper(char c) => (char)to_upper_m(c); - -fn bool char.in_range(char c, char start, char len) => in_range_m(c, start, len); -fn bool char.is_lower(char c) => is_lower_m(c); -fn bool char.is_upper(char c) => is_upper_m(c); -fn bool char.is_digit(char c) => is_digit_m(c); -fn bool char.is_bdigit(char c) => is_bdigit_m(c); -fn bool char.is_odigit(char c) => is_odigit_m(c); -fn bool char.is_xdigit(char c) => is_xdigit_m(c); -fn bool char.is_alpha(char c) => is_alpha_m(c); -fn bool char.is_print(char c) => is_print_m(c); -fn bool char.is_graph(char c) => is_graph_m(c); -fn bool char.is_space(char c) => is_space_m(c); -fn bool char.is_alnum(char c) => is_alnum_m(c); -fn bool char.is_punct(char c) => is_punct_m(c); -fn bool char.is_blank(char c) => is_blank_m(c); -fn bool char.is_cntrl(char c) => is_cntrl_m(c); -fn char char.to_lower(char c) => (char)to_lower_m(c); -fn char char.to_upper(char c) => (char)to_upper_m(c); -<* - @require c.is_xdigit() -*> -fn char char.from_hex(char c) => c.is_digit() ? c - '0' : 10 + (c | 0x20) - 'a'; - -fn bool uint.in_range(uint c, uint start, uint len) => in_range_m(c, start, len); -fn bool uint.is_lower(uint c) => is_lower_m(c); -fn bool uint.is_upper(uint c) => is_upper_m(c); -fn bool uint.is_digit(uint c) => is_digit_m(c); -fn bool uint.is_bdigit(uint c) => is_bdigit_m(c); -fn bool uint.is_odigit(uint c) => is_odigit_m(c); -fn bool uint.is_xdigit(uint c) => is_xdigit_m(c); -fn bool uint.is_alpha(uint c) => is_alpha_m(c); -fn bool uint.is_print(uint c) => is_print_m(c); -fn bool uint.is_graph(uint c) => is_graph_m(c); -fn bool uint.is_space(uint c) => is_space_m(c); -fn bool uint.is_alnum(uint c) => is_alnum_m(c); -fn bool uint.is_punct(uint c) => is_punct_m(c); -fn bool uint.is_blank(uint c) => is_blank_m(c); -fn bool uint.is_cntrl(uint c) => is_cntrl_m(c); -fn uint uint.to_lower(uint c) => (uint)to_lower_m(c); -fn uint uint.to_upper(uint c) => (uint)to_upper_m(c); diff --git a/lib7/std/atomic.c3 b/lib7/std/atomic.c3 deleted file mode 100644 index c26cf793e..000000000 --- a/lib7/std/atomic.c3 +++ /dev/null @@ -1,520 +0,0 @@ -// Copyright (c) 2023 Eduardo José Gómez Hernández. 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::atomic::types{Type}; - -struct Atomic -{ - Type data; -} - -<* - Loads data atomically, by default this uses SEQ_CONSISTENT ordering. - - @param ordering "The ordering, cannot be release or acquire-release." - @require ordering != RELEASE && ordering != ACQUIRE_RELEASE : "Release and acquire-release are not valid for load" -*> -macro Type Atomic.load(&self, AtomicOrdering ordering = SEQ_CONSISTENT) -{ - Type* data = &self.data; - switch(ordering) - { - case NOT_ATOMIC: return $$atomic_load(data, false, AtomicOrdering.NOT_ATOMIC.ordinal); - case UNORDERED: return $$atomic_load(data, false, AtomicOrdering.UNORDERED.ordinal); - case RELAXED: return $$atomic_load(data, false, AtomicOrdering.RELAXED.ordinal); - case ACQUIRE: return $$atomic_load(data, false, AtomicOrdering.ACQUIRE.ordinal); - case SEQ_CONSISTENT: return $$atomic_load(data, false, AtomicOrdering.SEQ_CONSISTENT.ordinal); - case ACQUIRE_RELEASE: - case RELEASE: unreachable("Invalid ordering."); - } -} -<* - Stores data atomically, by default this uses SEQ_CONSISTENT ordering. - - @param ordering "The ordering, cannot be acquire or acquire-release." - @require ordering != ACQUIRE && ordering != ACQUIRE_RELEASE : "Acquire and acquire-release are not valid for store" -*> -macro void Atomic.store(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) -{ - Type* data = &self.data; - switch(ordering) - { - case NOT_ATOMIC: $$atomic_store(data, value, false, AtomicOrdering.NOT_ATOMIC.ordinal); - case UNORDERED: $$atomic_store(data, value, false, AtomicOrdering.UNORDERED.ordinal); - case RELAXED: $$atomic_store(data, value, false, AtomicOrdering.RELAXED.ordinal); - case RELEASE: $$atomic_store(data, value, false, AtomicOrdering.RELEASE.ordinal); - case SEQ_CONSISTENT: $$atomic_store(data, value, false, AtomicOrdering.SEQ_CONSISTENT.ordinal); - case ACQUIRE_RELEASE: - case ACQUIRE: unreachable("Invalid ordering."); - } -} - -macro Type Atomic.add(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) -{ - Type* data = &self.data; - return @atomic_exec(atomic::fetch_add, data, value, ordering); -} - -macro Type Atomic.sub(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) -{ - Type* data = &self.data; - return @atomic_exec(atomic::fetch_sub, data, value, ordering); -} - -macro Type Atomic.mul(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) -{ - Type* data = &self.data; - return @atomic_exec(atomic::fetch_mul, data, value, ordering); -} - -macro Type Atomic.div(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) -{ - Type* data = &self.data; - return @atomic_exec(atomic::fetch_div, data, value, ordering); -} - -macro Type Atomic.max(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) -{ - Type* data = &self.data; - return @atomic_exec(atomic::fetch_div, data, value, ordering); -} - -macro Type Atomic.min(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) -{ - Type* data = &self.data; - return @atomic_exec(atomic::fetch_min, data, value, ordering); -} - -macro Type Atomic.or(&self, uint value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type)) -{ - Type* data = &self.data; - return @atomic_exec(atomic::fetch_or, data, value, ordering); -} - -fn Type Atomic.xor(&self, uint value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type)) -{ - Type* data = &self.data; - return @atomic_exec(atomic::fetch_xor, data, value, ordering); -} - -macro Type Atomic.and(&self, uint value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type)) -{ - Type* data = &self.data; - return @atomic_exec(atomic::fetch_and, data, value, ordering); -} - -macro Type Atomic.shift_right(&self, uint amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type)) -{ - Type* data = &self.data; - return @atomic_exec(atomic::fetch_shift_right, data, amount, ordering); -} - -macro Type Atomic.shift_left(&self, uint amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type)) -{ - Type* data = &self.data; - return @atomic_exec(atomic::fetch_shift_left, data, amount, ordering); -} - -macro @atomic_exec(#func, data, value, ordering) @local -{ - switch(ordering) - { - case RELAXED: return #func(data, value, RELAXED); - case ACQUIRE: return #func(data, value, ACQUIRE); - case RELEASE: return #func(data, value, RELEASE); - case ACQUIRE_RELEASE: return #func(data, value, ACQUIRE_RELEASE); - case SEQ_CONSISTENT: return #func(data, value, SEQ_CONSISTENT); - default: unreachable("Ordering may not be non-atomic or unordered."); - } -} - -module std::atomic; -import std::math; - -<* - @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @return "returns the old value of ptr" - - @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two." - @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used." - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." -*> -macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) -{ - $if $alignment == 0: - $alignment = $typeof(*ptr).sizeof; - $endif - return $$atomic_fetch_add(ptr, y, $volatile, $ordering.ordinal, $alignment); -} - -<* - @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @return "returns the old value of ptr" - - @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two." - @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used." - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." -*> -macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) -{ - $if $alignment == 0: - $alignment = $typeof(*ptr).sizeof; - $endif - return $$atomic_fetch_sub(ptr, y, $volatile, $ordering.ordinal, $alignment); -} - -<* - @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @return "returns the old value of ptr" - - @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used." - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." -*> -macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) -{ - var $load_ordering = $ordering; - $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: - $load_ordering = AtomicOrdering.SEQ_CONSISTENT; - $endif - - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); - - $StorageType* storage_ptr = ($StorageType*)ptr; - - $typeof(*ptr) old_value; - $typeof(*ptr) new_value; - - $StorageType storage_old_value; - $StorageType storage_new_value; - - - do { - storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); - old_value = bitcast(storage_old_value, $typeof(*ptr)); - new_value = old_value * y; - storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); - - return old_value; -} - -<* - @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @return "returns the old value of ptr" - - @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used." - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." -*> -macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) -{ - var $load_ordering = $ordering; - $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: - $load_ordering = AtomicOrdering.SEQ_CONSISTENT; - $endif - - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); - - $StorageType* storage_ptr = ($StorageType*)ptr; - - $typeof(*ptr) old_value; - $typeof(*ptr) new_value; - - $StorageType storage_old_value; - $StorageType storage_new_value; - - do { - storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); - old_value = bitcast(storage_old_value, $typeof(*ptr)); - new_value = old_value / y; - storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); - - return old_value; -} - -<* - @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @return "returns the old value of ptr" - - @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two." - @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." - @require types::is_int($typeof(y)) "The value for or must be an int" - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." -*> -macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) -{ - $if types::is_int($typeof(*ptr)): - return $$atomic_fetch_or(ptr, y, $volatile, $ordering.ordinal, $alignment); - $endif - - var $load_ordering = $ordering; - $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: - $load_ordering = AtomicOrdering.SEQ_CONSISTENT; - $endif - - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); - - $StorageType* storage_ptr = ($StorageType*)ptr; - - $typeof(*ptr) old_value; - $typeof(*ptr) new_value; - - $StorageType storage_old_value; - $StorageType storage_new_value; - $StorageType storage_y = ($StorageType)y; - - do { - storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); - old_value = bitcast(storage_old_value, $typeof(*ptr)); - new_value = storage_old_value | storage_y; - storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); - - return old_value; -} - -<* - @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @return "returns the old value of ptr" - - @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two." - @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." - @require types::is_int($typeof(y)) "The value for or must be an int" - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." -*> -macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) -{ - $if types::is_int($typeof(*ptr)): - return $$atomic_fetch_xor(ptr, y, $volatile, $ordering.ordinal, $alignment); - $endif - - var $load_ordering = $ordering; - $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: - $load_ordering = AtomicOrdering.SEQ_CONSISTENT; - $endif - - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); - - $StorageType* storage_ptr = ($StorageType*)ptr; - - $typeof(*ptr) old_value; - $typeof(*ptr) new_value; - - $StorageType storage_old_value; - $StorageType storage_new_value; - $StorageType storage_y = ($StorageType)y; - - do { - storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); - old_value = bitcast(storage_old_value, $typeof(*ptr)); - new_value = storage_old_value ^ storage_y; - storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); - - return old_value; -} - -<* - @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @return "returns the old value of ptr" - - @require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two." - @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." - @require types::is_int($typeof(y)) "The value for or must be an int" - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." -*> -macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) -{ - $if types::is_int($typeof(*ptr)): - return $$atomic_fetch_and(ptr, y, $volatile, $ordering.ordinal, $alignment); - $endif - - var $load_ordering = $ordering; - $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: - $load_ordering = AtomicOrdering.SEQ_CONSISTENT; - $endif - - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); - - $StorageType* storage_ptr = ($StorageType*)ptr; - - $typeof(*ptr) old_value; - $typeof(*ptr) new_value; - - $StorageType storage_old_value; - $StorageType storage_new_value; - $StorageType storage_y = ($StorageType)y; - - do { - storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); - old_value = bitcast(storage_old_value, $typeof(*ptr)); - new_value = storage_old_value & storage_y; - storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); - - return old_value; -} - -<* - @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @return "returns the old value of ptr" - - @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." - @require types::is_int($typeof(y)) "The value for or must be an int" - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." -*> -macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) -{ - var $load_ordering = $ordering; - $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: - $load_ordering = AtomicOrdering.SEQ_CONSISTENT; - $endif - - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); - - $StorageType* storage_ptr = ($StorageType*)ptr; - - $typeof(*ptr) old_value; - $typeof(*ptr) new_value; - - $StorageType storage_old_value; - $StorageType storage_new_value; - $StorageType storage_y = ($StorageType)y; - - do { - storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); - old_value = bitcast(storage_old_value, $typeof(*ptr)); - new_value = storage_old_value >> storage_y; - storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); - - return old_value; -} - -<* - @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @return "returns the old value of ptr" - - @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." - @require types::is_int($typeof(y)) "The value for or must be an int" - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." -*> -macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) -{ - var $load_ordering = $ordering; - $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: - $load_ordering = AtomicOrdering.SEQ_CONSISTENT; - $endif - - var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr))); - - $StorageType* storage_ptr = ($StorageType*)ptr; - - $typeof(*ptr) old_value; - $typeof(*ptr) new_value; - - $StorageType storage_old_value; - $StorageType storage_new_value; - $StorageType storage_y = ($StorageType)y; - - do { - storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal); - old_value = bitcast(storage_old_value, $typeof(*ptr)); - new_value = storage_old_value << storage_y; - storage_new_value = bitcast(new_value, $StorageType); - } while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value); - - return old_value; -} - -<* - @param [&in] ptr "the variable or dereferenced pointer to the data." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @return "returns the old value of ptr" - - @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." -*> -macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT) -{ - $typeof(*ptr) old_value; - $typeof(*ptr) new_value = true; - - do { - old_value = $$atomic_load(ptr, false, $ordering.ordinal); - } while (mem::compare_exchange(ptr, old_value, new_value, $ordering, $load_ordering) != old_value); - - return old_value; -} - -<* - @param [&in] ptr "the variable or dereferenced pointer to the data." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @return "returns the old value of ptr" - - @require types::is_int($typeof(*ptr)) "Only integer pointers may be used." - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." -*> -macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT) -{ - $typeof(*ptr) old_value; - $typeof(*ptr) new_value = false; - - do { - old_value = $$atomic_load(ptr, false, $ordering.ordinal); - } while (mem::compare_exchange(ptr, old_value, new_value, $ordering, $load_ordering) != old_value); - - return old_value; -} - -<* - @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @return "returns the old value of ptr" - - @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used." - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." -*> -macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) -{ - $if $alignment == 0: - $alignment = $typeof(*ptr).sizeof; - $endif - return $$atomic_fetch_max(ptr, y, $volatile, $ordering.ordinal, $alignment); -} - -<* - @param [&in] ptr "the variable or dereferenced pointer to the data." - @param [in] y "the value to be added to ptr." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @return "returns the old value of ptr" - - @require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used." - @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid." -*> -macro fetch_min(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) -{ - $if $alignment == 0: - $alignment = $typeof(*ptr).sizeof; - $endif - return $$atomic_fetch_min(ptr, y, $volatile, $ordering.ordinal, $alignment); -} diff --git a/lib7/std/atomic_nolibc.c3 b/lib7/std/atomic_nolibc.c3 deleted file mode 100644 index b9ec23e3a..000000000 --- a/lib7/std/atomic_nolibc.c3 +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2023 Eduardo José Gómez Hernández. 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::atomic; - -macro @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, $success, failure, $alignment) { - switch(failure) - { - case AtomicOrdering.RELAXED.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.RELAXED.ordinal, $alignment); - case AtomicOrdering.ACQUIRE.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.ACQUIRE.ordinal, $alignment); - case AtomicOrdering.SEQ_CONSISTENT.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.SEQ_CONSISTENT.ordinal, $alignment); - default: unreachable("Unrecognized failure ordering"); - } - return 0; -} - -macro @__atomic_compare_exchange_ordering_success(ptr, expected, desired, success, failure, $alignment) -{ - switch(success) - { - case AtomicOrdering.RELAXED.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.RELAXED.ordinal, failure, $alignment); - case AtomicOrdering.ACQUIRE.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.ACQUIRE.ordinal, failure, $alignment); - case AtomicOrdering.RELEASE.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.RELEASE.ordinal, failure, $alignment); - case AtomicOrdering.ACQUIRE_RELEASE.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.ACQUIRE_RELEASE.ordinal, failure, $alignment); - case AtomicOrdering.SEQ_CONSISTENT.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.SEQ_CONSISTENT.ordinal, failure, $alignment); - default: unreachable("Unrecognized success ordering"); - } - return 0; -} - -fn CInt __atomic_compare_exchange(CInt size, any ptr, any expected, any desired, CInt success, CInt failure) @extern("__atomic_compare_exchange") @export -{ - switch (size) - { - case 1: - char* pt = (char*)ptr; - char ex = *(char*)expected; - char de = *(char*)desired; - if (ex == @__atomic_compare_exchange_ordering_success(pt, ex, de, success, failure, 1)) return 1; - case 2: - short* pt = (short*)ptr; - short ex = *(short*)expected; - short de = *(short*)desired; - if (ex == @__atomic_compare_exchange_ordering_success(pt, ex, de, success, failure, 2)) return 1; - case 4: - int* pt = (int*)ptr; - int ex = *(int*)expected; - int de = *(int*)desired; - if (ex == @__atomic_compare_exchange_ordering_success(pt, ex, de, success, failure, 4)) return 1; - case 8: - $if iptr.sizeof >= 8: - long* pt = (long*)ptr; - long ex = *(long*)expected; - long de = *(long*)desired; - if (ex == @__atomic_compare_exchange_ordering_success(pt, ex, de, success, failure, 8)) return 1; - $else - nextcase; - $endif - default: - unreachable("Unsuported size (%d) for atomic_compare_exchange", size); - } - return 0; -} \ No newline at end of file diff --git a/lib7/std/bits.c3 b/lib7/std/bits.c3 deleted file mode 100644 index 212492a57..000000000 --- a/lib7/std/bits.c3 +++ /dev/null @@ -1,171 +0,0 @@ -module std::bits; - -<* - @require types::is_intlike($typeof(i)) `The input must be an integer or integer vector` -*> -macro reverse(i) => $$bitreverse(i); - -<* - @require types::is_intlike($typeof(i)) `The input must be an integer or integer vector` -*> -macro bswap(i) @builtin => $$bswap(i); - -macro uint[].popcount(self) => $$popcount(self); -macro uint[].ctz(self) => $$ctz(self); -macro uint[].clz(self) => $$clz(self); -macro uint[] uint[].fshl(hi, uint[] lo, uint[] shift) => $$fshl(hi, lo, shift); -macro uint[] uint[].fshr(hi, uint[] lo, uint[] shift) => $$fshr(hi, lo, shift); -macro uint[] uint[].rotl(self, uint[] shift) => $$fshl(self, self, shift); -macro uint[] uint[].rotr(self, uint[] shift) => $$fshr(self, self, shift); - -macro int[].popcount(self) => $$popcount(self); -macro int[].ctz(self) => $$ctz(self); -macro int[].clz(self) => $$clz(self); -macro int[] int[].fshl(hi, int[] lo, int[] shift) => $$fshl(hi, lo, shift); -macro int[] int[].fshr(hi, int[] lo, int[] shift) => $$fshr(hi, lo, shift); -macro int[] int[].rotl(self, int[] shift) => $$fshl(self, self, shift); -macro int[] int[].rotr(self, int[] shift) => $$fshr(self, self, shift); - -macro ushort[].popcount(self) => $$popcount(self); -macro ushort[].ctz(self) => $$ctz(self); -macro ushort[].clz(self) => $$clz(self); -macro ushort[] ushort[].fshl(hi, ushort[] lo, ushort[] shift) => $$fshl(hi, lo, shift); -macro ushort[] ushort[].fshr(hi, ushort[] lo, ushort[] shift) => $$fshr(hi, lo, shift); -macro ushort[] ushort[].rotl(self, ushort[] shift) => $$fshl(self, self, shift); -macro ushort[] ushort[].rotr(self, ushort[] shift) => $$fshr(self, self, shift); - -macro short[].popcount(self) => $$popcount(self); -macro short[].ctz(self) => $$ctz(self); -macro short[].clz(self) => $$clz(self); -macro short[] short[].fshl(hi, short[] lo, short[] shift) => $$fshl(hi, lo, shift); -macro short[] short[].fshr(hi, short[] lo, short[] shift) => $$fshr(hi, lo, shift); -macro short[] short[].rotl(self, short[] shift) => $$fshl(self, self, shift); -macro short[] short[].rotr(self, short[] shift) => $$fshr(self, self, shift); - -macro char[].popcount(self) => $$popcount(self); -macro char[].ctz(self) => $$ctz(self); -macro char[].clz(self) => $$clz(self); -macro char[] char[].fshl(hi, char[] lo, char[] shift) => $$fshl(hi, lo, shift); -macro char[] char[].fshr(hi, char[] lo, char[] shift) => $$fshr(hi, lo, shift); -macro char[] char[].rotl(self, char[] shift) => $$fshl(self, self, shift); -macro char[] char[].rotr(self, char[] shift) => $$fshr(self, self, shift); - -macro ichar[].popcount(self) => $$popcount(self); -macro ichar[].ctz(self) => $$ctz(self); -macro ichar[].clz(self) => $$clz(self); -macro ichar[] ichar[].fshl(hi, ichar[] lo, ichar[] shift) => $$fshl(hi, lo, shift); -macro ichar[] ichar[].fshr(hi, ichar[] lo, ichar[] shift) => $$fshr(hi, lo, shift); -macro ichar[] ichar[].rotl(self, ichar[] shift) => $$fshl(self, self, shift); -macro ichar[] ichar[].rotr(self, ichar[] shift) => $$fshr(self, self, shift); - -macro ulong[].popcount(self) => $$popcount(self); -macro ulong[].ctz(self) => $$ctz(self); -macro ulong[].clz(self) => $$clz(self); -macro ulong[] ulong[].fshl(hi, ulong[] lo, ulong[] shift) => $$fshl(hi, lo, shift); -macro ulong[] ulong[].fshr(hi, ulong[] lo, ulong[] shift) => $$fshr(hi, lo, shift); -macro ulong[] ulong[].rotl(self, ulong[] shift) => $$fshl(self, self, shift); -macro ulong[] ulong[].rotr(self, ulong[] shift) => $$fshr(self, self, shift); - -macro long[].popcount(self) => $$popcount(self); -macro long[].ctz(self) => $$ctz(self); -macro long[].clz(self) => $$clz(self); -macro long[] long[].fshl(hi, long[] lo, long[] shift) => $$fshl(hi, lo, shift); -macro long[] long[].fshr(hi, long[] lo, long[] shift) => $$fshr(hi, lo, shift); -macro long[] long[].rotl(self, long[] shift) => $$fshl(self, self, shift); -macro long[] long[].rotr(self, long[] shift) => $$fshr(self, self, shift); - -macro uint128[].popcount(self) => $$popcount(self); -macro uint128[].ctz(self) => $$ctz(self); -macro uint128[].clz(self) => $$clz(self); -macro uint128[] uint128[].fshl(hi, uint128[] lo, uint128[] shift) => $$fshl(hi, lo, shift); -macro uint128[] uint128[].fshr(hi, uint128[] lo, uint128[] shift) => $$fshr(hi, lo, shift); -macro uint128[] uint128[].rotl(self, uint128[] shift) => $$fshl(self, self, shift); -macro uint128[] uint128[].rotr(self, uint128[] shift) => $$fshr(self, self, shift); - -macro int128[].popcount(self) => $$popcount(self); -macro int128[].ctz(self) => $$ctz(self); -macro int128[].clz(self) => $$clz(self); -macro int128[] int128[].fshl(hi, int128[] lo, int128[] shift) => $$fshl(hi, lo, shift); -macro int128[] int128[].fshr(hi, int128[] lo, int128[] shift) => $$fshr(hi, lo, shift); -macro int128[] int128[].rotl(self, int128[] shift) => $$fshl(self, self, shift); -macro int128[] int128[].rotr(self, int128[] shift) => $$fshr(self, self, shift); - -macro uint.popcount(self) => $$popcount(self); -macro uint.ctz(self) => $$ctz(self); -macro uint.clz(self) => $$clz(self); -macro uint uint.fshl(hi, uint lo, uint shift) => $$fshl(hi, lo, shift); -macro uint uint.fshr(hi, uint lo, uint shift) => $$fshr(hi, lo, shift); -macro uint uint.rotl(self, uint shift) => $$fshl(self, self, shift); -macro uint uint.rotr(self, uint shift) => $$fshr(self, self, shift); - -macro int.popcount(self) => $$popcount(self); -macro int.ctz(self) => $$ctz(self); -macro int.clz(self) => $$clz(self); -macro int int.fshl(hi, int lo, int shift) => $$fshl(hi, lo, shift); -macro int int.fshr(hi, int lo, int shift) => $$fshr(hi, lo, shift); -macro int int.rotl(self, int shift) => $$fshl(self, self, shift); -macro int int.rotr(self, int shift) => $$fshr(self, self, shift); - -macro ushort.popcount(self) => $$popcount(self); -macro ushort.ctz(self) => $$ctz(self); -macro ushort.clz(self) => $$clz(self); -macro ushort ushort.fshl(hi, ushort lo, ushort shift) => $$fshl(hi, lo, shift); -macro ushort ushort.fshr(hi, ushort lo, ushort shift) => $$fshr(hi, lo, shift); -macro ushort ushort.rotl(self, ushort shift) => $$fshl(self, self, shift); -macro ushort ushort.rotr(self, ushort shift) => $$fshr(self, self, shift); - -macro short.popcount(self) => $$popcount(self); -macro short.ctz(self) => $$ctz(self); -macro short.clz(self) => $$clz(self); -macro short short.fshl(hi, short lo, short shift) => $$fshl(hi, lo, shift); -macro short short.fshr(hi, short lo, short shift) => $$fshr(hi, lo, shift); -macro short short.rotl(self, short shift) => $$fshl(self, self, shift); -macro short short.rotr(self, short shift) => $$fshr(self, self, shift); - -macro char.popcount(self) => $$popcount(self); -macro char.ctz(self) => $$ctz(self); -macro char.clz(self) => $$clz(self); -macro char char.fshl(hi, char lo, char shift) => $$fshl(hi, lo, shift); -macro char char.fshr(hi, char lo, char shift) => $$fshr(hi, lo, shift); -macro char char.rotl(self, char shift) => $$fshl(self, self, shift); -macro char char.rotr(self, char shift) => $$fshr(self, self, shift); - -macro ichar.popcount(self) => $$popcount(self); -macro ichar.ctz(self) => $$ctz(self); -macro ichar.clz(self) => $$clz(self); -macro ichar ichar.fshl(hi, ichar lo, ichar shift) => $$fshl(hi, lo, shift); -macro ichar ichar.fshr(hi, ichar lo, ichar shift) => $$fshr(hi, lo, shift); -macro ichar ichar.rotl(self, ichar shift) => $$fshl(self, self, shift); -macro ichar ichar.rotr(self, ichar shift) => $$fshr(self, self, shift); - -macro ulong.popcount(self) => $$popcount(self); -macro ulong.ctz(self) => $$ctz(self); -macro ulong.clz(self) => $$clz(self); -macro ulong ulong.fshl(hi, ulong lo, ulong shift) => $$fshl(hi, lo, shift); -macro ulong ulong.fshr(hi, ulong lo, ulong shift) => $$fshr(hi, lo, shift); -macro ulong ulong.rotl(self, ulong shift) => $$fshl(self, self, shift); -macro ulong ulong.rotr(self, ulong shift) => $$fshr(self, self, shift); - -macro long.popcount(self) => $$popcount(self); -macro long.ctz(self) => $$ctz(self); -macro long.clz(self) => $$clz(self); -macro long long.fshl(hi, long lo, long shift) => $$fshl(hi, lo, shift); -macro long long.fshr(hi, long lo, long shift) => $$fshr(hi, lo, shift); -macro long long.rotl(self, long shift) => $$fshl(self, self, shift); -macro long long.rotr(self, long shift) => $$fshr(self, self, shift); - -macro uint128.popcount(self) => $$popcount(self); -macro uint128.ctz(self) => $$ctz(self); -macro uint128.clz(self) => $$clz(self); -macro uint128 uint128.fshl(hi, uint128 lo, uint128 shift) => $$fshl(hi, lo, shift); -macro uint128 uint128.fshr(hi, uint128 lo, uint128 shift) => $$fshr(hi, lo, shift); -macro uint128 uint128.rotl(self, uint128 shift) => $$fshl(self, self, shift); -macro uint128 uint128.rotr(self, uint128 shift) => $$fshr(self, self, shift); - -macro int128.popcount(self) => $$popcount(self); -macro int128.ctz(self) => $$ctz(self); -macro int128.clz(self) => $$clz(self); -macro int128 int128.fshl(hi, int128 lo, int128 shift) => $$fshl(hi, lo, shift); -macro int128 int128.fshr(hi, int128 lo, int128 shift) => $$fshr(hi, lo, shift); -macro int128 int128.rotl(self, int128 shift) => $$fshl(self, self, shift); -macro int128 int128.rotr(self, int128 shift) => $$fshr(self, self, shift); diff --git a/lib7/std/collections/anylist.c3 b/lib7/std/collections/anylist.c3 deleted file mode 100644 index fc369ce81..000000000 --- a/lib7/std/collections/anylist.c3 +++ /dev/null @@ -1,450 +0,0 @@ -// 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; -import std::io,std::math; - -def AnyPredicate = fn bool(any value); -def AnyTest = fn bool(any type, any context); - -struct AnyList (Printable) -{ - usz size; - usz capacity; - Allocator allocator; - any* entries; -} - - -<* - @param [&inout] allocator "The allocator to use" - @param initial_capacity "The initial capacity to reserve" -*> -fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16) -{ - self.allocator = allocator; - self.size = 0; - if (initial_capacity > 0) - { - initial_capacity = math::next_power_of_2(initial_capacity); - self.entries = allocator::alloc_array(allocator, any, initial_capacity); - } - else - { - self.entries = null; - } - self.capacity = initial_capacity; - return self; -} - -<* - Initialize the list using the temp allocator. - - @param initial_capacity "The initial capacity to reserve" -*> -fn AnyList* AnyList.tinit(&self, usz initial_capacity = 16) -{ - return self.init(tmem(), initial_capacity) @inline; -} - -fn usz! AnyList.to_format(&self, Formatter* formatter) @dynamic -{ - switch (self.size) - { - case 0: - return formatter.print("[]")!; - case 1: - return formatter.printf("[%s]", self.entries[0])!; - default: - usz n = formatter.print("[")!; - foreach (i, element : self.entries[:self.size]) - { - if (i != 0) formatter.print(", ")!; - n += formatter.printf("%s", element)!; - } - n += formatter.print("]")!; - return n; - } -} - -<* - Push an element on the list by cloning it. -*> -macro void AnyList.push(&self, element) -{ - if (!self.allocator) self.allocator = allocator::heap(); - self.append_internal(allocator::clone(self.allocator, element)); -} - -fn void AnyList.append_internal(&self, any element) @local -{ - self.ensure_capacity(); - self.entries[self.size++] = element; -} - -<* - Free a retained element removed using *_retained. -*> -fn void AnyList.free_element(&self, any element) @inline -{ - allocator::free(self.allocator, element.ptr); -} - -<* - Pop a value who's type is known. If the type is incorrect, this - will still pop the element. - - @return! CastResult.TYPE_MISMATCH, IteratorResult.NO_MORE_ELEMENT -*> -macro AnyList.pop(&self, $Type) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - defer self.free_element(self.entries[self.size]); - return *anycast(self.entries[--self.size], $Type); -} - -<* - Pop the last value and allocate the copy using the given allocator. - @return! IteratorResult.NO_MORE_ELEMENT -*> -fn any! AnyList.copy_pop(&self, Allocator allocator) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - defer self.free_element(self.entries[self.size]); - return allocator::clone_any(allocator, self.entries[--self.size]); -} - - -<* - 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(tmem()); - -<* - Pop the last value. It must later be released using list.free_element() - @return! IteratorResult.NO_MORE_ELEMENT -*> -fn any! AnyList.pop_retained(&self) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - return self.entries[--self.size]; -} - -fn void AnyList.clear(&self) -{ - for (usz i = 0; i < self.size; i++) - { - self.free_element(self.entries[i]); - } - self.size = 0; -} - -<* - Same as pop() but pops the first value instead. -*> -macro AnyList.pop_first(&self, $Type) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - defer self.remove_at(0); - return *anycast(self.entries[0], $Type); -} - -<* - Same as pop_retained() but pops the first value instead. -*> -fn any! AnyList.pop_first_retained(&self) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - defer self.remove_at(0); - return self.entries[0]; -} - - -<* - Same as copy_pop() but pops the first value instead. -*> -fn any! AnyList.copy_pop_first(&self, Allocator allocator = allocator::heap()) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - defer self.free_element(self.entries[self.size]); - defer self.remove_at(0); - return allocator::clone_any(allocator, self.entries[0]); -} - -<* - Same as temp_pop() but pops the first value instead. -*> -fn any! AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem()); - -<* - @require index < self.size -*> -fn void AnyList.remove_at(&self, usz index) -{ - if (!--self.size || index == self.size) return; - self.free_element(self.entries[index]); - self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size]; -} - -fn void AnyList.add_all(&self, AnyList* other_list) -{ - if (!other_list.size) return; - self.reserve(other_list.size); - foreach (value : other_list) - { - self.entries[self.size++] = allocator::clone_any(self.allocator, value); - } -} - -<* - Reverse the elements in a list. -*> -fn void AnyList.reverse(&self) -{ - if (self.size < 2) return; - usz half = self.size / 2U; - usz end = self.size - 1; - for (usz i = 0; i < half; i++) - { - self.swap(i, end - i); - } -} - -fn any[] AnyList.array_view(&self) -{ - return self.entries[:self.size]; -} - -<* - Push an element to the front of the list. -*> -macro void AnyList.push_front(&self, type) -{ - self.insert_at(0, type); -} - -<* - @require index < self.size -*> -macro void AnyList.insert_at(&self, usz index, type) @local -{ - any value = allocator::copy(self.allocator, type); - self.insert_at_internal(self, index, value); -} - -<* - @require index < self.size -*> -fn void AnyList.insert_at_internal(&self, usz index, any value) @local -{ - self.ensure_capacity(); - for (usz i = self.size; i > index; i--) - { - self.entries[i] = self.entries[i - 1]; - } - self.size++; - self.entries[index] = value; -} - - -<* - @require self.size > 0 -*> -fn void AnyList.remove_last(&self) -{ - self.free_element(self.entries[--self.size]); -} - -<* - @require self.size > 0 -*> -fn void AnyList.remove_first(&self) -{ - self.remove_at(0); -} - -macro AnyList.first(&self, $Type) -{ - return *anycast(self.first_any(), $Type); -} - -fn any! AnyList.first_any(&self) @inline -{ - return self.size ? self.entries[0] : IteratorResult.NO_MORE_ELEMENT?; -} - -macro AnyList.last(&self, $Type) -{ - return *anycast(self.last_any(), $Type); -} - -fn any! AnyList.last_any(&self) @inline -{ - return self.size ? self.entries[self.size - 1] : IteratorResult.NO_MORE_ELEMENT?; -} - -fn bool AnyList.is_empty(&self) @inline -{ - return !self.size; -} - -fn usz AnyList.len(&self) @operator(len) @inline -{ - return self.size; -} - -<* - @require index < self.size "Index out of range" -*> -macro AnyList.get(&self, usz index, $Type) -{ - return *anycast(self.entries[index], $Type); -} - -<* - @require index < self.size "Index out of range" -*> -fn any AnyList.get_any(&self, usz index) @inline -{ - return self.entries[index]; -} - -fn void AnyList.free(&self) -{ - if (!self.allocator) return; - self.clear(); - allocator::free(self.allocator, self.entries); - self.capacity = 0; - self.entries = null; -} - -fn void AnyList.swap(&self, usz i, usz j) -{ - any temp = self.entries[i]; - self.entries[i] = self.entries[j]; - self.entries[j] = temp; -} - -<* - @param filter "The function to determine if it should be removed or not" - @return "the number of deleted elements" -*> -fn usz AnyList.remove_if(&self, AnyPredicate filter) -{ - return self._remove_if(filter, false); -} - -<* - @param selection "The function to determine if it should be kept or not" - @return "the number of deleted elements" -*> -fn usz AnyList.retain_if(&self, AnyPredicate selection) -{ - return self._remove_if(selection, true); -} - -macro usz AnyList._remove_if(&self, AnyPredicate filter, bool $invert) @local -{ - usz size = self.size; - for (usz i = size, usz k = size; k > 0; k = i) - { - // Find last index of item to be deleted. - $if $invert: - while (i > 0 && !filter(&self.entries[i - 1])) i--; - $else - while (i > 0 && filter(&self.entries[i - 1])) i--; - $endif - // Remove the items from this index up to the one not to be deleted. - usz n = self.size - k; - for (usz j = i; j < k; j++) self.free_element(self.entries[j]); - self.entries[i:n] = self.entries[k:n]; - self.size -= k - i; - // Find last index of item not to be deleted. - $if $invert: - while (i > 0 && filter(&self.entries[i - 1])) i--; - $else - while (i > 0 && !filter(&self.entries[i - 1])) i--; - $endif - } - return size - self.size; -} - -fn usz AnyList.remove_using_test(&self, AnyTest filter, any context) -{ - return self._remove_using_test(filter, false, context); -} - -fn usz AnyList.retain_using_test(&self, AnyTest filter, any context) -{ - return self._remove_using_test(filter, true, context); -} - -macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @local -{ - usz size = self.size; - for (usz i = size, usz k = size; k > 0; k = i) - { - // Find last index of item to be deleted. - $if $invert: - while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--; - $else - while (i > 0 && filter(&self.entries[i - 1], ctx)) i--; - $endif - // Remove the items from this index up to the one not to be deleted. - usz n = self.size - k; - for (usz j = i; j < k; j++) self.free_element(self.entries[j]); - self.entries[i:n] = self.entries[k:n]; - self.size -= k - i; - // Find last index of item not to be deleted. - $if $invert: - while (i > 0 && filter(&self.entries[i - 1], ctx)) i--; - $else - while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--; - $endif - } - return size - self.size; -} - -<* - Reserve at least min_capacity -*> -fn void AnyList.reserve(&self, usz min_capacity) -{ - if (!min_capacity) return; - if (self.capacity >= min_capacity) return; - 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; -} - -macro any AnyList.@item_at(&self, usz index) @operator([]) -{ - return self.entries[index]; -} - -<* - @require index <= self.size "Index out of range" -*> -macro void AnyList.set(&self, usz index, value) -{ - if (index == self.size) - { - self.push(value); - return; - } - self.free_element(self.entries[index]); - self.entries[index] = allocator::copy(self.allocator, value); -} - -fn void AnyList.ensure_capacity(&self, usz added = 1) @inline @private -{ - usz new_size = self.size + added; - if (self.capacity >= new_size) return; - - assert(new_size < usz.max / 2U); - usz new_capacity = self.capacity ? 2U * self.capacity : 16U; - while (new_capacity < new_size) new_capacity *= 2U; - self.reserve(new_capacity); -} diff --git a/lib7/std/collections/bitset.c3 b/lib7/std/collections/bitset.c3 deleted file mode 100644 index 128f41622..000000000 --- a/lib7/std/collections/bitset.c3 +++ /dev/null @@ -1,155 +0,0 @@ -<* - @require SIZE > 0 -*> -module std::collections::bitset{SIZE}; - -def Type = uint; - -const BITS = Type.sizeof * 8; -const SZ = (SIZE + BITS - 1) / BITS; - -struct BitSet -{ - Type[SZ] data; -} - -fn usz BitSet.cardinality(&self) -{ - usz n; - foreach (x : self.data) - { - n += x.popcount(); - } - return n; -} - -<* - @require i < SIZE -*> -fn void BitSet.set(&self, usz i) -{ - usz q = i / BITS; - usz r = i % BITS; - self.data[q] |= 1 << r; -} - -<* - @require i < SIZE -*> -fn void BitSet.unset(&self, usz i) -{ - usz q = i / BITS; - usz r = i % BITS; - self.data[q] &= ~(1 << r); -} - -<* - @require i < SIZE -*> -fn bool BitSet.get(&self, usz i) @operator([]) @inline -{ - usz q = i / BITS; - usz r = i % BITS; - return self.data[q] & (1 << r) != 0; -} - -fn usz BitSet.len(&self) @operator(len) @inline -{ - return SZ * BITS; -} - -<* - @require i < SIZE -*> -fn void BitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline -{ - if (value) return self.set(i); - self.unset(i); -} - -<* - @require Type.kindof == UNSIGNED_INT -*> -module std::collections::growablebitset{Type}; -import std::collections::list; - -const BITS = Type.sizeof * 8; - -def GrowableBitSetList = List{Type}; - -struct GrowableBitSet -{ - GrowableBitSetList data; -} - -<* - @param initial_capacity - @param [&inout] allocator "The allocator to use, defaults to the heap allocator" -*> -fn GrowableBitSet* GrowableBitSet.init(&self, Allocator allocator, usz initial_capacity = 1) -{ - self.data.init(allocator, initial_capacity); - return self; -} - -fn GrowableBitSet* GrowableBitSet.tinit(&self, usz initial_capacity = 1) -{ - return self.init(tmem(), initial_capacity) @inline; -} - -fn void GrowableBitSet.free(&self) -{ - self.data.free(); -} - -fn usz GrowableBitSet.cardinality(&self) -{ - usz n; - foreach (x : self.data) - { - n += x.popcount(); - } - return n; -} - -fn void GrowableBitSet.set(&self, usz i) -{ - usz q = i / BITS; - usz r = i % BITS; - usz current_len = self.data.len(); - while (q >= current_len) - { - self.data.push(0); - current_len++; - } - self.data.set(q, self.data[q] | (1 << r)); -} - -fn void GrowableBitSet.unset(&self, usz i) -{ - usz q = i / BITS; - usz r = i % BITS; - if (q >= self.data.len()) return; - self.data.set(q, self.data[q] &~ (1 << r)); -} - -fn bool GrowableBitSet.get(&self, usz i) @operator([]) @inline -{ - usz q = i / BITS; - usz r = i % BITS; - if (q >= self.data.len()) return false; - return self.data[q] & (1 << r) != 0; -} - -fn usz GrowableBitSet.len(&self) @operator(len) -{ - usz n = self.data.len() * BITS; - if (n > 0) n -= (usz)self.data[^1].clz(); - return n; -} - -fn void GrowableBitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline -{ - if (value) return self.set(i); - self.unset(i); -} \ No newline at end of file diff --git a/lib7/std/collections/elastic_array.c3 b/lib7/std/collections/elastic_array.c3 deleted file mode 100644 index 292d48189..000000000 --- a/lib7/std/collections/elastic_array.c3 +++ /dev/null @@ -1,429 +0,0 @@ -// 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. -<* - @require MAX_SIZE >= 1 `The size must be at least 1 element big.` -*> -module std::collections::elastic_array{Type, MAX_SIZE}; -import std::io, std::math, std::collections::list_common; - -def ElementPredicate = fn bool(Type *type); -def ElementTest = fn bool(Type *type, any context); -const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type); -const ELEMENT_IS_POINTER = Type.kindof == POINTER; -macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT; - -struct ElasticArray (Printable) -{ - usz size; - Type[MAX_SIZE] entries; -} - -fn usz! ElasticArray.to_format(&self, Formatter* formatter) @dynamic -{ - switch (self.size) - { - case 0: - return formatter.print("[]")!; - case 1: - return formatter.printf("[%s]", self.entries[0])!; - default: - usz n = formatter.print("[")!; - foreach (i, element : self.entries[:self.size]) - { - if (i != 0) formatter.print(", ")!; - n += formatter.printf("%s", element)!; - } - n += formatter.print("]")!; - return n; - } -} - -fn String ElasticArray.to_tstring(&self) -{ - return string::tformat("%s", *self); -} - -fn void! ElasticArray.push_try(&self, Type element) @inline -{ - if (self.size == MAX_SIZE) return AllocationFailure.OUT_OF_MEMORY?; - self.entries[self.size++] = element; -} - -<* - @require self.size < MAX_SIZE `Tried to exceed the max size` -*> -fn void ElasticArray.push(&self, Type element) @inline -{ - self.entries[self.size++] = element; -} - -fn Type! ElasticArray.pop(&self) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - return self.entries[--self.size]; -} - -fn void ElasticArray.clear(&self) -{ - self.size = 0; -} - -<* - @require self.size > 0 -*> -fn Type! ElasticArray.pop_first(&self) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - defer self.remove_at(0); - return self.entries[0]; -} - -<* - @require index < self.size -*> -fn void ElasticArray.remove_at(&self, usz index) -{ - if (!--self.size || index == self.size) return; - self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size]; -} - -<* - @require other_list.size + self.size <= MAX_SIZE -*> -fn void ElasticArray.add_all(&self, ElasticArray* other_list) -{ - if (!other_list.size) return; - foreach (&value : other_list) - { - self.entries[self.size++] = *value; - } -} - -<* - Add as many elements as possible to the new array, - returning the number of elements that didn't fit. -*> -fn usz ElasticArray.add_all_to_limit(&self, ElasticArray* other_list) -{ - if (!other_list.size) return 0; - foreach (i, &value : other_list) - { - if (self.size == MAX_SIZE) return other_list.size - i; - self.entries[self.size++] = *value; - } - return 0; -} - -<* - Add as many values from this array as possible, returning the - number of elements that didn't fit. - - @param [in] array -*> -fn usz ElasticArray.add_array_to_limit(&self, Type[] array) -{ - if (!array.len) return 0; - foreach (i, &value : array) - { - if (self.size == MAX_SIZE) return array.len - i; - self.entries[self.size++] = *value; - } - return 0; -} - -<* - Add the values of an array to this list. - - @param [in] array - @require array.len + self.size <= MAX_SIZE `Size would exceed max.` - @ensure self.size >= array.len -*> -fn void ElasticArray.add_array(&self, Type[] array) -{ - if (!array.len) return; - foreach (&value : array) - { - self.entries[self.size++] = *value; - } -} - - - -<* - IMPORTANT The returned array must be freed using free_aligned. -*> -fn Type[] ElasticArray.to_aligned_array(&self, Allocator 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[] ElasticArray.to_array(&self, Allocator allocator) -{ - return list_common::list_to_array(Type, self, allocator); -} - -fn Type[] ElasticArray.to_tarray(&self) -{ - $if type_is_overaligned(): - return self.to_aligned_array(tmem()); - $else - return self.to_array(tmem()); - $endif; -} - -<* - Reverse the elements in a list. -*> -fn void ElasticArray.reverse(&self) -{ - list_common::list_reverse(self); -} - -fn Type[] ElasticArray.array_view(&self) -{ - return self.entries[:self.size]; -} - -<* - @require self.size < MAX_SIZE `List would exceed max size` -*> -fn void ElasticArray.push_front(&self, Type type) @inline -{ - self.insert_at(0, type); -} - -<* - @require self.size < MAX_SIZE `List would exceed max size` -*> -fn void! ElasticArray.push_front_try(&self, Type type) @inline -{ - return self.insert_at_try(0, type); -} - -<* - @require index <= self.size -*> -fn void! ElasticArray.insert_at_try(&self, usz index, Type value) -{ - if (self.size == MAX_SIZE) return AllocationFailure.OUT_OF_MEMORY?; - self.insert_at(index, value); -} - -<* - @require self.size < MAX_SIZE `List would exceed max size` - @require index <= self.size -*> -fn void ElasticArray.insert_at(&self, usz index, Type type) -{ - for (usz i = self.size; i > index; i--) - { - self.entries[i] = self.entries[i - 1]; - } - self.size++; - self.entries[index] = type; -} - -<* - @require index < self.size -*> -fn void ElasticArray.set_at(&self, usz index, Type type) -{ - self.entries[index] = type; -} - -fn void! ElasticArray.remove_last(&self) @maydiscard -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - self.size--; -} - -fn void! ElasticArray.remove_first(&self) @maydiscard -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - self.remove_at(0); -} - -fn Type! ElasticArray.first(&self) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - return self.entries[0]; -} - -fn Type! ElasticArray.last(&self) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - return self.entries[self.size - 1]; -} - -fn bool ElasticArray.is_empty(&self) @inline -{ - return !self.size; -} - -fn usz ElasticArray.byte_size(&self) @inline -{ - return Type.sizeof * self.size; -} - -fn usz ElasticArray.len(&self) @operator(len) @inline -{ - return self.size; -} - -fn Type ElasticArray.get(&self, usz index) @inline -{ - return self.entries[index]; -} - -fn void ElasticArray.swap(&self, usz i, usz j) -{ - @swap(self.entries[i], self.entries[j]); -} - -<* - @param filter "The function to determine if it should be removed or not" - @return "the number of deleted elements" -*> -fn usz ElasticArray.remove_if(&self, ElementPredicate filter) -{ - return list_common::list_remove_if(self, filter, false); -} - -<* - @param selection "The function to determine if it should be kept or not" - @return "the number of deleted elements" -*> -fn usz ElasticArray.retain_if(&self, ElementPredicate selection) -{ - return list_common::list_remove_if(self, selection, true); -} - -fn usz ElasticArray.remove_using_test(&self, ElementTest filter, any context) -{ - return list_common::list_remove_using_test(self, filter, false, context); -} - -fn usz ElasticArray.retain_using_test(&self, ElementTest filter, any context) -{ - return list_common::list_remove_using_test(self, filter, true, context); -} - - -macro Type ElasticArray.@item_at(&self, usz index) @operator([]) -{ - return self.entries[index]; -} - -fn Type* ElasticArray.get_ref(&self, usz index) @operator(&[]) @inline -{ - return &self.entries[index]; -} - -fn void ElasticArray.set(&self, usz index, Type value) @operator([]=) -{ - self.entries[index] = value; -} - - -// Functions for equatable types -fn usz! ElasticArray.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE) -{ - foreach (i, v : self) - { - if (equals(v, type)) return i; - } - return SearchResult.MISSING?; -} - -fn usz! ElasticArray.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE) -{ - foreach_r (i, v : self) - { - if (equals(v, type)) return i; - } - return SearchResult.MISSING?; -} - -fn bool ElasticArray.equals(&self, ElasticArray other_list) @if(ELEMENT_IS_EQUATABLE) -{ - if (self.size != other_list.size) return false; - foreach (i, v : self) - { - if (!equals(v, other_list.entries[i])) return false; - } - return true; -} - -<* - Check for presence of a value in a list. - - @param [&in] self "the list to find elements in" - @param value "The value to search for" - @return "True if the value is found, false otherwise" -*> -fn bool ElasticArray.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE) -{ - foreach (i, v : self) - { - if (equals(v, value)) return true; - } - return false; -} - -<* - @param [&inout] self "The list to remove elements from" - @param value "The value to remove" - @return "true if the value was found" -*> -fn bool ElasticArray.remove_last_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE) -{ - return @ok(self.remove_at(self.rindex_of(value))); -} - -<* - @param [&inout] self "The list to remove elements from" - @param value "The value to remove" - @return "true if the value was found" -*> -fn bool ElasticArray.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE) -{ - return @ok(self.remove_at(self.index_of(value))); -} - -<* - @param [&inout] self "The list to remove elements from" - @param value "The value to remove" - @return "the number of deleted elements." -*> -fn usz ElasticArray.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE) -{ - return list_common::list_remove_item(self, value); -} - - - -fn void ElasticArray.remove_all_from(&self, ElasticArray* other_list) @if(ELEMENT_IS_EQUATABLE) -{ - if (!other_list.size) return; - foreach (v : other_list) self.remove_item(v); -} - -<* - @param [&in] self - @return "The number non-null values in the list" -*> -fn usz ElasticArray.compact_count(&self) @if(ELEMENT_IS_POINTER) -{ - usz vals = 0; - foreach (v : self) if (v) vals++; - return vals; -} - -fn usz ElasticArray.compact(&self) @if(ELEMENT_IS_POINTER) -{ - return list_common::list_compact(self); -} \ No newline at end of file diff --git a/lib7/std/collections/enummap.c3 b/lib7/std/collections/enummap.c3 deleted file mode 100644 index 98b7974b0..000000000 --- a/lib7/std/collections/enummap.c3 +++ /dev/null @@ -1,56 +0,0 @@ -<* - @require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enummap" -*> -module std::collections::enummap{Enum, ValueType}; -import std::io; -struct EnumMap (Printable) -{ - ValueType[Enum.len] values; -} - -fn void EnumMap.init(&self, ValueType init_value) -{ - foreach (&a : self.values) - { - *a = init_value; - } -} - -fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic -{ - usz n = formatter.print("{ ")!; - foreach (i, &value : self.values) - { - if (i != 0) formatter.print(", ")!; - n += formatter.printf("%s: %s", Enum.from_ordinal(i), *value)!; - } - n += formatter.print(" }")!; - return n; -} - -<* - @return "The total size of this map, which is the same as the number of enum values" - @pure -*> -fn usz EnumMap.len(&self) @operator(len) @inline -{ - return self.values.len; -} - -<* - @return "Retrieve a value given the underlying enum, if there is no entry, then the zero value for the value is returned." -*> -fn ValueType EnumMap.get(&self, Enum key) @operator([]) @inline -{ - return self.values[key.ordinal]; -} - -fn ValueType* EnumMap.get_ref(&self, Enum key) @operator(&[]) @inline -{ - return &self.values[key.ordinal]; -} - -fn void EnumMap.set(&self, Enum key, ValueType value) @operator([]=) @inline -{ - self.values[key.ordinal] = value; -} \ No newline at end of file diff --git a/lib7/std/collections/enumset.c3 b/lib7/std/collections/enumset.c3 deleted file mode 100644 index 68cf9d807..000000000 --- a/lib7/std/collections/enumset.c3 +++ /dev/null @@ -1,160 +0,0 @@ -// 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. - -<* - @require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enumset" -*> -module std::collections::enumset{Enum}; -import std::io; - -def EnumSetType = $typefrom(type_for_enum_elements(Enum.elements)) @private; - -const IS_CHAR_ARRAY = Enum.elements > 128; -distinct EnumSet (Printable) = EnumSetType; - -fn void EnumSet.add(&self, Enum v) -{ - $if IS_CHAR_ARRAY: - (*self)[(usz)v.ordinal / 8] |= (char)(1u << ((usz)v.ordinal % 8)); - $else - *self = (EnumSet)((EnumSetType)*self | 1u << (EnumSetType)v.ordinal); - $endif -} - -fn void EnumSet.clear(&self) -{ - $if IS_CHAR_ARRAY: - *self = {}; - $else - *self = 0; - $endif -} - -fn bool EnumSet.remove(&self, Enum v) -{ - $if IS_CHAR_ARRAY: - if (!self.has(v) @inline) return false; - (*self)[(usz)v.ordinal / 8] &= (char)~(1u << ((usz)v.ordinal % 8)); - return true; - $else - EnumSetType old = (EnumSetType)*self; - EnumSetType new = old & ~(1u << (EnumSetType)v.ordinal); - *self = (EnumSet)new; - return old != new; - $endif -} - -fn bool EnumSet.has(&self, Enum v) -{ - $if IS_CHAR_ARRAY: - return (bool)(((*self)[(usz)v.ordinal / 8] << ((usz)v.ordinal % 8)) & 0x01); - $else - return ((EnumSetType)*self & (1u << (EnumSetType)v.ordinal)) != 0; - $endif -} - -fn void EnumSet.add_all(&self, EnumSet s) -{ - $if IS_CHAR_ARRAY: - foreach (i, c : s) (*self)[i] |= c; - $else - *self = (EnumSet)((EnumSetType)*self | (EnumSetType)s); - $endif -} - -fn void EnumSet.retain_all(&self, EnumSet s) -{ - $if IS_CHAR_ARRAY: - foreach (i, c : s) (*self)[i] &= c; - $else - *self = (EnumSet)((EnumSetType)*self & (EnumSetType)s); - $endif -} - -fn void EnumSet.remove_all(&self, EnumSet s) -{ - $if IS_CHAR_ARRAY: - foreach (i, c : s) (*self)[i] &= ~c; - $else - *self = (EnumSet)((EnumSetType)*self & ~(EnumSetType)s); - $endif -} - -fn EnumSet EnumSet.and_of(&self, EnumSet s) -{ - $if IS_CHAR_ARRAY: - EnumSet copy = *self; - copy.retain_all(s); - return copy; - $else - return (EnumSet)((EnumSetType)*self & (EnumSetType)s); - $endif -} - -fn EnumSet EnumSet.or_of(&self, EnumSet s) -{ - $if IS_CHAR_ARRAY: - EnumSet copy = *self; - copy.add_all(s); - return copy; - $else - return (EnumSet)((EnumSetType)*self | (EnumSetType)s); - $endif -} - - -fn EnumSet EnumSet.diff_of(&self, EnumSet s) -{ - $if IS_CHAR_ARRAY: - EnumSet copy = *self; - copy.remove_all(s); - return copy; - $else - return (EnumSet)((EnumSetType)*self & ~(EnumSetType)s); - $endif -} - -fn EnumSet EnumSet.xor_of(&self, EnumSet s) -{ - $if IS_CHAR_ARRAY: - EnumSet copy = *self; - foreach (i, c : s) copy[i] ^= c; - return copy; - $else - return (EnumSet)((EnumSetType)*self ^ (EnumSetType)s); - $endif -} - -fn usz! EnumSet.to_format(&set, Formatter* formatter) @dynamic -{ - usz n = formatter.print("[")!; - bool found; - foreach (value : Enum.values) - { - if (!set.has(value)) continue; - if (found) n += formatter.print(", ")!; - found = true; - n += formatter.printf("%s", value)!; - } - n += formatter.print("]")!; - return n; -} - -macro typeid type_for_enum_elements(usz $elements) @local -{ - $switch - $case ($elements > 128): - return char[($elements + 7) / 8].typeid; - $case ($elements > 64): - return uint128.typeid; - $case ($elements > 32 || $$C_INT_SIZE > 32): - return ulong.typeid; - $case ($elements > 16 || $$C_INT_SIZE > 16): - return uint.typeid; - $case ($elements > 8 || $$C_INT_SIZE > 8): - return ushort.typeid; - $default: - return char.typeid; - $endswitch -} \ No newline at end of file diff --git a/lib7/std/collections/hashmap.c3 b/lib7/std/collections/hashmap.c3 deleted file mode 100644 index a033555af..000000000 --- a/lib7/std/collections/hashmap.c3 +++ /dev/null @@ -1,580 +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. -<* - @require $defined((Key){}.hash()) `No .hash function found on the key` -*> -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; - Allocator allocator; - uint count; // Number of elements - uint threshold; // Resize limit - 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.init(&self, Allocator allocator, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) -{ - capacity = math::next_power_of_2(capacity); - self.allocator = allocator; - self.load_factor = load_factor; - self.threshold = (uint)(capacity * load_factor); - self.table = allocator::new_array(allocator, Entry*, capacity); - return self; -} - -<* - @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.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) -{ - return self.init(tmem(), capacity, load_factor) @inline; -} - -<* - @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" -*> -macro HashMap* HashMap.init_with_key_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) -{ - self.init(allocator, capacity, load_factor); - $for (var $i = 0; $i < $vacount; $i += 2) - 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" - @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.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.init(allocator, capacity, load_factor); - for (usz i = 0; i < keys.len; i++) - { - self.set(keys[i], values[i]); - } - return self; -} - - -<* - @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" -*> -fn HashMap* HashMap.tinit_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) -{ - return self.init_from_keys_and_values(tmem(), keys, values, capacity, load_factor); -} - -<* - Has this hash map been initialized yet? - - @param [&in] map "The hash map we are testing" - @return "Returns true if it has been initialized, false otherwise" -*> -fn bool HashMap.is_initialized(&map) -{ - return (bool)map.allocator; -} - -<* - @param [&inout] allocator "The allocator to use" - @param [&in] other_map "The map to copy from." -*> -fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map) -{ - self.init(allocator, other_map.table.len, other_map.load_factor); - self.put_all_for_create(other_map); - return self; -} - -<* - @param [&in] other_map "The map to copy from." -*> -fn HashMap* HashMap.tinit_from_map(&map, HashMap* other_map) -{ - return map.init_from_map(tmem(), other_map) @inline; -} - -fn bool HashMap.is_empty(&map) @inline -{ - return !map.count; -} - -fn usz HashMap.len(&map) @inline -{ - return map.count; -} - -fn Value*! HashMap.get_ref(&map, Key key) -{ - if (!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*! HashMap.get_entry(&map, Key key) -{ - if (!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; - } - return SearchResult.MISSING?; -} - -<* - Get the value or update and - @require $assignable(#expr, Value) -*> -macro Value HashMap.@get_or_set(&map, Key key, Value #expr) -{ - if (!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! HashMap.get(&map, Key key) @operator([]) -{ - return *map.get_ref(key) @inline; -} - -fn bool HashMap.has_key(&map, Key key) -{ - return @ok(map.get_ref(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(); - } - 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! HashMap.remove(&map, Key key) @maydiscard -{ - if (!map.remove_entry_for_key(key)) return SearchResult.MISSING?; -} - -fn void HashMap.clear(&map) -{ - if (!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 HashMap.free(&map) -{ - if (!map.allocator) return; - map.clear(); - map.free_internal(map.table.ptr); - map.table = {}; -} - -fn Key[] HashMap.tkeys(&self) -{ - return self.keys(tmem()) @inline; -} - -fn Key[] HashMap.keys(&self, Allocator allocator) -{ - if (!self.count) return {}; - - Key[] list = allocator::alloc_array(allocator, Key, self.count); - usz index = 0; - foreach (Entry* entry : self.table) - { - while (entry) - { - $if COPY_KEYS: - list[index++] = entry.key.copy(allocator); - $else - list[index++] = entry.key; - $endif - entry = entry.next; - } - } - return list; -} - -macro HashMap.@each(map; @body(key, value)) -{ - map.@each_entry(; Entry* entry) - { - @body(entry.key, entry.value); - }; -} - -macro HashMap.@each_entry(map; @body(entry)) -{ - if (!map.count) return; - foreach (Entry* entry : map.table) - { - while (entry) - { - @body(entry); - entry = entry.next; - } - } -} - -fn Value[] HashMap.tvalues(&map) -{ - return map.values(tmem()) @inline; -} - -fn Value[] HashMap.values(&self, Allocator allocator) -{ - if (!self.count) return {}; - Value[] list = allocator::alloc_array(allocator, Value, self.count); - usz index = 0; - foreach (Entry* entry : self.table) - { - while (entry) - { - list[index++] = entry.value; - entry = entry.next; - } - } - return list; -} - -fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE) -{ - if (!map.count) return false; - foreach (Entry* entry : map.table) - { - while (entry) - { - if (equals(v, entry.value)) return true; - entry = entry.next; - } - } - return false; -} - -fn HashMapIterator HashMap.iter(&self) -{ - return { .map = self, .index = -1 }; -} - -fn HashMapValueIterator HashMap.value_iter(&self) -{ - return { .map = self, .index = -1 }; -} - -fn HashMapKeyIterator HashMap.key_iter(&self) -{ - return { .map = self, .index = -1 }; -} - -// --- private methods - -fn void HashMap.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 HashMap.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 usz! HashMap.to_format(&self, Formatter* f) @dynamic -{ - usz len; - len += f.print("{ ")!; - self.@each_entry(; Entry* entry) - { - if (len > 2) len += f.print(", ")!; - len += f.printf("%s: %s", entry.key, entry.value)!; - }; - return len + f.print(" }"); -} - -fn void HashMap.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 HashMap.put_all_for_create(&map, HashMap* other_map) @private -{ - if (!other_map.count) return; - foreach (Entry *e : other_map.table) - { - while (e) - { - map.put_for_create(e.key, e.value); - e = e.next; - } - } -} - -fn void HashMap.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 HashMap.free_internal(&map, void* ptr) @inline @private -{ - allocator::free(map.allocator, ptr); -} - -fn bool HashMap.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 HashMap.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 HashMap.free_entry(&self, Entry *entry) @local -{ - $if COPY_KEYS: - allocator::free(self.allocator, entry.key); - $endif - self.free_internal(entry); -} - - -struct HashMapIterator -{ - HashMap* map; - int top_index; - int index; - Entry* current_entry; -} - -distinct HashMapValueIterator = HashMapIterator; -distinct HashMapKeyIterator = HashMapIterator; - - -<* - @require idx < self.map.count -*> -fn Entry HashMapIterator.get(&self, usz idx) @operator([]) -{ - if (idx < self.index) - { - self.top_index = 0; - self.current_entry = null; - self.index = -1; - } - while (self.index != idx) - { - if (self.current_entry) - { - self.current_entry = self.current_entry.next; - if (self.current_entry) self.index++; - continue; - } - self.current_entry = self.map.table[self.top_index++]; - if (self.current_entry) self.index++; - } - return *self.current_entry; -} - -fn Value HashMapValueIterator.get(&self, usz idx) @operator([]) -{ - return ((HashMapIterator*)self).get(idx).value; -} - -fn Key HashMapKeyIterator.get(&self, usz idx) @operator([]) -{ - return ((HashMapIterator*)self).get(idx).key; -} - -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/lib7/std/collections/linkedlist.c3 b/lib7/std/collections/linkedlist.c3 deleted file mode 100644 index 921813c0a..000000000 --- a/lib7/std/collections/linkedlist.c3 +++ /dev/null @@ -1,334 +0,0 @@ -// 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{Type}; - -const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type); - -struct Node @private -{ - Node *next; - Node *prev; - Type value; -} - -struct LinkedList -{ - Allocator allocator; - usz size; - Node *_first; - Node *_last; -} - -<* - @param [&inout] allocator "The allocator to use, defaults to the heap allocator" - @return "the initialized list" -*> -fn LinkedList* LinkedList.init(&self, Allocator allocator) -{ - *self = { .allocator = allocator }; - return self; -} - -fn LinkedList* LinkedList.tinit(&self) -{ - return self.init(tmem()) @inline; -} - -<* - @require self.allocator != null -*> -macro void LinkedList.free_node(&self, Node* node) @private -{ - allocator::free(self.allocator, node); -} - -macro Node* LinkedList.alloc_node(&self) @private -{ - if (!self.allocator) self.allocator = tmem(); - return allocator::alloc(self.allocator, Node); -} - -fn void LinkedList.push_front(&self, Type value) -{ - Node *first = self._first; - Node *new_node = self.alloc_node(); - *new_node = { .next = first, .value = value }; - self._first = new_node; - if (!first) - { - self._last = new_node; - } - else - { - first.prev = new_node; - } - self.size++; -} - -fn void LinkedList.push(&self, Type value) -{ - Node *last = self._last; - Node *new_node = self.alloc_node(); - *new_node = { .prev = last, .value = value }; - self._last = new_node; - if (!last) - { - self._first = new_node; - } - else - { - last.next = new_node; - } - self.size++; -} - -fn Type! LinkedList.peek(&self) => self.first() @inline; -fn Type! LinkedList.peek_last(&self) => self.last() @inline; - -fn Type! LinkedList.first(&self) -{ - if (!self._first) return IteratorResult.NO_MORE_ELEMENT?; - return self._first.value; -} - -fn Type! LinkedList.last(&self) -{ - if (!self._last) return IteratorResult.NO_MORE_ELEMENT?; - return self._last.value; -} - -fn void LinkedList.free(&self) => self.clear() @inline; - -fn void LinkedList.clear(&self) -{ - for (Node* node = self._first; node != null;) - { - Node* next = node.next; - self.free_node(node); - node = next; - } - self._first = null; - self._last = null; - self.size = 0; -} - -fn usz LinkedList.len(&self) @inline => self.size; - -<* - @require index < self.size -*> -macro Node* LinkedList.node_at_index(&self, usz index) -{ - if (index * 2 >= self.size) - { - Node* node = self._last; - index = self.size - index - 1; - while (index--) node = node.prev; - return node; - } - Node* node = self._first; - while (index--) node = node.next; - return node; -} -<* - @require index < self.size -*> -fn Type LinkedList.get(&self, usz index) -{ - return self.node_at_index(index).value; -} - -<* - @require index < self.size -*> -fn void LinkedList.set(&self, usz index, Type element) -{ - self.node_at_index(index).value = element; -} - -<* - @require index < self.size -*> -fn void LinkedList.remove_at(&self, usz index) -{ - self.unlink(self.node_at_index(index)); -} - -<* - @require index <= self.size -*> -fn void LinkedList.insert_at(&self, usz index, Type element) -{ - switch (index) - { - case 0: - self.push_front(element); - case self.size: - self.push(element); - default: - self.link_before(self.node_at_index(index), element); - } -} -<* - @require succ != null -*> -fn void LinkedList.link_before(&self, Node *succ, Type value) @private -{ - Node* pred = succ.prev; - Node* new_node = self.alloc_node(); - *new_node = { .prev = pred, .next = succ, .value = value }; - succ.prev = new_node; - if (!pred) - { - self._first = new_node; - } - else - { - pred.next = new_node; - } - self.size++; -} - -<* - @require self._first != null -*> -fn void LinkedList.unlink_first(&self) @private -{ - Node* f = self._first; - Node* next = f.next; - self.free_node(f); - self._first = next; - if (!next) - { - self._last = null; - } - else - { - next.prev = null; - } - self.size--; -} - -fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE) -{ - usz start = self.size; - Node* node = self._first; - while (node) - { - switch - { - case equals(node.value, t): - Node* next = node.next; - self.unlink(node); - node = next; - default: - node = node.next; - } - } - return start - self.size; -} - -fn Type! LinkedList.pop(&self) -{ - if (!self._last) return IteratorResult.NO_MORE_ELEMENT?; - defer self.unlink_last(); - return self._last.value; -} - -fn bool LinkedList.is_empty(&self) -{ - return !self._first; -} - -fn Type! LinkedList.pop_front(&self) -{ - if (!self._first) return IteratorResult.NO_MORE_ELEMENT?; - defer self.unlink_first(); - return self._first.value; -} - -fn void! LinkedList.remove_last(&self) @maydiscard -{ - if (!self._first) return IteratorResult.NO_MORE_ELEMENT?; - self.unlink_last(); -} - -fn void! LinkedList.remove_first(&self) @maydiscard -{ - if (!self._first) return IteratorResult.NO_MORE_ELEMENT?; - self.unlink_first(); -} - - -fn bool LinkedList.remove_first_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE) -{ - for (Node* node = self._first; node != null; node = node.next) - { - if (node.value == t) - { - self.unlink(node); - return true; - } - } - return false; -} - -fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE) -{ - for (Node* node = self._last; node != null; node = node.prev) - { - if (node.value == t) - { - self.unlink(node); - return true; - } - } - return false; -} -<* - @require self._last != null -*> -fn void LinkedList.unlink_last(&self) @inline @private -{ - Node* l = self._last; - Node* prev = l.prev; - self._last = prev; - self.free_node(l); - if (!prev) - { - self._first = null; - } - else - { - prev.next = null; - } - self.size--; -} - -<* - @require x != null -*> -fn void LinkedList.unlink(&self, Node* x) @private -{ - Node* next = x.next; - Node* prev = x.prev; - if (!prev) - { - self._first = next; - } - else - { - prev.next = next; - } - if (!next) - { - self._last = prev; - } - else - { - next.prev = prev; - } - self.free_node(x); - self.size--; -} diff --git a/lib7/std/collections/list.c3 b/lib7/std/collections/list.c3 deleted file mode 100644 index c2e1c6bb2..000000000 --- a/lib7/std/collections/list.c3 +++ /dev/null @@ -1,570 +0,0 @@ -// 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{Type}; -import std::io, std::math, std::collections::list_common; - -def ElementPredicate = fn bool(Type *type); -def ElementTest = fn bool(Type *type, any context); -const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type); -const ELEMENT_IS_POINTER = Type.kindof == POINTER; - -macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT; - -struct List (Printable) -{ - usz size; - usz capacity; - Allocator allocator; - Type *entries; -} - -<* - @param initial_capacity "The initial capacity to reserve" - @param [&inout] allocator "The allocator to use, defaults to the heap allocator" -*> -fn List* List.init(&self, Allocator allocator, usz initial_capacity = 16) -{ - 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.tinit(&self, usz initial_capacity = 16) -{ - return self.init(tmem(), initial_capacity) @inline; -} - -<* - Initialize a new list with an array. - - @param [in] values `The values to initialize the list with.` - @require self.size == 0 "The List must be empty" -*> -fn List* List.init_with_array(&self, Allocator allocator, Type[] values) -{ - self.init(allocator, values.len) @inline; - self.add_array(values) @inline; - return self; -} - -<* - Initialize a temporary list with an array. - - @param [in] values `The values to initialize the list with.` - @require self.size == 0 "The List must be empty" -*> -fn List* List.tinit_with_array(&self, Type[] values) -{ - self.tinit(values.len) @inline; - self.add_array(values) @inline; - return self; -} - -<* - @require self.capacity == 0 "The List must not be allocated" -*> -fn void List.init_wrapping_array(&self, Allocator allocator, Type[] types) -{ - self.allocator = allocator; - self.capacity = types.len; - self.entries = types.ptr; - self.set_size(types.len); -} - -fn usz! List.to_format(&self, Formatter* formatter) @dynamic -{ - switch (self.size) - { - case 0: - return formatter.print("[]")!; - case 1: - return formatter.printf("[%s]", self.entries[0])!; - default: - usz n = formatter.print("[")!; - foreach (i, element : self.entries[:self.size]) - { - if (i != 0) formatter.print(", ")!; - n += formatter.printf("%s", element)!; - } - n += formatter.print("]")!; - return n; - } -} - -fn void List.push(&self, Type element) @inline -{ - self.reserve(1); - self.entries[self.set_size(self.size + 1)] = element; -} - -fn Type! List.pop(&self) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - defer self.set_size(self.size - 1); - return self.entries[self.size - 1]; -} - -fn void List.clear(&self) -{ - self.set_size(0); -} - -fn Type! List.pop_first(&self) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - defer self.remove_at(0); - return self.entries[0]; -} - -<* - @require index < self.size `Removed element out of bounds` -*> -fn void List.remove_at(&self, usz index) -{ - self.set_size(self.size - 1); - if (!self.size || index == self.size) return; - self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size]; -} - -fn void List.add_all(&self, List* other_list) -{ - if (!other_list.size) return; - self.reserve(other_list.size); - usz index = self.set_size(self.size + other_list.size); - foreach (&value : other_list) - { - self.entries[index++] = *value; - } -} - - -<* - IMPORTANT The returned array must be freed using free_aligned. -*> -fn Type[] List.to_aligned_array(&self, Allocator 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_array(&self, Allocator allocator) -{ - return list_common::list_to_array(Type, self, allocator); -} - -fn Type[] List.to_tarray(&self) -{ - $if type_is_overaligned(): - return self.to_aligned_array(tmem()); - $else - return self.to_array(tmem()); - $endif; -} - -<* - Reverse the elements in a list. -*> -fn void List.reverse(&self) -{ - list_common::list_reverse(self); -} - -fn Type[] List.array_view(&self) -{ - return self.entries[:self.size]; -} - -<* - Add the values of an array to this list. - - @param [in] array - @ensure self.size >= array.len -*> -fn void List.add_array(&self, Type[] array) -{ - if (!array.len) return; - self.reserve(array.len); - usz index = self.set_size(self.size + array.len); - self.entries[index : array.len] = array[..]; -} - -fn void List.push_front(&self, Type type) @inline -{ - self.insert_at(0, type); -} - -<* - @require index <= self.size `Insert was out of bounds` -*> -fn void List.insert_at(&self, usz index, Type type) -{ - self.reserve(1); - self.set_size(self.size + 1); - for (isz i = self.size - 1; i > index; i--) - { - self.entries[i] = self.entries[i - 1]; - } - self.entries[index] = type; -} - -<* - @require index < self.size -*> -fn void List.set_at(&self, usz index, Type type) -{ - self.entries[index] = type; -} - -fn void! List.remove_last(&self) @maydiscard -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - self.set_size(self.size - 1); -} - -fn void! List.remove_first(&self) @maydiscard -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - self.remove_at(0); -} - -fn Type! List.first(&self) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - return self.entries[0]; -} - -fn Type! List.last(&self) -{ - if (!self.size) return IteratorResult.NO_MORE_ELEMENT?; - return self.entries[self.size - 1]; -} - -fn bool List.is_empty(&self) @inline -{ - return !self.size; -} - -fn usz List.byte_size(&self) @inline -{ - return Type.sizeof * self.size; -} - -fn usz List.len(&self) @operator(len) @inline -{ - return self.size; -} - -<* - @require index < self.size `Access out of bounds` -*> -fn Type List.get(&self, usz index) @inline -{ - return self.entries[index]; -} - -fn void List.free(&self) -{ - if (!self.allocator || !self.capacity) return; - - self.pre_free(); // Remove sanitizer annotation - - $if type_is_overaligned(): - allocator::free_aligned(self.allocator, self.entries); - $else - allocator::free(self.allocator, self.entries); - $endif; - self.capacity = 0; - self.size = 0; - self.entries = null; -} - -<* - @require i < self.size && j < self.size `Access out of bounds` -*> -fn void List.swap(&self, usz i, usz j) -{ - @swap(self.entries[i], self.entries[j]); -} - -<* - @param filter "The function to determine if it should be removed or not" - @return "the number of deleted elements" -*> -fn usz List.remove_if(&self, ElementPredicate filter) -{ - return list_common::list_remove_if(self, filter, false); -} - -<* - @param selection "The function to determine if it should be kept or not" - @return "the number of deleted elements" -*> -fn usz List.retain_if(&self, ElementPredicate selection) -{ - return list_common::list_remove_if(self, selection, true); -} - -fn usz List.remove_using_test(&self, ElementTest filter, any context) -{ - usz old_size = self.size; - defer - { - if (old_size != self.size) self._update_size_change(old_size, self.size); - } - return list_common::list_remove_using_test(self, filter, false, context); -} - - - -fn usz List.retain_using_test(&self, ElementTest filter, any context) -{ - usz old_size = self.size; - defer { - if (old_size != self.size) self._update_size_change(old_size, self.size); - } - return list_common::list_remove_using_test(self, filter, true, context); -} - -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(); - - self.pre_free(); // Remove sanitizer annotation - - min_capacity = math::next_power_of_2(min_capacity); - $if type_is_overaligned(): - self.entries = allocator::realloc_aligned(self.allocator, self.entries, Type.sizeof * min_capacity, alignment: Type[1].alignof)!!; - $else - self.entries = allocator::realloc(self.allocator, self.entries, Type.sizeof * min_capacity); - $endif; - self.capacity = min_capacity; - - self.post_alloc(); // Add sanitizer annotation -} - -<* - @require index < self.size `Access out of bounds` -*> -macro Type List.@item_at(&self, usz index) @operator([]) -{ - return self.entries[index]; -} - -<* - @require index < self.size `Access out of bounds` -*> -fn Type* List.get_ref(&self, usz index) @operator(&[]) @inline -{ - return &self.entries[index]; -} - -<* - @require index < self.size `Access out of bounds` -*> -fn void List.set(&self, usz index, Type value) @operator([]=) -{ - self.entries[index] = value; -} - -fn void List.reserve(&self, usz added) -{ - usz new_size = self.size + added; - if (self.capacity >= new_size) return; - - assert(new_size < usz.max / 2U); - usz new_capacity = self.capacity ? 2U * self.capacity : 16U; - while (new_capacity < new_size) new_capacity *= 2U; - self.ensure_capacity(new_capacity); -} - -fn void List._update_size_change(&self,usz old_size, usz new_size) -{ - if (old_size == new_size) return; - sanitizer::annotate_contiguous_container(self.entries, - &self.entries[self.capacity], - &self.entries[old_size], - &self.entries[new_size]); -} -<* - @require new_size == 0 || self.capacity != 0 -*> -fn usz List.set_size(&self, usz new_size) @inline @private -{ - usz old_size = self.size; - self._update_size_change(old_size, new_size); - self.size = new_size; - return old_size; -} - -macro void List.pre_free(&self) @private -{ - if (!self.capacity) return; - self._update_size_change(self.size, self.capacity); -} - -<* - @require self.capacity > 0 -*> -macro void List.post_alloc(&self) @private -{ - self._update_size_change(self.capacity, self.size); -} - -// Functions for equatable types - - -fn usz! List.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE) -{ - foreach (i, v : self) - { - if (equals(v, type)) return i; - } - return SearchResult.MISSING?; -} - -fn usz! List.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE) -{ - foreach_r (i, v : self) - { - if (equals(v, type)) return i; - } - return SearchResult.MISSING?; -} - -fn bool List.equals(&self, List other_list) @if(ELEMENT_IS_EQUATABLE) -{ - if (self.size != other_list.size) return false; - foreach (i, v : self) - { - if (!equals(v, other_list.entries[i])) return false; - } - return true; -} - -<* - Check for presence of a value in a list. - - @param [&in] self "the list to find elements in" - @param value "The value to search for" - @return "True if the value is found, false otherwise" -*> -fn bool List.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE) -{ - foreach (i, v : self) - { - if (equals(v, value)) return true; - } - return false; -} - -<* - @param [&inout] self "The list to remove elements from" - @param value "The value to remove" - @return "true if the value was found" -*> -fn bool List.remove_last_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE) -{ - return @ok(self.remove_at(self.rindex_of(value))); -} - -<* - @param [&inout] self "The list to remove elements from" - @param value "The value to remove" - @return "true if the value was found" -*> -fn bool List.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE) -{ - return @ok(self.remove_at(self.index_of(value))); -} -<* - @param [&inout] self "The list to remove elements from" - @param value "The value to remove" - @return "the number of deleted elements." -*> -fn usz List.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE) -{ - usz old_size = self.size; - defer { - if (old_size != self.size) self._update_size_change(old_size, self.size); - } - return list_common::list_remove_item(self, value); -} - - - -fn void List.remove_all_from(&self, List* other_list) @if(ELEMENT_IS_EQUATABLE) -{ - if (!other_list.size) return; - usz old_size = self.size; - defer { - if (old_size != self.size) self._update_size_change(old_size, self.size); - } - foreach (v : other_list) self.remove_item(v); -} - -<* - @param [&in] self - @return "The number non-null values in the list" -*> -fn usz List.compact_count(&self) @if(ELEMENT_IS_POINTER) -{ - usz vals = 0; - foreach (v : self) if (v) vals++; - return vals; -} - -fn usz List.compact(&self) @if(ELEMENT_IS_POINTER) -{ - usz old_size = self.size; - defer { - if (old_size != self.size) self._update_size_change(old_size, self.size); - } - return list_common::list_compact(self); -} - -// --> Deprecated - -<* - @param [&inout] self "The list to remove elements from" - @param value "The value to remove" - @return "true if the value was found" -*> -fn bool List.remove_last_match(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated -{ - return self.remove_last_item(value) @inline; -} - -<* - @param [&inout] self "The list to remove elements from" - @param value "The value to remove" - @return "true if the value was found" -*> -fn bool List.remove_first_match(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated -{ - return self.remove_first_item(value) @inline; -} - - -<* - @param [&inout] self "The list to remove elements from" - @param value "The value to remove" - @return "the number of deleted elements." -*> -fn usz List.remove_all_matches(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated -{ - return self.remove_item(value) @inline; -} diff --git a/lib7/std/collections/list_common.c3 b/lib7/std/collections/list_common.c3 deleted file mode 100644 index 021fa1fac..000000000 --- a/lib7/std/collections/list_common.c3 +++ /dev/null @@ -1,112 +0,0 @@ -module std::collections::list_common; - -<* - IMPORTANT The returned array must be freed using free_aligned. -*> -macro list_to_aligned_array($Type, self, Allocator allocator) -{ - if (!self.size) return ($Type[]){}; - $Type[] result = allocator::alloc_array_aligned(allocator, $Type, self.size); - result[..] = self.entries[:self.size]; - return result; -} - -macro list_to_array($Type, self, Allocator allocator) -{ - if (!self.size) return ($Type[]){}; - $Type[] result = allocator::alloc_array(allocator, $Type, self.size); - result[..] = self.entries[:self.size]; - return result; -} - -macro void list_reverse(self) -{ - if (self.size < 2) return; - usz half = self.size / 2U; - usz end = self.size - 1; - for (usz i = 0; i < half; i++) - { - @swap(self.entries[i], self.entries[end - i]); - } -} - -macro usz list_remove_using_test(self, filter, bool $invert, ctx) -{ - usz size = self.size; - for (usz i = size, usz k = size; k > 0; k = i) - { - // Find last index of item to be deleted. - $if $invert: - while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--; - $else - while (i > 0 && filter(&self.entries[i - 1], ctx)) i--; - $endif - // Remove the items from this index up to the one not to be deleted. - usz n = self.size - k; - self.entries[i:n] = self.entries[k:n]; - self.size -= k - i; - // Find last index of item not to be deleted. - $if $invert: - while (i > 0 && filter(&self.entries[i - 1], ctx)) i--; - $else - while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--; - $endif - } - return size - self.size; -} - -macro usz list_compact(self) -{ - usz size = self.size; - for (usz i = size; i > 0; i--) - { - if (self.entries[i - 1]) continue; - for (usz j = i; j < size; j++) - { - self.entries[j - 1] = self.entries[j]; - } - self.size--; - } - return size - self.size; -} - -macro usz list_remove_item(self, value) -{ - usz size = self.size; - for (usz i = size; i > 0; i--) - { - if (!equals(self.entries[i - 1], value)) continue; - for (usz j = i; j < self.size; j++) - { - self.entries[j - 1] = self.entries[j]; - } - self.size--; - } - return size - self.size; -} - - -macro usz list_remove_if(self, filter, bool $invert) -{ - usz size = self.size; - for (usz i = size, usz k = size; k > 0; k = i) - { - // Find last index of item to be deleted. - $if $invert: - while (i > 0 && !filter(&self.entries[i - 1])) i--; - $else - while (i > 0 && filter(&self.entries[i - 1])) i--; - $endif - // Remove the items from this index up to the one not to be deleted. - usz n = self.size - k; - self.entries[i:n] = self.entries[k:n]; - self.size -= k - i; - // Find last index of item not to be deleted. - $if $invert: - while (i > 0 && filter(&self.entries[i - 1])) i--; - $else - while (i > 0 && !filter(&self.entries[i - 1])) i--; - $endif - } - return size - self.size; -} diff --git a/lib7/std/collections/maybe.c3 b/lib7/std/collections/maybe.c3 deleted file mode 100644 index dd5dea31f..000000000 --- a/lib7/std/collections/maybe.c3 +++ /dev/null @@ -1,36 +0,0 @@ -module std::collections::maybe{Type}; -import std::io; - -struct Maybe (Printable) -{ - Type value; - bool has_value; -} - -fn usz! Maybe.to_format(&self, Formatter* f) @dynamic -{ - if (self.has_value) return f.printf("[%s]", self.value); - return f.printf("[EMPTY]"); -} - -fn void Maybe.set(&self, Type val) -{ - *self = { .value = val, .has_value = true }; -} - -fn void Maybe.reset(&self) -{ - *self = {}; -} - -fn Maybe value(Type val) -{ - return { .value = val, .has_value = true }; -} - -const Maybe EMPTY = { }; - -macro Type! Maybe.get(self) -{ - return self.has_value ? self.value : SearchResult.MISSING?; -} diff --git a/lib7/std/collections/object.c3 b/lib7/std/collections/object.c3 deleted file mode 100644 index b2ce81704..000000000 --- a/lib7/std/collections/object.c3 +++ /dev/null @@ -1,468 +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::object; -import std::collections::map, std::collections::list, std::io; - -const Object TRUE_OBJECT = { .b = true, .type = bool.typeid }; -const Object FALSE_OBJECT = { .b = false, .type = bool.typeid }; -const Object NULL_OBJECT = { .type = void*.typeid }; - -struct Object (Printable) -{ - typeid type; - Allocator allocator; - union - { - uint128 i; - double f; - bool b; - String s; - void* other; - ObjectInternalList array; - ObjectInternalMap map; - } -} - - -fn usz! Object.to_format(&self, Formatter* formatter) @dynamic -{ - switch (self.type) - { - case void: - return formatter.printf("{}")!; - case void*: - return formatter.printf("null")!; - case String: - return formatter.printf(`"%s"`, self.s)!; - case bool: - return formatter.printf(self.b ? "true" : "false")!; - case ObjectInternalList: - usz n = formatter.printf("[")!; - foreach (i, ol : self.array) - { - if (i > 0) n += formatter.printf(",")!; - n += ol.to_format(formatter)!; - } - n += formatter.printf("]")!; - return n; - case ObjectInternalMap: - usz n = formatter.printf("{")!; - @stack_mem(1024; Allocator mem) - { - foreach (i, key : self.map.keys(mem)) - { - if (i > 0) n += formatter.printf(",")!; - n += formatter.printf(`"%s":`, key)!; - n += self.map.get(key).to_format(formatter)!; - } - }; - n += formatter.printf("}")!; - return n; - default: - switch (self.type.kindof) - { - case SIGNED_INT: - return formatter.printf("%d", (int128)self.i)!; - case UNSIGNED_INT: - return formatter.printf("%d", (uint128)self.i)!; - case FLOAT: - return formatter.printf("%g", self.f)!; - case ENUM: - return formatter.printf("%d", self.i)!; - default: - return formatter.printf("<>")!; - } - } -} - -fn Object* new_obj(Allocator allocator) -{ - return allocator::new(allocator, Object, { .allocator = allocator, .type = void.typeid }); -} - -fn Object* new_null() -{ - return &NULL_OBJECT; -} - -fn Object* new_int(int128 i, Allocator allocator) -{ - return allocator::new(allocator, Object, { .i = i, .allocator = allocator, .type = int128.typeid }); -} - -macro Object* new_enum(e, Allocator allocator) -{ - return allocator::new(allocator, Object, { .i = (int128)e, .allocator = allocator, .type = @typeid(e) }); -} - -fn Object* new_float(double f, Allocator allocator) -{ - return allocator::new(allocator, Object, { .f = f, .allocator = allocator, .type = double.typeid }); -} - -fn Object* new_string(String s, Allocator allocator) -{ - return allocator::new(allocator, Object, { .s = s.copy(allocator), .allocator = allocator, .type = String.typeid }); -} - - -fn Object* new_bool(bool b) -{ - return b ? &TRUE_OBJECT : &FALSE_OBJECT; -} - -fn void Object.free(&self) -{ - switch (self.type) - { - case void: - break; - case String: - allocator::free(self.allocator, self.s); - case ObjectInternalList: - foreach (ol : self.array) - { - ol.free(); - } - self.array.free(); - case ObjectInternalMap: - self.map.@each_entry(; ObjectInternalMapEntry* entry) { - entry.value.free(); - }; - self.map.free(); - default: - break; - } - if (self.allocator) allocator::free(self.allocator, self); -} - -fn bool Object.is_null(&self) @inline => self == &NULL_OBJECT; -fn bool Object.is_empty(&self) @inline => self.type == void.typeid; -fn bool Object.is_map(&self) @inline => self.type == ObjectInternalMap.typeid; -fn bool Object.is_array(&self) @inline => self.type == ObjectInternalList.typeid; -fn bool Object.is_bool(&self) @inline => self.type == bool.typeid; -fn bool Object.is_string(&self) @inline => self.type == String.typeid; -fn bool Object.is_float(&self) @inline => self.type == double.typeid; -fn bool Object.is_int(&self) @inline => self.type == int128.typeid; -fn bool Object.is_keyable(&self) => self.is_empty() || self.is_map(); -fn bool Object.is_indexable(&self) => self.is_empty() || self.is_array(); - -<* - @require self.is_keyable() -*> -fn void Object.init_map_if_needed(&self) @private -{ - if (self.is_empty()) - { - self.type = ObjectInternalMap.typeid; - self.map.init(self.allocator); - } -} - -<* - @require self.is_indexable() -*> -fn void Object.init_array_if_needed(&self) @private -{ - if (self.is_empty()) - { - self.type = ObjectInternalList.typeid; - self.array.init(self.allocator); - } -} - -<* - @require self.is_keyable() -*> -fn void Object.set_object(&self, String key, Object* new_object) @private -{ - self.init_map_if_needed(); - ObjectInternalMapEntry*! entry = self.map.get_entry(key); - defer - { - (void)entry.value.free(); - } - self.map.set(key, new_object); -} - -macro Object* Object.object_from_value(&self, value) @private -{ - var $Type = $typeof(value); - $switch - $case types::is_int($Type): - return new_int(value, self.allocator); - $case types::is_float($Type): - return new_float(value, self.allocator); - $case $Type.typeid == String.typeid: - return new_string(value, self.allocator); - $case $Type.typeid == bool.typeid: - return new_bool(value); - $case $Type.typeid == Object*.typeid: - return value; - $case $Type.typeid == void*.typeid: - if (value != null) return CastResult.TYPE_MISMATCH?; - return &NULL_OBJECT; - $case $assignable(value, String): - return new_string(value, self.allocator); - $default: - $error "Unsupported object type."; - $endswitch - -} - -macro Object* Object.set(&self, String key, value) -{ - Object* val = self.object_from_value(value); - self.set_object(key, val); - return val; -} - -<* - @require self.is_indexable() -*> -macro Object* Object.set_at(&self, usz index, String key, value) -{ - Object* val = self.object_from_value(value); - self.set_object_at(key, index, val); - return val; -} - -<* - @require self.is_indexable() - @ensure return != null -*> -macro Object* Object.push(&self, value) -{ - Object* val = self.object_from_value(value); - self.push_object(val); - return val; -} - -<* - @require self.is_keyable() -*> -fn Object*! Object.get(&self, String key) => self.is_empty() ? SearchResult.MISSING? : self.map.get(key); - - -fn bool Object.has_key(&self, String key) => self.is_map() && self.map.has_key(key); - -<* - @require self.is_indexable() -*> -fn Object* Object.get_at(&self, usz index) -{ - return self.array.get(index); -} - -<* - @require self.is_indexable() -*> -fn usz Object.get_len(&self) -{ - return self.array.len(); -} - -<* - @require self.is_indexable() -*> -fn void Object.push_object(&self, Object* to_append) -{ - self.init_array_if_needed(); - self.array.push(to_append); -} - -<* - @require self.is_indexable() -*> -fn void Object.set_object_at(&self, usz index, Object* to_set) -{ - self.init_array_if_needed(); - while (self.array.len() < index) - { - self.array.push(&NULL_OBJECT); - } - if (self.array.len() == index) - { - self.array.push(to_set); - return; - } - self.array.get(index).free(); - self.array.set_at(index, to_set); -} - -<* - @require $Type.kindof.is_int() "Expected an integer type." -*> -macro get_integer_value(Object* value, $Type) -{ - if (value.is_float()) - { - return ($Type)value.f; - } - if (value.is_string()) - { - $if $Type.kindof == TypeKind.SIGNED_INT: - return ($Type)value.s.to_int128(); - $else - return ($Type)value.s.to_uint128(); - $endif - } - if (!value.is_int()) return NumberConversion.MALFORMED_INTEGER?; - return ($Type)value.i; -} - - -<* - @require self.is_indexable() - @require $Type.kindof.is_int() : "Expected an integer type" -*> -macro Object.get_integer_at(&self, $Type, usz index) @private -{ - return get_integer_value(self.get_at(index), $Type); -} - -<* - @require self.is_keyable() - @require $Type.kindof.is_int() : "Expected an integer type" -*> -macro Object.get_integer(&self, $Type, String key) @private -{ - return get_integer_value(self.get(key), $Type); -} - -fn ichar! Object.get_ichar(&self, String key) => self.get_integer(ichar, key); -fn short! Object.get_short(&self, String key) => self.get_integer(short, key); -fn int! Object.get_int(&self, String key) => self.get_integer(int, key); -fn long! Object.get_long(&self, String key) => self.get_integer(long, key); -fn int128! Object.get_int128(&self, String key) => self.get_integer(int128, key); - -fn ichar! Object.get_ichar_at(&self, usz index) => self.get_integer_at(ichar, index); -fn short! Object.get_short_at(&self, usz index) => self.get_integer_at(short, index); -fn int! Object.get_int_at(&self, usz index) => self.get_integer_at(int, index); -fn long! Object.get_long_at(&self, usz index) => self.get_integer_at(long, index); -fn int128! Object.get_int128_at(&self, usz index) => self.get_integer_at(int128, index); - -fn char! Object.get_char(&self, String key) => self.get_integer(ichar, key); -fn short! Object.get_ushort(&self, String key) => self.get_integer(ushort, key); -fn uint! Object.get_uint(&self, String key) => self.get_integer(uint, key); -fn ulong! Object.get_ulong(&self, String key) => self.get_integer(ulong, key); -fn uint128! Object.get_uint128(&self, String key) => self.get_integer(uint128, key); - -fn char! Object.get_char_at(&self, usz index) => self.get_integer_at(char, index); -fn ushort! Object.get_ushort_at(&self, usz index) => self.get_integer_at(ushort, index); -fn uint! Object.get_uint_at(&self, usz index) => self.get_integer_at(uint, index); -fn ulong! Object.get_ulong_at(&self, usz index) => self.get_integer_at(ulong, index); -fn uint128! Object.get_uint128_at(&self, usz index) => self.get_integer_at(uint128, index); - -<* - @require self.is_keyable() -*> -fn String! Object.get_string(&self, String key) -{ - Object* value = self.get(key)!; - if (!value.is_string()) return CastResult.TYPE_MISMATCH?; - return value.s; -} - -<* - @require self.is_indexable() -*> -fn String! Object.get_string_at(&self, usz index) -{ - Object* value = self.get_at(index); - if (!value.is_string()) return CastResult.TYPE_MISMATCH?; - return value.s; -} - -<* - @require self.is_keyable() -*> -macro String! Object.get_enum(&self, $EnumType, String key) -{ - Object value = self.get(key)!; - if ($EnumType.typeid != value.type) return CastResult.TYPE_MISMATCH?; - return ($EnumType)value.i; -} - -<* - @require self.is_indexable() -*> -macro String! Object.get_enum_at(&self, $EnumType, usz index) -{ - Object value = self.get_at(index); - if ($EnumType.typeid != value.type) return CastResult.TYPE_MISMATCH?; - return ($EnumType)value.i; -} - -<* - @require self.is_keyable() -*> -fn bool! Object.get_bool(&self, String key) -{ - Object* value = self.get(key)!; - if (!value.is_bool()) return CastResult.TYPE_MISMATCH?; - return value.b; -} - - -<* - @require self.is_indexable() -*> -fn bool! Object.get_bool_at(&self, usz index) -{ - Object* value = self.get_at(index); - if (!value.is_bool()) return CastResult.TYPE_MISMATCH?; - return value.b; -} - -<* - @require self.is_keyable() -*> -fn double! Object.get_float(&self, String key) -{ - Object* value = self.get(key)!; - switch (value.type.kindof) - { - case SIGNED_INT: - return (double)value.i; - case UNSIGNED_INT: - return (double)(uint128)value.i; - case FLOAT: - return value.f; - default: - return CastResult.TYPE_MISMATCH?; - } -} - -<* - @require self.is_indexable() -*> -fn double! Object.get_float_at(&self, usz index) -{ - Object* value = self.get_at(index); - switch (value.type.kindof) - { - case SIGNED_INT: - return (double)value.i; - case UNSIGNED_INT: - return (double)(uint128)value.i; - case FLOAT: - return value.f; - default: - return CastResult.TYPE_MISMATCH?; - } -} - -fn Object* Object.get_or_create_obj(&self, String key) -{ - if (try obj = self.get(key) && !obj.is_null()) return obj; - Object* container = new_obj(self.allocator); - self.set(key, container); - return container; -} - -def ObjectInternalMap = HashMap{String, Object*} @private; -def ObjectInternalList = List{Object*} @private; -def ObjectInternalMapEntry = Entry{String, Object*} @private; - diff --git a/lib7/std/collections/priorityqueue.c3 b/lib7/std/collections/priorityqueue.c3 deleted file mode 100644 index 4a244292f..000000000 --- a/lib7/std/collections/priorityqueue.c3 +++ /dev/null @@ -1,155 +0,0 @@ -// priorityqueue.c3 -// A priority queue using a classic binary heap for C3. -// -// 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 -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// 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{Type}; -import std::collections::priorityqueue::private; - -distinct PriorityQueue = inline PrivatePriorityQueue{Type, false}; -distinct PriorityQueueMax = inline PrivatePriorityQueue{Type, true}; - -module std::collections::priorityqueue::private{Type, MAX}; -import std::collections::list, std::io; - -struct PrivatePriorityQueue (Printable) -{ - List{Type} heap; -} - -fn PrivatePriorityQueue* PrivatePriorityQueue.init(&self, Allocator allocator, usz initial_capacity = 16, ) @inline -{ - self.heap.init(allocator, initial_capacity); - return self; -} - -fn PrivatePriorityQueue* PrivatePriorityQueue.tinit(&self, usz initial_capacity = 16) @inline -{ - self.init(tmem(), initial_capacity); - return self; -} - - -fn void PrivatePriorityQueue.push(&self, Type element) -{ - self.heap.push(element); - usz i = self.heap.len() - 1; - while (i > 0) - { - usz parent = (i - 1) / 2; - Type item = self.heap[i]; - Type parent_item = self.heap[parent]; - $if MAX: - bool ok = greater(item, parent_item); - $else - bool ok = less(item, parent_item); - $endif - if (!ok) break; - self.heap.swap(i, parent); - i = parent; - } -} - -<* - @require index < self.len() : "Index out of range" -*> -fn void PrivatePriorityQueue.remove_at(&self, usz index) -{ - if (index == 0) - { - self.pop()!!; - return; - } - self.heap.remove_at(index); -} -<* - @require self != null -*> -fn Type! PrivatePriorityQueue.pop(&self) -{ - usz i = 0; - usz len = self.heap.len(); - if (!len) return IteratorResult.NO_MORE_ELEMENT?; - usz new_count = len - 1; - self.heap.swap(0, new_count); - while OUTER: ((2 * i + 1) < new_count) - { - usz j = 2 * i + 1; - Type left = self.heap[j]; - Type item = self.heap[i]; - switch - { - case j + 1 < new_count: - Type right = self.heap[j + 1]; - $if MAX: - if (!greater(right, left)) nextcase; - if (!greater(right, item)) break OUTER; - $else - if (!greater(left, right)) nextcase; - if (!greater(item, right)) break OUTER; - $endif - j++; - default: - $if MAX: - if (!greater(left, item)) break OUTER; - $else - if (!greater(item, left)) break OUTER; - $endif - } - self.heap.swap(i, j); - i = j; - } - - return self.heap.pop(); -} - -fn Type! PrivatePriorityQueue.first(&self) -{ - return self.heap.first(); -} - -fn void PrivatePriorityQueue.free(&self) -{ - self.heap.free(); -} - -fn usz PrivatePriorityQueue.len(&self) @operator(len) -{ - return self.heap.len() @inline; -} - -fn bool PrivatePriorityQueue.is_empty(&self) -{ - return self.heap.is_empty() @inline; -} - -<* - @require index < self.len() -*> -fn Type PrivatePriorityQueue.get(&self, usz index) @operator([]) -{ - return self.heap[index]; -} - -fn usz! PrivatePriorityQueue.to_format(&self, Formatter* formatter) @dynamic -{ - return self.heap.to_format(formatter); -} - diff --git a/lib7/std/collections/range.c3 b/lib7/std/collections/range.c3 deleted file mode 100644 index e689856ef..000000000 --- a/lib7/std/collections/range.c3 +++ /dev/null @@ -1,65 +0,0 @@ -<* - @require Type.is_ordered : "The type must be ordered" -*> -module std::collections::range{Type}; -import std::io; - -struct Range (Printable) -{ - Type start; - Type end; -} - -fn usz Range.len(&self) @operator(len) -{ - if (self.end < self.start) return 0; - return (usz)(self.end - self.start) + 1; -} - -fn bool Range.contains(&self, Type value) @inline -{ - return value >= self.start && value <= self.end; -} - -<* - @require index < self.len() : "Can't index into an empty range" -*> -fn Type Range.get(&self, usz index) @operator([]) -{ - return (Type)(self.start + (usz)index); -} - -fn usz! Range.to_format(&self, Formatter* formatter) @dynamic -{ - return formatter.printf("[%s..%s]", self.start, self.end)!; -} - -struct ExclusiveRange (Printable) -{ - Type start; - Type end; -} - -fn usz ExclusiveRange.len(&self) @operator(len) -{ - if (self.end < self.start) return 0; - return (usz)(self.end - self.start); -} - -fn bool ExclusiveRange.contains(&self, Type value) @inline -{ - return value >= self.start && value < self.end; -} - -fn usz! ExclusiveRange.to_format(&self, Formatter* formatter) @dynamic -{ - return formatter.printf("[%s..<%s]", self.start, self.end)!; -} - -<* - @require index < self.len() : "Can't index into an empty range" -*> -fn Type ExclusiveRange.get(&self, usz index) @operator([]) -{ - return (Type)(self.start + index); -} diff --git a/lib7/std/collections/ringbuffer.c3 b/lib7/std/collections/ringbuffer.c3 deleted file mode 100644 index d3213d770..000000000 --- a/lib7/std/collections/ringbuffer.c3 +++ /dev/null @@ -1,115 +0,0 @@ -<* - @require Type.kindof == ARRAY : "Required an array type" -*> -module std::collections::ringbuffer{Type}; -import std::io; - -def Element = $typeof((Type){}[0]); - -struct RingBuffer (Printable) -{ - Type buf; - usz written; - usz head; -} - -fn void RingBuffer.init(&self) @inline -{ - *self = {}; -} - -fn void RingBuffer.push(&self, Element c) -{ - if (self.written < self.buf.len) - { - self.buf[self.written] = c; - self.written++; - } - else - { - self.buf[self.head] = c; - self.head = (self.head + 1) % self.buf.len; - } -} - -fn Element RingBuffer.get(&self, usz index) @operator([]) -{ - index %= self.buf.len; - usz avail = self.buf.len - self.head; - if (index < avail) - { - return self.buf[self.head + index]; - } - return self.buf[index - avail]; -} - -fn Element! RingBuffer.pop(&self) -{ - switch - { - case self.written == 0: - return SearchResult.MISSING?; - case self.written < self.buf.len: - self.written--; - return self.buf[self.written]; - default: - self.head = (self.head - 1) % self.buf.len; - return self.buf[self.head]; - } -} - -fn usz! RingBuffer.to_format(&self, Formatter* format) @dynamic -{ - // Improve this? - return format.printf("%s", self.buf); -} - -fn usz RingBuffer.read(&self, usz index, Element[] buffer) -{ - index %= self.buf.len; - if (self.written < self.buf.len) - { - if (index >= self.written) return 0; - usz end = self.written - index; - usz n = min(end, buffer.len); - buffer[:n] = self.buf[index:n]; - return n; - } - usz end = self.buf.len - self.head; - if (index >= end) - { - index -= end; - if (index >= self.head) return 0; - usz n = min(self.head - index, buffer.len); - buffer[:n] = self.buf[index:n]; - return n; - } - if (buffer.len <= self.buf.len - index) - { - usz n = buffer.len; - buffer[:n] = self.buf[self.head + index:n]; - return n; - } - usz n1 = self.buf.len - index; - buffer[:n1] = self.buf[self.head + index:n1]; - buffer = buffer[n1..]; - index -= n1; - usz n2 = min(self.head - index, buffer.len); - buffer[:n2] = self.buf[index:n2]; - return n1 + n2; -} - -fn void RingBuffer.write(&self, Element[] buffer) -{ - usz i; - while (self.written < self.buf.len && i < buffer.len) - { - self.buf[self.written] = buffer[i++]; - self.written++; - } - foreach (c : buffer[i..]) - { - self.buf[self.head] = c; - self.head = (self.head + 1) % self.buf.len; - } -} \ No newline at end of file diff --git a/lib7/std/collections/tuple.c3 b/lib7/std/collections/tuple.c3 deleted file mode 100644 index 597605e35..000000000 --- a/lib7/std/collections/tuple.c3 +++ /dev/null @@ -1,16 +0,0 @@ -module std::collections::tuple{Type1, Type2}; - -struct Tuple -{ - Type1 first; - Type2 second; -} - -module std::collections::triple{Type1, Type2, Type3}; - -struct Triple -{ - Type1 first; - Type2 second; - Type3 third; -} \ No newline at end of file diff --git a/lib7/std/compression/qoi.c3 b/lib7/std/compression/qoi.c3 deleted file mode 100644 index b896b7e6d..000000000 --- a/lib7/std/compression/qoi.c3 +++ /dev/null @@ -1,474 +0,0 @@ -module std::compression::qoi; - -const uint PIXELS_MAX = 400000000; - -<* - Colorspace. - Purely informative. It will be saved to the file header, - but does not affect how chunks are en-/decoded. -*> -enum QOIColorspace : char (char id) -{ - SRGB = 0, // sRGB with linear alpha - LINEAR = 1 // all channels linear -} - -<* - Channels. - The channels used in an image. - AUTO can be used when decoding to automatically determine - the channels from the file's header. -*> -enum QOIChannels : char (char id) -{ - AUTO = 0, - RGB = 3, - RGBA = 4 -} - -<* - Descriptor. - Contains information about an image. -*> -struct QOIDesc -{ - uint width; - uint height; - QOIChannels channels; - QOIColorspace colorspace; -} - -<* - QOI Errors. - These are all the possible bad outcomes. -*> -fault QOIError -{ - INVALID_PARAMETERS, - FILE_OPEN_FAILED, - FILE_WRITE_FAILED, - INVALID_DATA, - TOO_MANY_PIXELS -} - - -// Let the user decide if they want to use std::io -module std::compression::qoi @if(!$feature(QOI_NO_STDIO)); -import std::io; - -<* - Encode raw RGB or RGBA pixels into a QOI image and write it to the - file system. - - The desc struct must be filled with the image width, height, the - used channels (QOIChannels.RGB or RGBA) and the colorspace - (QOIColorspace.SRGB or LINEAR). - - The function returns an optional, which can either be a QOIError - or the number of bytes written on success. - - @param [in] filename `The file's name to write the image to` - @param [in] input `The raw RGB or RGBA pixels to encode` - @param [&in] desc `The descriptor of the image` -*> -fn usz! write(String filename, char[] input, QOIDesc* desc) => @pool() -{ - // encode data - char[] output = encode(tmem(), input, desc)!; - - file::save(filename, output)!; - return output.len; -} - - -<* - Read and decode a QOI image from the file system. - - If channels is set to QOIChannels.AUTO, the function will - automatically determine the channels from the file's header. - However, if channels is RGB or RGBA, the output format will be - forced into this number of channels. - - The desc struct will be filled with the width, height, - channels and colorspace of the image. - - The function returns an optional, which can either be a QOIError - or a char[] pointing to the decoded pixels on success. - - The returned pixel data should be free()d after use, or the decoding - and use of the data should be wrapped in a @pool() { ... }; block. - - @param [in] filename `The file's name to read the image from` - @param [&out] desc `The descriptor to fill with the image's info` - @param channels `The channels to be used` -*> -fn char[]! read(Allocator allocator, String filename, QOIDesc* desc, QOIChannels channels = AUTO) => @pool(allocator) -{ - // read file - char[] data = file::load_temp(filename) ?? QOIError.FILE_OPEN_FAILED?!; - // pass data to decode function - return decode(allocator, data, desc, channels); -} - - - -// Back to basic non-stdio mode -module std::compression::qoi; -import std::bits; - -<* - Encode raw RGB or RGBA pixels into a QOI image in memory. - - The function returns an optional, which can either be a QOIError - or a char[] pointing to the encoded data on success. - - The returned qoi data should be free()d after use, or the encoding - and use of the data should be wrapped in a @pool() { ... }; block. - See the write() function for an example. - - @param [in] input `The raw RGB or RGBA pixels to encode` - @param [&in] desc `The descriptor of the image` -*> -fn char[]! encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard -{ - // check info in desc - if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_PARAMETERS?; - if (desc.channels == AUTO) return QOIError.INVALID_PARAMETERS?; - uint pixels = desc.width * desc.height; - if (pixels > PIXELS_MAX) return QOIError.TOO_MANY_PIXELS?; - - // check input data size - uint image_size = pixels * desc.channels.id; - if (image_size != input.len) return QOIError.INVALID_DATA?; - - // allocate memory for encoded data (output) - // header + chunk tag and RGB(A) data for each pixel + end of stream - uint max_size = Header.sizeof + pixels + image_size + END_OF_STREAM.len; - char[] output = allocator::alloc_array(allocator, char, max_size); // no need to init - defer catch allocator::free(allocator, output); - - // write header - *(Header*)output.ptr = { - .be_magic = bswap('qoif'), - .be_width = bswap(desc.width), - .be_height = bswap(desc.height), - .channels = desc.channels.id, - .colorspace = desc.colorspace.id - }; - - uint pos = Header.sizeof; // Current position in output - uint loc; // Current position in image (top-left corner) - uint loc_end = image_size - desc.channels.id; // End of image data - char run_length = 0; // Length of the current run - - Pixel[64] palette; // Zero-initialized by default - Pixel prev = { 0, 0, 0, 255 }; - Pixel p = { 0, 0, 0, 255 }; - - ichar[<3>] diff; // pre-allocate for diff - ichar[<3>] luma; // ...and luma - - // write chunks - for (loc = 0; loc < image_size; loc += desc.channels.id) - { - // set previous pixel - prev = p; - - // get current pixel - p[:3] = input[loc:3]; // cutesy slices :3 - if (desc.channels == RGBA) p.a = input[loc + 3]; - - // check if we can run the previous pixel - if (prev == p) - { - run_length++; - if (run_length == 62 || loc == loc_end) - { - *@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 }; - run_length = 0; - } - continue; - } - // end last run if there was one - if (run_length > 0) - { - *@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 }; - run_length = 0; - } - - switch - { - // check if we can index the palette - case (palette[p.hash()] == p): - *@extract(OpIndex, output, &pos) = { - OP_INDEX, - p.hash() - }; - - // check if we can use diff or luma - case (prev != p && prev.a == p.a): - // diff the pixels - diff = p.rgb - prev.rgb; - if (diff.r > -3 && diff.r < 2 - && diff.g > -3 && diff.g < 2 - && diff.b > -3 && diff.b < 2) - { - *@extract(OpDiff, output, &pos) = { - OP_DIFF, - (char)diff.r + 2, - (char)diff.g + 2, - (char)diff.b + 2 - }; - palette[p.hash()] = p; - break; - } - // check luma eligibility - luma = { diff.r - diff.g, diff.g, diff.b - diff.g }; - if (luma.r >= -8 && luma.r <= 7 - && luma.g >= -32 && luma.g <= 31 - && luma.b >= -8 && luma.b <= 7) - { - *@extract(OpLuma, output, &pos) = { - OP_LUMA, - (char)luma.g + 32, - (char)luma.r + 8, - (char)luma.b + 8 - }; - palette[p.hash()] = p; - break; - } - nextcase; - - // worst case scenario: just encode the raw pixel - default: - if (prev.a != p.a) - { - *@extract(OpRGBA, output, &pos) = { OP_RGBA, p.r, p.g, p.b, p.a }; - } - else - { - *@extract(OpRGB, output, &pos) = { OP_RGB, p.r, p.g, p.b }; - } - palette[p.hash()] = p; - } - } - - // write end of stream - output[pos:END_OF_STREAM.len] = END_OF_STREAM; - pos += END_OF_STREAM.len; - - return output[:pos]; -} - - - -<* - Decode a QOI image from memory. - - If channels is set to QOIChannels.AUTO, the function will - automatically determine the channels from the file's header. - However, if channels is RGB or RGBA, the output format will be - forced into this number of channels. - - The desc struct will be filled with the width, height, - channels and colorspace of the image. - - The function returns an optional, which can either be a QOIError - or a char[] pointing to the decoded pixels on success. - - The returned pixel data should be free()d after use, or the decoding - and use of the data should be wrapped in a @pool() { ... }; block. - - @param [in] data `The QOI image data to decode` - @param [&out] desc `The descriptor to fill with the image's info` - @param channels `The channels to be used` -*> -fn char[]! decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels channels = AUTO) @nodiscard -{ - // check input data - if (data.len < Header.sizeof + END_OF_STREAM.len) return QOIError.INVALID_DATA?; - - // get header - Header* header = (Header*)data.ptr; - - // check magic bytes (FourCC) - if (bswap(header.be_magic) != 'qoif') return QOIError.INVALID_DATA?; - - // copy header data to desc - desc.width = bswap(header.be_width); - desc.height = bswap(header.be_height); - desc.channels = @enumcast(QOIChannels, header.channels)!; // Rethrow if invalid - desc.colorspace = @enumcast(QOIColorspace, header.colorspace)!; // Rethrow if invalid - if (desc.channels == AUTO) return QOIError.INVALID_DATA?; // Channels must be specified in the header - - // check width and height - if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_DATA?; - - // check pixel count - ulong pixels = (ulong)desc.width * (ulong)desc.height; - if (pixels > PIXELS_MAX) return QOIError.TOO_MANY_PIXELS?; - - uint pos = Header.sizeof; // Current position in data - uint loc; // Current position in image (top-left corner) - char run_length = 0; // Length of the current run - char tag; // Current chunk tag - - Pixel[64] palette; // Zero-initialized by default - Pixel p = { 0, 0, 0, 255 }; - - if (channels == AUTO) channels = desc.channels; - - // allocate memory for image data - usz image_size = (usz)pixels * channels.id; - char[] image = allocator::alloc_array(allocator, char, image_size); - defer catch allocator::free(allocator, image); - - for (loc = 0; loc < image_size; loc += channels.id) - { - // get chunk tag - tag = data[pos]; - - // check for chunk type - switch - { - case run_length > 0: - run_length--; - - case tag == OP_RGB: - OpRGB* op = @extract(OpRGB, data, &pos); - p = { op.red, op.green, op.blue, p.a }; - palette[p.hash()] = p; - - case tag == OP_RGBA: - OpRGBA* op = @extract(OpRGBA, data, &pos); - p = { op.red, op.green, op.blue, op.alpha }; - palette[p.hash()] = p; - - case tag >> 6 == OP_INDEX: - OpIndex* op = @extract(OpIndex, data, &pos); - p = palette[op.index]; - - case tag >> 6 == OP_DIFF: - OpDiff* op = @extract(OpDiff, data, &pos); - p.r += op.diff_red - 2; - p.g += op.diff_green - 2; - p.b += op.diff_blue - 2; - palette[p.hash()] = p; - - case tag >> 6 == OP_LUMA: - OpLuma* op = @extract(OpLuma, data, &pos); - int diff_green = op.diff_green - 32; - p.r += (char)(op.diff_red_minus_green - 8 + diff_green); - p.g += (char)(diff_green); - p.b += (char)(op.diff_blue_minus_green - 8 + diff_green); - palette[p.hash()] = p; - - case tag >> 6 == OP_RUN: - OpRun* op = @extract(OpRun, data, &pos); - run_length = op.run; - } - - // draw the pixel - if (channels == RGBA) { image[loc:4] = p.rgba; } else { image[loc:3] = p.rgb; } - } - - return image; -} - - - -// *************************************************************************** -// *** *** -// *** Main functions are at the top to make the file more readable. *** -// *** From here on, helper functions and types are defined. *** -// *** *** -// *************************************************************************** -module std::compression::qoi @private; - -// 8-bit opcodes -const OP_RGB = 0b11111110; -const OP_RGBA = 0b11111111; -// 2-bit opcodes -const OP_INDEX = 0b00; -const OP_DIFF = 0b01; -const OP_LUMA = 0b10; -const OP_RUN = 0b11; - -struct Header @packed -{ - uint be_magic; // magic bytes "qoif" - uint be_width; // image width in pixels (BE) - uint be_height; // image height in pixels (BE) - - // informative fields - char channels; // 3 = RGB, 4 = RGB - char colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear -} - -const char[?] END_OF_STREAM = {0, 0, 0, 0, 0, 0, 0, 1}; - -// inefficient, but it's only run once at a time -macro @enumcast($Type, raw) -{ - foreach (value : $Type.values) - { - if (value.id == raw) return value; - } - return QOIError.INVALID_DATA?; -} - -distinct Pixel = inline char[<4>]; -macro char Pixel.hash(Pixel p) -{ - return (p.r * 3 + p.g * 5 + p.b * 7 + p.a * 11) % 64; -} - -struct OpRGB // No need to use @packed here, the alignment is 1 anyways. -{ - char tag; - char red; - char green; - char blue; -} -struct OpRGBA @packed -{ - char tag; - char red; - char green; - char blue; - char alpha; -} -bitstruct OpIndex : char -{ - char tag : 6..7; - char index : 0..5; -} -bitstruct OpDiff : char -{ - char tag : 6..7; - char diff_red : 4..5; - char diff_green : 2..3; - char diff_blue : 0..1; -} -bitstruct OpLuma : ushort @align(1) -{ - char tag : 6..7; - char diff_green : 0..5; - char diff_red_minus_green : 12..15; - char diff_blue_minus_green : 8..11; -} -bitstruct OpRun : char -{ - char tag : 6..7; - char run : 0..5; -} - -// Macro used to locate chunks in data buffers. -// Can be used both for reading and writing. -macro @extract($Type, char[] data, uint* pos) -{ - // slice data, then double cast - $Type* chunk = ($Type*)data[*pos : $Type.sizeof].ptr; - *pos += $Type.sizeof; - return chunk; -} diff --git a/lib7/std/core/allocators/arena_allocator.c3 b/lib7/std/core/allocators/arena_allocator.c3 deleted file mode 100644 index 952e3cad5..000000000 --- a/lib7/std/core/allocators/arena_allocator.c3 +++ /dev/null @@ -1,112 +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::core::mem::allocator; -import std::math; - -struct ArenaAllocator (Allocator) -{ - char[] data; - usz used; -} - -<* - Initialize a memory arena for use using the provided bytes. -*> -fn ArenaAllocator* ArenaAllocator.init(&self, char[] data) -{ - self.data = data; - self.used = 0; - return self; -} - -fn void ArenaAllocator.clear(&self) -{ - self.used = 0; -} - -struct ArenaAllocatorHeader @local -{ - usz size; - char[?] data; -} - -macro ArenaAllocator* wrap(char[] bytes) -{ - return (ArenaAllocator){}.init(bytes); -} - -<* - @require ptr != null -*> -fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic -{ - assert((uptr)ptr >= (uptr)self.data.ptr, "Pointer originates from a different allocator."); - ArenaAllocatorHeader* header = ptr - ArenaAllocatorHeader.sizeof; - // Reclaim memory if it's the last element. - if (ptr + header.size == &self.data[self.used]) - { - self.used -= header.size + ArenaAllocatorHeader.sizeof; - } -} - -fn usz ArenaAllocator.mark(&self) @dynamic => self.used; -fn void ArenaAllocator.reset(&self, usz mark) @dynamic => self.used = mark; - -<* - @require !alignment || math::is_power_of_2(alignment) - @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big` - @require size > 0 -*> -fn void*! ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic -{ - alignment = alignment_for_allocation(alignment); - usz total_len = self.data.len; - if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?; - void* start_mem = self.data.ptr; - void* unaligned_pointer_to_offset = start_mem + self.used + ArenaAllocatorHeader.sizeof; - void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment); - usz end = (usz)(mem - self.data.ptr) + size; - if (end > total_len) return AllocationFailure.OUT_OF_MEMORY?; - self.used = end; - ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof; - header.size = size; - if (init_type == ZERO) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT); - return mem; -} - -<* - @require !alignment || math::is_power_of_2(alignment) - @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big` - @require old_pointer != null - @require size > 0 -*> -fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment) @dynamic -{ - alignment = alignment_for_allocation(alignment); - assert(old_pointer >= self.data.ptr, "Pointer originates from a different allocator."); - usz total_len = self.data.len; - if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?; - ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof; - usz old_size = header.size; - // Do last allocation and alignment match? - if (&self.data[self.used] == old_pointer + old_size && mem::ptr_is_aligned(old_pointer, alignment)) - { - if (old_size >= size) - { - self.used -= old_size - size; - } - else - { - usz new_used = self.used + size - old_size; - if (new_used > total_len) return AllocationFailure.OUT_OF_MEMORY?; - self.used = new_used; - } - header.size = size; - return old_pointer; - } - // Otherwise just allocate new memory. - void* mem = self.acquire(size, NO_ZERO, alignment)!; - mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT); - return mem; -} \ No newline at end of file diff --git a/lib7/std/core/allocators/dynamic_arena.c3 b/lib7/std/core/allocators/dynamic_arena.c3 deleted file mode 100644 index 7d1b9556f..000000000 --- a/lib7/std/core/allocators/dynamic_arena.c3 +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright (c) 2021-2024 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::core::mem::allocator; -import std::math; - -struct DynamicArenaAllocator (Allocator) -{ - Allocator backing_allocator; - DynamicArenaPage* page; - DynamicArenaPage* unused_page; - usz page_size; -} - -<* - @param [&inout] allocator - @require page_size >= 128 -*> -fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator allocator) -{ - self.page = null; - self.unused_page = null; - self.page_size = page_size; - self.backing_allocator = allocator; -} - -fn void DynamicArenaAllocator.free(&self) -{ - DynamicArenaPage* page = self.page; - while (page) - { - DynamicArenaPage* next_page = page.prev_arena; - allocator::free(self.backing_allocator, page.memory); - allocator::free(self.backing_allocator, page); - page = next_page; - } - page = self.unused_page; - while (page) - { - DynamicArenaPage* next_page = page.prev_arena; - allocator::free(self.backing_allocator, page.memory); - allocator::free(self.backing_allocator, page); - page = next_page; - } - self.page = null; - self.unused_page = null; -} - -struct DynamicArenaPage @local -{ - void* memory; - void* prev_arena; - usz total; - usz used; - void* current_stack_ptr; -} - -struct DynamicArenaChunk @local -{ - usz size; -} - -<* - @require ptr != null - @require self.page != null `tried to free pointer on invalid allocator` -*> -fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic -{ - DynamicArenaPage* current_page = self.page; - if (ptr == current_page.current_stack_ptr) - { - current_page.used = (usz)((ptr - DEFAULT_SIZE_PREFIX) - current_page.memory); - } - current_page.current_stack_ptr = null; -} - -<* - @require size > 0 `Resize doesn't support zeroing` - @require old_pointer != null `Resize doesn't handle null pointers` - @require self.page != null `tried to realloc pointer on invalid allocator` -*> -fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic -{ - DynamicArenaPage* current_page = self.page; - alignment = alignment_for_allocation(alignment); - usz* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX; - usz old_size = *old_size_ptr; - // We have the old pointer and it's correctly aligned. - if (old_size >= size && mem::ptr_is_aligned(old_pointer, alignment)) - { - *old_size_ptr = size; - if (current_page.current_stack_ptr == old_pointer) - { - current_page.used = (usz)((old_pointer - DEFAULT_SIZE_PREFIX) - current_page.memory); - } - return old_pointer; - } - if REUSE: (current_page.current_stack_ptr == old_pointer && mem::ptr_is_aligned(old_pointer, alignment)) - { - assert(size > old_size); - usz add_size = size - old_size; - if (add_size + current_page.used > current_page.total) break REUSE; - *old_size_ptr = size; - current_page.used += add_size; - return old_pointer; - } - void* new_mem = self.acquire(size, NO_ZERO, alignment)!; - mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT); - return new_mem; -} - -fn void DynamicArenaAllocator.reset(&self, usz mark = 0) @dynamic -{ - assert(mark == 0, "Unexpectedly reset dynamic arena allocator with mark %d", mark); - DynamicArenaPage* page = self.page; - DynamicArenaPage** unused_page_ptr = &self.unused_page; - while (page) - { - DynamicArenaPage* next_page = page.prev_arena; - page.used = 0; - DynamicArenaPage* prev_unused = *unused_page_ptr; - *unused_page_ptr = page; - page.prev_arena = prev_unused; - page = next_page; - } - self.page = page; -} - -<* - @require math::is_power_of_2(alignment) - @require size > 0 -*> -fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local -{ - // First, make sure that we can align it, extending the page size if needed. - usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + alignment, alignment)); - assert(page_size > size + DynamicArenaChunk.sizeof); - // Grab the page without alignment (we do it ourselves) - void* mem = allocator::malloc_try(self.backing_allocator, page_size)!; - DynamicArenaPage*! page = allocator::new_try(self.backing_allocator, DynamicArenaPage); - if (catch err = page) - { - allocator::free(self.backing_allocator, mem); - return err?; - } - page.memory = mem; - void* mem_start = mem::aligned_pointer(mem + DynamicArenaChunk.sizeof, alignment); - assert(mem_start + size < mem + page_size); - DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1; - chunk.size = size; - page.prev_arena = self.page; - page.total = page_size; - page.used = mem_start + size - page.memory; - self.page = page; - page.current_stack_ptr = mem_start; - return mem_start; -} - -<* - @require size > 0 `acquire expects size > 0` - @require !alignment || math::is_power_of_2(alignment) -*> -fn void*! DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic -{ - alignment = alignment_for_allocation(alignment); - DynamicArenaPage* page = self.page; - - void* ptr @noinit; - do SET_DONE: - { - if (!page && self.unused_page) - { - self.page = page = self.unused_page; - self.unused_page = page.prev_arena; - page.prev_arena = null; - } - if (!page) - { - ptr = self._alloc_new(size, alignment)!; - break SET_DONE; - } - void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment); - usz new_used = start - page.memory + size; - if ALLOCATE_NEW: (new_used > page.total) - { - if ((page = self.unused_page)) - { - start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment); - new_used = start + size - page.memory; - if (page.total >= new_used) - { - self.unused_page = page.prev_arena; - page.prev_arena = self.page; - self.page = page; - break ALLOCATE_NEW; - } - } - ptr = self._alloc_new(size, alignment)!; - break SET_DONE; - } - page.used = new_used; - assert(start + size == page.memory + page.used); - ptr = start; - DynamicArenaChunk* chunk = (DynamicArenaChunk*)ptr - 1; - chunk.size = size; - }; - if (init_type == ZERO) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT); - return ptr; -} diff --git a/lib7/std/core/allocators/heap_allocator.c3 b/lib7/std/core/allocators/heap_allocator.c3 deleted file mode 100644 index 3ea8e126a..000000000 --- a/lib7/std/core/allocators/heap_allocator.c3 +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright (c) 2021-2025 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::core::mem::allocator; -import std::math; - -struct SimpleHeapAllocator (Allocator) -{ - MemoryAllocFn alloc_fn; - Header* free_list; -} - -<* - @require allocator != null "An underlying memory provider must be given" - @require !self.free_list "The allocator may not be already initialized" -*> -fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator) -{ - self.alloc_fn = allocator; - self.free_list = null; -} - -fn void*! SimpleHeapAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic -{ - if (init_type == ZERO) - { - return alignment > 0 ? @aligned_alloc(self._calloc, size, alignment) : self._calloc(size); - } - return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : self._alloc(size); -} - -fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic -{ - return alignment > 0 - ? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment) - : self._realloc(old_pointer, size); -} - -fn void SimpleHeapAllocator.release(&self, void* old_pointer, bool aligned) @dynamic -{ - if (aligned) - { - @aligned_free(self._free, old_pointer)!!; - } - else - { - self._free(old_pointer); - } -} - -<* - @require old_pointer && bytes > 0 -*> -fn void*! SimpleHeapAllocator._realloc(&self, void* old_pointer, usz bytes) @local -{ - // Find the block header. - Header* block = (Header*)old_pointer - 1; - if (block.size >= bytes) return old_pointer; - void* new = self._alloc(bytes)!; - usz max_to_copy = math::min(block.size, bytes); - mem::copy(new, old_pointer, max_to_copy); - self._free(old_pointer); - return new; -} - -fn void*! SimpleHeapAllocator._calloc(&self, usz bytes) @local -{ - void* data = self._alloc(bytes)!; - mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT); - return data; -} - -fn void*! SimpleHeapAllocator._alloc(&self, usz bytes) @local -{ - usz aligned_bytes = mem::aligned_offset(bytes, mem::DEFAULT_MEM_ALIGNMENT); - if (!self.free_list) - { - self.add_block(aligned_bytes)!; - } - - Header* current = self.free_list; - Header* previous = current; - while (current) - { - switch - { - case current.size >= aligned_bytes && current.size <= aligned_bytes + Header.sizeof + 64: - if (current == previous) - { - self.free_list = current.next; - } - else - { - previous.next = current.next; - } - current.next = null; - return current + 1; - case current.size > aligned_bytes: - Header* unallocated = (Header*)((char*)current + aligned_bytes + Header.sizeof); - unallocated.size = current.size - aligned_bytes - Header.sizeof; - unallocated.next = current.next; - if (current == self.free_list) - { - self.free_list = unallocated; - } - else - { - previous.next = unallocated; - } - current.size = aligned_bytes; - current.next = null; - return current + 1; - default: - previous = current; - current = current.next; - } - } - self.add_block(aligned_bytes)!; - return self._alloc(aligned_bytes); -} - -fn void! SimpleHeapAllocator.add_block(&self, usz aligned_bytes) @local -{ - assert(mem::aligned_offset(aligned_bytes, mem::DEFAULT_MEM_ALIGNMENT) == aligned_bytes); - char[] result = self.alloc_fn(aligned_bytes + Header.sizeof)!; - Header* new_block = (Header*)result.ptr; - new_block.size = result.len - Header.sizeof; - new_block.next = null; - self._free(new_block + 1); -} - - -fn void SimpleHeapAllocator._free(&self, void* ptr) @local -{ - // Empty ptr -> do nothing. - if (!ptr) return; - - // Find the block header. - Header* block = (Header*)ptr - 1; - - // No free list? Then just return self. - if (!self.free_list) - { - self.free_list = block; - return; - } - - // Find where in the list it should be inserted. - Header* current = self.free_list; - Header* prev = current; - while (current) - { - if (block < current) - { - // Between prev and current - if (block > prev) break; - // Before current - if (current == prev) break; - } - prev = current; - current = prev.next; - } - if (current) - { - // Insert after the current block. - // Are the blocks adjacent? - if (current == (Header*)((char*)(block + 1) + block.size)) - { - // Merge - block.size += current.size + Header.sizeof; - block.next = current.next; - } - else - { - // Chain to current - block.next = current; - } - } - if (prev == current) - { - // Swap new start of free list - self.free_list = block; - } - else - { - // Prev adjacent? - if (block == (Header*)((char*)(prev + 1) + prev.size)) - { - prev.size += block.size + Header.sizeof; - prev.next = block.next; - } - else - { - // Link prev to block - prev.next = block; - } - } -} - -union Header @local -{ - struct - { - Header* next; - usz size; - } - usz align; -} diff --git a/lib7/std/core/allocators/libc_allocator.c3 b/lib7/std/core/allocators/libc_allocator.c3 deleted file mode 100644 index 377e9a223..000000000 --- a/lib7/std/core/allocators/libc_allocator.c3 +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) 2021-2024 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::core::mem::allocator @if(env::LIBC); -import std::io; -import libc; - -const LibcAllocator LIBC_ALLOCATOR = {}; -distinct LibcAllocator (Allocator, Printable) = uptr; - -fn String LibcAllocator.to_string(&self, Allocator allocator) @dynamic => "Libc allocator".copy(allocator); -fn usz! LibcAllocator.to_format(&self, Formatter *format) @dynamic => format.print("Libc allocator"); - -module std::core::mem::allocator @if(env::POSIX); -import std::os; -import libc; - - -fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic -{ - if (init_type == ZERO) - { - void* data @noinit; - if (alignment > mem::DEFAULT_MEM_ALIGNMENT) - { - if (posix::posix_memalign(&data, alignment, bytes)) return AllocationFailure.OUT_OF_MEMORY?; - mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT); - return data; - } - return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?; - } - else - { - void* data @noinit; - if (alignment > mem::DEFAULT_MEM_ALIGNMENT) - { - if (posix::posix_memalign(&data, alignment, bytes)) return AllocationFailure.OUT_OF_MEMORY?; - } - else - { - if (!(data = libc::malloc(bytes))) return AllocationFailure.OUT_OF_MEMORY?; - } - $if env::TESTING: - for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA; - $endif - return data; - } -} - -fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic -{ - if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?; - void* new_ptr; - if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return AllocationFailure.OUT_OF_MEMORY?; - - $switch - $case env::DARWIN: - usz old_usable_size = darwin::malloc_size(old_ptr); - $case env::LINUX: - usz old_usable_size = linux::malloc_usable_size(old_ptr); - $default: - usz old_usable_size = new_bytes; - $endswitch - - usz copy_size = new_bytes < old_usable_size ? new_bytes : old_usable_size; - mem::copy(new_ptr, old_ptr, copy_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT); - libc::free(old_ptr); - return new_ptr; -} - -fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic -{ - libc::free(old_ptr); -} - -module std::core::mem::allocator @if(env::WIN32); -import std::os::win32; -import libc; - -fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic -{ - if (init_type == ZERO) - { - if (alignment > 0) - { - return win32::_aligned_recalloc(null, 1, bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?; - } - return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?; - } - void* data = alignment > 0 ? win32::_aligned_malloc(bytes, alignment) : libc::malloc(bytes); - if (!data) return AllocationFailure.OUT_OF_MEMORY?; - $if env::TESTING: - for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA; - $endif - return data; -} - -fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic -{ - if (alignment) - { - return win32::_aligned_realloc(old_ptr, new_bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?; - } - return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?; -} - -fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic -{ - if (aligned) - { - win32::_aligned_free(old_ptr); - return; - } - libc::free(old_ptr); -} - -module std::core::mem::allocator @if(!env::WIN32 && !env::POSIX && env::LIBC); -import libc; - -fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic -{ - if (init_type == ZERO) - { - void* data = alignment ? @aligned_alloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment)!! : libc::calloc(bytes, 1); - return data ?: AllocationFailure.OUT_OF_MEMORY?; - } - else - { - void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment)!! : libc::malloc(bytes); - if (!data) return AllocationFailure.OUT_OF_MEMORY?; - $if env::TESTING: - for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA; - $endif - return data; - } -} - - -fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic -{ - if (alignment) - { - void* data = @aligned_realloc(fn void*(usz bytes) => libc::malloc(bytes), libc::free, old_ptr, new_bytes, alignment)!!; - return data ?: AllocationFailure.OUT_OF_MEMORY?; - } - return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?; -} - - -fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic -{ - if (aligned) - { - @aligned_free(libc::free, old_ptr)!!; - } - else - { - libc::free(old_ptr); - } -} diff --git a/lib7/std/core/allocators/on_stack_allocator.c3 b/lib7/std/core/allocators/on_stack_allocator.c3 deleted file mode 100644 index 28fe42bf1..000000000 --- a/lib7/std/core/allocators/on_stack_allocator.c3 +++ /dev/null @@ -1,150 +0,0 @@ -module std::core::mem::allocator; - -struct OnStackAllocator (Allocator) -{ - Allocator backing_allocator; - char[] data; - usz used; - OnStackAllocatorExtraChunk* chunk; -} - - -struct OnStackAllocatorExtraChunk @local -{ - bool is_aligned; - OnStackAllocatorExtraChunk* prev; - void* data; -} - -<* - Initialize a memory arena for use using the provided bytes. - - @param [&inout] allocator -*> -fn void OnStackAllocator.init(&self, char[] data, Allocator allocator) -{ - self.data = data; - self.backing_allocator = allocator; - self.used = 0; -} - -fn void OnStackAllocator.free(&self) -{ - OnStackAllocatorExtraChunk* chunk = self.chunk; - while (chunk) - { - if (chunk.is_aligned) - { - allocator::free_aligned(self.backing_allocator, chunk.data); - } - else - { - allocator::free(self.backing_allocator, chunk.data); - } - void* old = chunk; - chunk = chunk.prev; - allocator::free(self.backing_allocator, old); - } - self.chunk = null; - self.used = 0; -} - -struct OnStackAllocatorHeader -{ - usz size; - char[?] data; -} - -<* - @require old_pointer != null -*> -fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned) @dynamic -{ - if (allocation_in_stack_mem(self, old_pointer)) return; - on_stack_allocator_remove_chunk(self, old_pointer); - self.backing_allocator.release(old_pointer, aligned); -} - -fn bool allocation_in_stack_mem(OnStackAllocator* a, void* ptr) @local -{ - return ptr >= a.data.ptr && ptr <= &a.data[^1]; -} - -fn void on_stack_allocator_remove_chunk(OnStackAllocator* a, void* ptr) @local -{ - OnStackAllocatorExtraChunk* chunk = a.chunk; - OnStackAllocatorExtraChunk** addr = &a.chunk; - while (chunk) - { - if (chunk.data == ptr) - { - *addr = chunk.prev; - allocator::free(a.backing_allocator, chunk); - return; - } - addr = &chunk.prev; - chunk = *addr; - } - unreachable("Missing chunk"); -} - -fn OnStackAllocatorExtraChunk* on_stack_allocator_find_chunk(OnStackAllocator* a, void* ptr) @local -{ - OnStackAllocatorExtraChunk* chunk = a.chunk; - while (chunk) - { - if (chunk.data == ptr) return chunk; - chunk = chunk.prev; - } - return null; -} - -<* - @require size > 0 - @require old_pointer != null - @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big` -*> -fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic -{ - if (!allocation_in_stack_mem(self, old_pointer)) - { - OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(self, old_pointer); - assert(chunk, "Tried to realloc pointer not belonging to the allocator"); - return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment)!; - } - - OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof; - usz old_size = header.size; - void* mem = self.acquire(size, NO_ZERO, alignment)!; - mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT); - return mem; -} - -<* - @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big` - @require size > 0 -*> -fn void*! OnStackAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic -{ - bool aligned = alignment > 0; - alignment = alignment_for_allocation(alignment); - usz total_len = self.data.len; - void* start_mem = self.data.ptr; - void* unaligned_pointer_to_offset = start_mem + self.used + OnStackAllocatorHeader.sizeof ; - void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment); - usz end = (usz)(mem - self.data.ptr) + size; - Allocator backing_allocator = self.backing_allocator; - - if (end > total_len) - { - OnStackAllocatorExtraChunk* chunk = allocator::alloc_try(backing_allocator, OnStackAllocatorExtraChunk)!; - defer catch allocator::free(backing_allocator, chunk); - defer try self.chunk = chunk; - *chunk = { .prev = self.chunk, .is_aligned = aligned }; - return chunk.data = backing_allocator.acquire(size, init_type, aligned ? alignment : 0)!; - } - self.used = end; - OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof; - header.size = size; - return mem; -} diff --git a/lib7/std/core/allocators/temp_allocator.c3 b/lib7/std/core/allocators/temp_allocator.c3 deleted file mode 100644 index 37dd3ec3a..000000000 --- a/lib7/std/core/allocators/temp_allocator.c3 +++ /dev/null @@ -1,230 +0,0 @@ -module std::core::mem::allocator; -import std::io, std::math; - -struct TempAllocatorChunk @local -{ - usz size; - char[?] data; -} - -struct TempAllocator (Allocator) -{ - Allocator backing_allocator; - TempAllocatorPage* last_page; - usz used; - usz capacity; - char[?] data; -} - -const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u; - - -struct TempAllocatorPage -{ - TempAllocatorPage* prev_page; - void* start; - usz mark; - usz size; - usz ident; - char[?] data; -} - -macro usz TempAllocatorPage.pagesize(&self) => self.size & ~PAGE_IS_ALIGNED; -macro bool TempAllocatorPage.is_aligned(&self) => self.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED; - -<* - @require size >= 16 -*> -fn TempAllocator*! new_temp_allocator(usz size, Allocator allocator) -{ - TempAllocator* temp = allocator::alloc_with_padding(allocator, TempAllocator, size)!; - temp.last_page = null; - temp.backing_allocator = allocator; - temp.used = 0; - temp.capacity = size; - return temp; -} - -fn void TempAllocator.destroy(&self) -{ - self.reset(0); - if (self.last_page) (void)self._free_page(self.last_page); - allocator::free(self.backing_allocator, self); -} - -fn usz TempAllocator.mark(&self) @dynamic => self.used; - -fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic -{ - usz old_size = *(usz*)(old_pointer - DEFAULT_SIZE_PREFIX); - if (old_pointer + old_size == &self.data[self.used]) - { - self.used -= old_size; - asan::poison_memory_region(&self.data[self.used], old_size); - } -} -fn void TempAllocator.reset(&self, usz mark) @dynamic -{ - TempAllocatorPage *last_page = self.last_page; - while (last_page && last_page.mark > mark) - { - self.used = last_page.mark; - TempAllocatorPage *to_free = last_page; - last_page = last_page.prev_page; - self._free_page(to_free)!!; - } - self.last_page = last_page; - $if env::COMPILER_SAFE_MODE || env::ADDRESS_SANITIZER: - if (!last_page) - { - usz cleaned = self.used - mark; - if (cleaned > 0) - { - $if env::COMPILER_SAFE_MODE && !env::ADDRESS_SANITIZER: - self.data[mark : cleaned] = 0xAA; - $endif - asan::poison_memory_region(&self.data[mark], cleaned); - } - } - $endif - self.used = mark; -} - -fn void! TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local -{ - void* mem = page.start; - return self.backing_allocator.release(mem, page.is_aligned()); -} - -fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment) @inline @local -{ - // Then the actual start pointer: - void* real_pointer = page.start; - - // Walk backwards to find the pointer to this page. - TempAllocatorPage **pointer_to_prev = &self.last_page; - // Remove the page from the list - while (*pointer_to_prev != page) - { - pointer_to_prev = &((*pointer_to_prev).prev_page); - } - *pointer_to_prev = page.prev_page; - usz page_size = page.pagesize(); - // Clear on size > original size. - void* data = self.acquire(size, NO_ZERO, alignment)!; - mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT); - self.backing_allocator.release(real_pointer, page.is_aligned()); - return data; -} - -fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic -{ - TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof; - if (chunk.size == (usz)-1) - { - assert(self.last_page, "Realloc of non temp pointer"); - // First grab the page - TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof; - return self._realloc_page(page, size, alignment); - } - - TempAllocatorChunk* data = self.acquire(size, NO_ZERO, alignment)!; - mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT); - - return data; -} - -<* - @require size > 0 - @require !alignment || math::is_power_of_2(alignment) - @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big` -*> -fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic -{ - alignment = alignment_for_allocation(alignment); - void* start_mem = &self.data; - void* starting_ptr = start_mem + self.used; - void* aligned_header_start = mem::aligned_pointer(starting_ptr, TempAllocatorChunk.alignof); - void* mem = aligned_header_start + TempAllocatorChunk.sizeof; - if (alignment > TempAllocatorChunk.alignof) - { - mem = mem::aligned_pointer(mem, alignment); - } - usz new_usage = (usz)(mem - start_mem) + size; - - // Arena allocation, simple! - if (new_usage <= self.capacity) - { - asan::unpoison_memory_region(starting_ptr, new_usage - self.used); - TempAllocatorChunk* chunk_start = mem - TempAllocatorChunk.sizeof; - chunk_start.size = size; - self.used = new_usage; - if (init_type == ZERO) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT); - return mem; - } - - // Fallback to backing allocator - TempAllocatorPage* page; - - // We have something we need to align. - if (alignment > mem::DEFAULT_MEM_ALIGNMENT) - { - // This is actually simpler, since it will create the offset for us. - usz total_alloc_size = mem::aligned_offset(TempAllocatorPage.sizeof + size, alignment); - if (init_type == ZERO) - { - mem = allocator::calloc_aligned(self.backing_allocator, total_alloc_size, alignment)!; - } - else - { - mem = allocator::malloc_aligned(self.backing_allocator, total_alloc_size, alignment)!; - } - void* start = mem; - mem += mem::aligned_offset(TempAllocatorPage.sizeof, alignment); - page = (TempAllocatorPage*)mem - 1; - page.start = start; - page.size = size | PAGE_IS_ALIGNED; - } - else - { - // Here we might need to pad - usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT); - usz total_alloc_size = padded_header_size + size; - void* alloc = self.backing_allocator.acquire(total_alloc_size, init_type, 0)!; - - // Find the page. - page = alloc + padded_header_size - TempAllocatorPage.sizeof; - assert(mem::ptr_is_aligned(page, TempAllocator.alignof)); - assert(mem::ptr_is_aligned(&page.data[0], mem::DEFAULT_MEM_ALIGNMENT)); - page.start = alloc; - page.size = size; - } - - // Mark it as a page - page.ident = ~(usz)0; - // Store when it was created - page.mark = ++self.used; - // Hook up the page. - page.prev_page = self.last_page; - self.last_page = page; - return &page.data[0]; -} - -fn void! TempAllocator.print_pages(&self, File* f) -{ - TempAllocatorPage *last_page = self.last_page; - if (!last_page) - { - io::fprintf(f, "No pages.\n")!; - return; - } - io::fprintf(f, "---Pages----\n")!; - uint index = 0; - while (last_page) - { - bool is_not_aligned = !(last_page.size & (1u64 << 63)); - io::fprintf(f, "%d. Alloc: %d %d at %p%s\n", ++index, - last_page.size & ~(1u64 << 63), last_page.mark, &last_page.data[0], is_not_aligned ? "" : " [aligned]")!; - last_page = last_page.prev_page; - } -} diff --git a/lib7/std/core/allocators/tracking_allocator.c3 b/lib7/std/core/allocators/tracking_allocator.c3 deleted file mode 100644 index 9f1366496..000000000 --- a/lib7/std/core/allocators/tracking_allocator.c3 +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2021-2024 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::core::mem::allocator; -import std::collections, std::io, std::os::backtrace; - -const MAX_BACKTRACE = 16; -struct Allocation -{ - void* ptr; - usz size; - void*[MAX_BACKTRACE] backtrace; -} - -def AllocMap = HashMap { uptr, Allocation }; - -// A simple tracking allocator. -// It tracks allocations using a hash map but -// is not compatible with allocators that uses mark() -struct TrackingAllocator (Allocator) -{ - Allocator inner_allocator; - AllocMap map; - usz mem_total; - usz allocs_total; -} - -<* - Initialize a tracking allocator to wrap (and track) another allocator. - - @param [&inout] allocator "The allocator to track" -*> -fn void TrackingAllocator.init(&self, Allocator allocator) -{ - *self = { .inner_allocator = allocator }; - self.map.init(allocator); -} - -<* - Free this tracking allocator. -*> -fn void TrackingAllocator.free(&self) -{ - self.map.free(); - *self = {}; -} - -<* - @return "the total allocated memory not yet freed." -*> -fn usz TrackingAllocator.allocated(&self) => @pool() -{ - usz allocated = 0; - foreach (&allocation : self.map.tvalues()) allocated += allocation.size; - return allocated; -} - -<* - @return "the total memory allocated (freed or not)." -*> -fn usz TrackingAllocator.total_allocated(&self) => self.mem_total; - -<* - @return "the total number of allocations (freed or not)." -*> -fn usz TrackingAllocator.total_allocation_count(&self) => self.allocs_total; - -fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator allocator) -{ - return self.map.tvalues(); -} - -<* - @return "the number of non-freed allocations." -*> -fn usz TrackingAllocator.allocation_count(&self) => self.map.count; - -fn void*! TrackingAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic -{ - void* data = self.inner_allocator.acquire(size, init_type, alignment)!; - self.allocs_total++; - void*[MAX_BACKTRACE] bt; - backtrace::capture_current(&bt); - self.map.set((uptr)data, { data, size, bt }); - self.mem_total += size; - return data; -} - -fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic -{ - void* data = self.inner_allocator.resize(old_pointer, size, alignment)!; - self.map.remove((uptr)old_pointer); - void*[MAX_BACKTRACE] bt; - backtrace::capture_current(&bt); - self.map.set((uptr)data, { data, size, bt }); - self.mem_total += size; - self.allocs_total++; - return data; -} - -fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dynamic -{ - if (catch self.map.remove((uptr)old_pointer)) - { - unreachable("Attempt to release untracked pointer %p, this is likely a bug.", old_pointer); - } - self.inner_allocator.release(old_pointer, is_aligned); -} - -fn void TrackingAllocator.clear(&self) -{ - self.map.clear(); -} - -fn bool TrackingAllocator.has_leaks(&self) -{ - return self.map.len() > 0; -} - -fn void TrackingAllocator.print_report(&self) => self.fprint_report(io::stdout())!!; - - -fn void! TrackingAllocator.fprint_report(&self, OutStream out) => @pool() -{ - usz total = 0; - usz entries = 0; - bool leaks = false; - - Allocation[] allocs = self.map.tvalues(); - if (allocs.len) - { - if (!allocs[0].backtrace[0]) - { - io::fprintn(out, "======== Memory Report ========")!; - io::fprintn(out, "Size in bytes Address")!; - foreach (i, &allocation : allocs) - { - entries++; - total += allocation.size; - io::fprintfn(out, "%13s %p", allocation.size, allocation.ptr)!; - } - io::fprintn(out, "===============================")!; - - } - else - { - io::fprintn(out, "================================== Memory Report ==================================")!; - io::fprintn(out, "Size in bytes Address Function ")!; - foreach (i, &allocation : allocs) - { - entries++; - total += allocation.size; - BacktraceList backtraces = {}; - Backtrace trace = backtrace::BACKTRACE_UNKNOWN; - if (allocation.backtrace[3]) - { - 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, - allocation.ptr, trace.function.len ? trace.function : "???", - trace.line ? trace.line : 0)!; - } - io::fprintn(out, "===================================================================================")!; - } - } - else - { - io::fprintn(out, "* NO ALLOCATIONS FOUND *")!; - } - io::fprintfn(out, "- Total currently allocated memory: %d", total)!; - io::fprintfn(out, "- Total current allocations: %d", entries)!; - io::fprintfn(out, "- Total allocations (freed and retained): %d", self.allocs_total)!; - io::fprintfn(out, "- Total allocated memory (freed and retained): %d", self.mem_total)!; - if (leaks) - { - io::fprintn(out)!; - io::fprintn(out, "Full leak report:")!; - foreach (i, &allocation : allocs) - { - if (!allocation.backtrace[3]) - { - io::fprintfn(out, "Allocation %d (%d bytes) - no backtrace available.", i + 1, allocation.size)!; - continue; - } - BacktraceList backtraces = {}; - usz end = MAX_BACKTRACE; - foreach (j, val : allocation.backtrace) - { - if (!val) - { - end = j; - break; - } - } - 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) - { - if (trace.has_file()) - { - io::fprintfn(out, " %s (in %s:%d)", trace.function, trace.file, trace.line); - continue; - } - if (trace.is_unknown()) - { - io::fprintfn(out, " ??? (in unknown)"); - continue; - } - io::fprintfn(out, " %s (source unavailable)", trace.function); - } - } - } -} \ No newline at end of file diff --git a/lib7/std/core/array.c3 b/lib7/std/core/array.c3 deleted file mode 100644 index 6dd9cdc67..000000000 --- a/lib7/std/core/array.c3 +++ /dev/null @@ -1,177 +0,0 @@ -module std::core::array; -import std::core::array::slice; - -<* - @param [in] array - @param [in] element - @return "the first index of the element" - @return! SearchResult.MISSING -*> -macro index_of(array, element) -{ - foreach (i, &e : array) - { - if (*e == element) return i; - } - return SearchResult.MISSING?; -} - -<* - @require @typekind(array_ptr) == POINTER - @require @typekind(*array_ptr) == VECTOR || @typekind(*array_ptr) == ARRAY - @require @typekind((*array_ptr)[0]) == VECTOR || @typekind((*array_ptr)[0]) == ARRAY -*> -macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0) -{ - if (xlen < 1) xlen = $typeof((*array_ptr)[0]).len + xlen; - if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen; - var $ElementType = $typeof((*array_ptr)[0][0]); - return Slice2d{$ElementType} { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen }; -} - - -<* - @param [in] array - @param [in] element - @return "the last index of the element" - @return! SearchResult.MISSING -*> -macro rindex_of(array, element) -{ - foreach_r (i, &e : array) - { - if (*e == element) return i; - } - return SearchResult.MISSING?; -} - -<* - Concatenate two arrays or slices, returning a slice containing the concatenation of them. - - @param [in] arr1 - @param [in] arr2 - @param [&inout] allocator "The allocator to use, default is the heap allocator" - @require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY - @require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY - @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type" - @ensure result.len == arr1.len + arr2.len -*> -macro concat(Allocator allocator, arr1, arr2) @nodiscard -{ - var $Type = $typeof(arr1[0]); - $Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len); - if (arr1.len > 0) - { - mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $Type.alignof, $Type.alignof); - } - if (arr2.len > 0) - { - mem::copy(&result[arr1.len], &arr2[0], arr2.len * $Type.sizeof, $Type.alignof, $Type.alignof); - } - return result; -} -<* - Concatenate two arrays or slices, returning a slice containing the concatenation of them, - allocated using the temp allocator. - - @param [in] arr1 - @param [in] arr2 - @require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY - @require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY - @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type" - @ensure return.len == arr1.len + arr2.len -*> -macro tconcat(arr1, arr2) @nodiscard => concat(allocator::temp(), arr1, arr2); - -module std::core::array::slice{Type}; - -struct Slice2d -{ - Type* ptr; - usz inner_len; - usz ystart; - usz ylen; - usz xstart; - usz xlen; -} - -fn usz Slice2d.len(&self) @operator(len) -{ - return self.ylen; -} - -fn usz Slice2d.count(&self) -{ - return self.ylen * self.xlen; -} - -macro void Slice2d.@each(&self; @body(usz[<2>], Type)) -{ - foreach (y, line : *self) - { - foreach (x, val : line) - { - @body({ x, y }, val); - } - } -} - -macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*)) -{ - foreach (y, line : *self) - { - foreach (x, &val : line) - { - @body({ x, y }, val); - } - } -} - -<* - @require idy >= 0 && idy < self.ylen -*> -macro Type[] Slice2d.get_row(self, usz idy) @operator([]) -{ - return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen]; -} - -macro Type Slice2d.get_coord(self, usz[<2>] coord) -{ - return *self.get_coord_ref(coord); -} - -macro Type Slice2d.get_xy(self, x, y) -{ - return *self.get_xy_ref(x, y); -} - -macro Type* Slice2d.get_xy_ref(self, x, y) -{ - return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x; -} - -macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord) -{ - return self.get_xy_ref(coord.x, coord.y); -} - -macro void Slice2d.set_coord(self, usz[<2>] coord, Type value) -{ - *self.get_coord_ref(coord) = value; -} - -macro void Slice2d.set_xy(self, x, y, Type value) -{ - *self.get_xy_ref(x, y) = value; -} - -<* - @require y >= 0 && y < self.ylen - @require x >= 0 && x < self.xlen -*> -fn Slice2d Slice2d.slice(&self, isz x = 0, isz xlen = 0, isz y = 0, isz ylen = 0) -{ - if (xlen < 1) xlen = self.xlen + xlen; - if (ylen < 1) ylen = self.ylen + ylen; - return { self.ptr, self.inner_len, y + self.ystart, ylen, x + self.xstart, xlen }; -} diff --git a/lib7/std/core/bitorder.c3 b/lib7/std/core/bitorder.c3 deleted file mode 100644 index 0b49cee2d..000000000 --- a/lib7/std/core/bitorder.c3 +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) 2023 Christoffer Lerno and contributors. 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::core::bitorder; - -// This module contains types of different endianness. -// *BE types represent big-endian types -// *LE types represent little-endian types. - -bitstruct ShortBE : short @bigendian -{ - short val : 0..15; -} - -bitstruct UShortBE : ushort @bigendian -{ - ushort val : 0..15; -} - -bitstruct IntBE : int @bigendian -{ - int val : 0..31; -} - -bitstruct UIntBE : int @bigendian -{ - uint val : 0..31; -} - -bitstruct LongBE : long @bigendian -{ - long val : 0..63; -} - -bitstruct ULongBE : ulong @bigendian -{ - ulong val : 0..63; -} - -bitstruct Int128BE : int128 @bigendian -{ - int128 val : 0..127; -} - -bitstruct UInt128BE : uint128 @bigendian -{ - uint128 val : 0..127; -} - -bitstruct ShortLE : short @littleendian -{ - short val : 0..15; -} - -bitstruct UShortLE : ushort @littleendian -{ - ushort val : 0..15; -} - -bitstruct IntLE : int @littleendian -{ - int val : 0..31; -} - -bitstruct UIntLE : int @littleendian -{ - uint val : 0..31; -} - -bitstruct LongLE : long @littleendian -{ - long val : 0..63; -} - -bitstruct ULongLE : ulong @littleendian -{ - ulong val : 0..63; -} - -bitstruct Int128LE : int128 @littleendian -{ - int128 val : 0..127; -} - -bitstruct UInt128LE : uint128 @littleendian -{ - uint128 val : 0..127; -} - -<* - @require is_array_or_slice_of_char(bytes) "argument must be an array, a pointer to an array or a slice of char" - @require is_bitorder($Type) "type must be a bitorder integer" -*> -macro read(bytes, $Type) -{ - char[] s; - $switch (@typekind(bytes)) - $case POINTER: - s = (*bytes)[:$Type.sizeof]; - $default: - s = bytes[:$Type.sizeof]; - $endswitch - return bitcast(*(char[$Type.sizeof]*)s.ptr, $Type).val; -} - -<* - @require is_arrayptr_or_slice_of_char(bytes) "argument must be a pointer to an array or a slice of char" - @require is_bitorder($Type) "type must be a bitorder integer" -*> -macro write(x, bytes, $Type) -{ - char[] s; - $switch (@typekind(bytes)) - $case POINTER: - s = (*bytes)[:$Type.sizeof]; - $default: - s = bytes[:$Type.sizeof]; - $endswitch - *($typeof(x)*)s.ptr = bitcast(x, $Type).val; -} - -macro is_bitorder($Type) -{ - $switch ($Type) - $case UShortLE: - $case ShortLE: - $case UIntLE: - $case IntLE: - $case ULongLE: - $case LongLE: - $case UInt128LE: - $case Int128LE: - $case UShortBE: - $case ShortBE: - $case UIntBE: - $case IntBE: - $case ULongBE: - $case LongBE: - $case UInt128BE: - $case Int128BE: - return true; - $default: - return false; - $endswitch -} - -macro bool is_array_or_slice_of_char(bytes) -{ - $switch (@typekind(bytes)) - $case POINTER: - var $Inner = $typefrom($typeof(bytes).inner); - $if $Inner.kindof == ARRAY: - var $Inner2 = $typefrom($Inner.inner); - return $Inner2.typeid == char.typeid; - $endif - $case ARRAY: - $case SLICE: - var $Inner = $typefrom($typeof(bytes).inner); - return $Inner.typeid == char.typeid; - $default: - return false; - $endswitch -} - -macro bool is_arrayptr_or_slice_of_char(bytes) -{ - $switch (@typekind(bytes)) - $case POINTER: - var $Inner = $typefrom($typeof(bytes).inner); - $if $Inner.kindof == ARRAY: - var $Inner2 = $typefrom($Inner.inner); - return $Inner2.typeid == char.typeid; - $endif - $case SLICE: - var $Inner = $typefrom($typeof(bytes).inner); - return $Inner.typeid == char.typeid; - $default: - return false; - $endswitch -} \ No newline at end of file diff --git a/lib7/std/core/builtin.c3 b/lib7/std/core/builtin.c3 deleted file mode 100644 index 4e01a6b7e..000000000 --- a/lib7/std/core/builtin.c3 +++ /dev/null @@ -1,779 +0,0 @@ -// Copyright (c) 2021-2024 Christoffer Lerno and contributors. 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::core::builtin; -import libc, std::hash, std::io, std::os::backtrace; - -<* - EMPTY_MACRO_SLOT is a value used for implementing optional arguments for macros in an efficient - way. It relies on the fact that distinct types are not implicitly convertable. - - You can use `@is_empty_macro_slot()` and `@is_valid_macro_slot()` to figure out whether - the argument has been used or not. - - An example: - -```c3 -macro foo(a, #b = EMPTY_MACRO_SLOT) -{ - $if @is_valid_macro_slot(#b): - return invoke_foo2(a, #b); - $else - return invoke_foo1(a); - $endif -} -*> -const EmptySlot EMPTY_MACRO_SLOT @builtin = null; - -distinct EmptySlot = void*; -macro @is_empty_macro_slot(#arg) @const @builtin => @typeis(#arg, EmptySlot); -macro @is_valid_macro_slot(#arg) @const @builtin => !@typeis(#arg, EmptySlot); - -/* - Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds. -*/ -fault IteratorResult { NO_MORE_ELEMENT } - -/* - Use `SearchResult` when trying to return a value from some collection but the element is missing. -*/ -fault SearchResult { MISSING } - -/* - Use `CastResult` when an attempt at conversion fails. -*/ -fault CastResult { TYPE_MISMATCH } - - -def VoidFn = fn void(); - -<* - Stores a variable on the stack, then restores it at the end of the - macro scope. - - @param #variable `the variable to store and restore` - @require values::@is_lvalue(#variable) -*> -macro void @scope(#variable; @body) @builtin -{ - var temp = #variable; - defer #variable = temp; - @body(); -} - -<* - Swap two variables - @require $defined(#a = #b, #b = #a) `The values must be mutually assignable` -*> -macro void @swap(#a, #b) @builtin -{ - var temp = #a; - #a = #b; - #b = temp; -} - -<* - Convert an `any` type to a type, returning an failure if there is a type mismatch. - - @param v `the any to convert to the given type.` - @param $Type `the type to convert to` - @return `The any.ptr converted to its type.` - @ensure @typeis(return, $Type*) - @return! CastResult.TYPE_MISMATCH -*> -macro anycast(any v, $Type) @builtin -{ - if (v.type != $Type.typeid) return CastResult.TYPE_MISMATCH?; - return ($Type*)v.ptr; -} - -fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIVE_STACKTRACE) => @pool() -{ - void*[256] buffer; - void*[] backtraces = backtrace::capture_current(&buffer); - backtraces_to_ignore++; - BacktraceList! backtrace = backtrace::symbolize_backtrace(tmem(), backtraces); - if (catch backtrace) return false; - if (backtrace.len() <= backtraces_to_ignore) return false; - io::eprint("\nERROR: '"); - io::eprint(message); - io::eprintn("'"); - foreach (i, &trace : backtrace) - { - if (i < backtraces_to_ignore) continue; - String inline_suffix = trace.is_inline ? " [inline]" : ""; - if (trace.is_unknown()) - { - io::eprintfn(" in ???%s", inline_suffix); - continue; - } - if (trace.has_file()) - { - io::eprintfn(" in %s (%s:%d) [%s]%s", trace.function, trace.file, trace.line, trace.object_file, inline_suffix); - continue; - } - io::eprintfn(" in %s (source unavailable) [%s]%s", trace.function, trace.object_file, inline_suffix); - } - return true; - -} - -fn void default_panic(String message, String file, String function, uint line) @if(env::NATIVE_STACKTRACE) -{ - $if $defined(io::stderr): - if (!print_backtrace(message, 2)) - { - io::eprintfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line); - } - $endif - $$trap(); - -} - -macro void abort(String string = "Unrecoverable error reached", ...) @builtin @noreturn -{ - panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat); - $$trap(); -} - -bool in_panic @local = false; - -fn void default_panic(String message, String file, String function, uint line) @if(!env::NATIVE_STACKTRACE) -{ - if (in_panic) - { - io::eprintn("Panic inside of panic."); - return; - } - in_panic = true; - $if $defined(io::stderr): - io::eprint("\nERROR: '"); - io::eprint(message); - io::eprintfn("', in %s (%s:%d)", function, file, line); - $endif - in_panic = false; - $$trap(); -} - -def PanicFn = fn void(String message, String file, String function, uint line); - -PanicFn panic = &default_panic; - -fn void panicf(String fmt, String file, String function, uint line, args...) -{ - if (in_panic) - { - io::eprint("Panic inside of panic: "); - io::eprintn(fmt); - return; - } - in_panic = true; - @stack_mem(512; Allocator allocator) - { - DString s; - s.init(allocator); - s.appendf(fmt, ...args); - in_panic = false; - panic(s.str_view(), file, function, line); - }; -} - -<* - Marks the path as unreachable. This will panic in safe mode, and in fast will simply be assumed - never happens. - @param [in] string "The panic message or format string" -*> -macro void unreachable(String string = "Unreachable statement reached.", ...) @builtin @noreturn -{ - $if env::COMPILER_SAFE_MODE: - panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat); - $endif; - $$unreachable(); -} - -<* - Marks the path as unsupported, this is similar to unreachable. - @param [in] string "The error message" -*> -macro void unsupported(String string = "Unsupported function invoked") @builtin @noreturn -{ - panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat); - $$unreachable(); -} - -<* - Unconditionally break into an attached debugger when reached. -*> -macro void breakpoint() @builtin -{ - $$breakpoint(); -} - -macro any_make(void* ptr, typeid type) @builtin -{ - return $$any_make(ptr, type); -} - -macro any.retype_to(&self, typeid type) -{ - return $$any_make(self.ptr, type); -} - -macro any.as_inner(&self) -{ - return $$any_make(self.ptr, self.type.inner); -} - -<* - @param expr "the expression to cast" - @param $Type "the type to cast to" - - @require $sizeof(expr) == $Type.sizeof "Cannot bitcast between types of different size." - @ensure @typeis(return, $Type) -*> -macro bitcast(expr, $Type) @builtin -{ - $if $Type.alignof <= $alignof(expr): - return *($Type*)&expr; - $else - $Type x @noinit; - $$memcpy(&x, &expr, $sizeof(expr), false, $Type.alignof, $alignof(expr)); - return x; - $endif -} - -<* - @param $Type `The type of the enum` - @param [in] enum_name `The name of the enum to search for` - @require $Type.kindof == ENUM `Only enums may be used` - @ensure @typeis(return, $Type) - @return! SearchResult.MISSING -*> -macro enum_by_name($Type, String enum_name) @builtin -{ - typeid x = $Type.typeid; - foreach (i, name : x.names) - { - if (name == enum_name) return $Type.from_ordinal(i); - } - return SearchResult.MISSING?; -} - -<* - @param $Type `The type of the enum` - @require $Type.kindof == ENUM `Only enums may be used` - @require $defined($Type.#value) `Expected '#value' to match an enum associated value` - @require $assignable(value, $typeof(($Type){}.#value)) `Expected the value to match the type of the associated value` - @ensure @typeis(return, $Type) - @return! SearchResult.MISSING -*> -macro @enum_from_value($Type, #value, value) @builtin -{ - usz elements = $Type.elements; - foreach (e : $Type.values) - { - if (e.#value == value) return e; - } - return SearchResult.MISSING?; -} - -<* - Mark an expression as likely to be true - - @param #value "expression to be marked likely" - @param $probability "in the range 0 - 1" - @require $probability >= 0 && $probability <= 1.0 -*> -macro bool @likely(bool #value, $probability = 1.0) @builtin -{ - $switch - $case env::BUILTIN_EXPECT_IS_DISABLED: - return #value; - $case $probability == 1.0: - return $$expect(#value, true); - $default: - return $$expect_with_probability(#value, true, $probability); - $endswitch -} - -<* - Mark an expression as unlikely to be true - - @param #value "expression to be marked unlikely" - @param $probability "in the range 0 - 1" - @require $probability >= 0 && $probability <= 1.0 -*> -macro bool @unlikely(bool #value, $probability = 1.0) @builtin -{ - $switch - $case env::BUILTIN_EXPECT_IS_DISABLED: - return #value; - $case $probability == 1.0: - return $$expect(#value, false); - $default: - return $$expect_with_probability(#value, false, $probability); - $endswitch -} - -<* - @require values::@is_int(#value) || values::@is_bool(#value) - @require $assignable(expected, $typeof(#value)) - @require $probability >= 0 && $probability <= 1.0 -*> -macro @expect(#value, expected, $probability = 1.0) @builtin -{ - $switch - $case env::BUILTIN_EXPECT_IS_DISABLED: - return #value == expected; - $case $probability == 1.0: - return $$expect(#value, ($typeof(#value))expected); - $default: - return $$expect_with_probability(#value, expected, $probability); - $endswitch -} - -<* - Locality for prefetch, levels 0 - 3, corresponding - to "extremely local" to "no locality" -*> -enum PrefetchLocality -{ - NO_LOCALITY, - FAR, - NEAR, - VERY_NEAR, -} - -<* - Prefetch a pointer. - - @param [in] ptr `Pointer to prefetch` - @param $locality `Locality ranging from none to extremely local` - @param $write `Prefetch for write, otherwise prefetch for read.` -*> -macro @prefetch(void* ptr, PrefetchLocality $locality = VERY_NEAR, bool $write = false) @builtin -{ - $if !env::BUILTIN_PREFETCH_IS_DISABLED: - $$prefetch(ptr, $write ? 1 : 0, $locality.ordinal); - $endif -} - -macro swizzle(v, ...) @builtin -{ - return $$swizzle(v, $vasplat); -} - -macro swizzle2(v, v2, ...) @builtin -{ - return $$swizzle2(v, v2, $vasplat); -} -<* - Return the excuse in the Optional if it is Empty, otherwise - return a null fault. - - @require @typekind(#expr) == OPTIONAL : `@catch expects an Optional value` -*> -macro anyfault @catch(#expr) @builtin -{ - if (catch f = #expr) return f; - return {}; -} - -<* - Check if an Optional expression holds a value or is empty, returning true - if it has a value. - - @require @typekind(#expr) == OPTIONAL : `@ok expects an Optional value` -*> -macro bool @ok(#expr) @builtin -{ - if (catch #expr) return false; - return true; -} - -<* - @require $defined(&#value, (char*)&#value) "This must be a value that can be viewed as a char array" - *> -macro char[] @as_char_view(#value) @builtin -{ - return ((char*)&#value)[:$sizeof(#value)]; -} - -macro isz @str_find(String $string, String $needle) @builtin => $$str_find($string, $needle); -macro String @str_upper(String $str) @builtin => $$str_upper($str); -macro String @str_lower(String $str) @builtin => $$str_lower($str); -macro uint @str_hash(String $str) @builtin => $$str_hash($str); - -macro @generic_hash_core(h, value) -{ - h ^= (uint)value; // insert lowest 32 bits - h *= 0x96f59e5b; // diffuse them up - h ^= h >> 16; // diffuse them down - return h; -} - -macro @generic_hash(value) -{ - uint h = @generic_hash_core((uint)0x3efd4391, value); - $for (var $cnt = 4; $cnt < $sizeof(value); $cnt += 4) - value >>= 32; // reduce value - h = @generic_hash_core(h, value); - $endfor - return h; -} - -macro uint int.hash(int i) => @generic_hash(i); -macro uint uint.hash(uint i) => @generic_hash(i); -macro uint short.hash(short s) => @generic_hash(s); -macro uint ushort.hash(ushort s) => @generic_hash(s); -macro uint char.hash(char c) => @generic_hash(c); -macro uint ichar.hash(ichar c) => @generic_hash(c); -macro uint long.hash(long i) => @generic_hash(i); -macro uint ulong.hash(ulong i) => @generic_hash(i); -macro uint int128.hash(int128 i) => @generic_hash(i); -macro uint uint128.hash(uint128 i) => @generic_hash(i); -macro uint bool.hash(bool b) => @generic_hash(b); -macro uint typeid.hash(typeid t) => @generic_hash(((ulong)(uptr)t)); -macro uint String.hash(String c) => (uint)fnv32a::encode(c); -macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c); -macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr)); - - -const MAX_FRAMEADDRESS = 128; -<* - @require n >= 0 -*> -macro void* get_frameaddress(int n) -{ - if (n > MAX_FRAMEADDRESS) return null; - switch (n) - { - case 0: return $$frameaddress(0); - case 1: return $$frameaddress(1); - case 2: return $$frameaddress(2); - case 3: return $$frameaddress(3); - case 4: return $$frameaddress(4); - case 5: return $$frameaddress(5); - case 6: return $$frameaddress(6); - case 7: return $$frameaddress(7); - case 8: return $$frameaddress(8); - case 9: return $$frameaddress(9); - case 10: return $$frameaddress(10); - case 11: return $$frameaddress(11); - case 12: return $$frameaddress(12); - case 13: return $$frameaddress(13); - case 14: return $$frameaddress(14); - case 15: return $$frameaddress(15); - case 16: return $$frameaddress(16); - case 17: return $$frameaddress(17); - case 18: return $$frameaddress(18); - case 19: return $$frameaddress(19); - case 20: return $$frameaddress(20); - case 21: return $$frameaddress(21); - case 22: return $$frameaddress(22); - case 23: return $$frameaddress(23); - case 24: return $$frameaddress(24); - case 25: return $$frameaddress(25); - case 26: return $$frameaddress(26); - case 27: return $$frameaddress(27); - case 28: return $$frameaddress(28); - case 29: return $$frameaddress(29); - case 30: return $$frameaddress(30); - case 31: return $$frameaddress(31); - case 32: return $$frameaddress(32); - case 33: return $$frameaddress(33); - case 34: return $$frameaddress(34); - case 35: return $$frameaddress(35); - case 36: return $$frameaddress(36); - case 37: return $$frameaddress(37); - case 38: return $$frameaddress(38); - case 39: return $$frameaddress(39); - case 40: return $$frameaddress(40); - case 41: return $$frameaddress(41); - case 42: return $$frameaddress(42); - case 43: return $$frameaddress(43); - case 44: return $$frameaddress(44); - case 45: return $$frameaddress(45); - case 46: return $$frameaddress(46); - case 47: return $$frameaddress(47); - case 48: return $$frameaddress(48); - case 49: return $$frameaddress(49); - case 50: return $$frameaddress(50); - case 51: return $$frameaddress(51); - case 52: return $$frameaddress(52); - case 53: return $$frameaddress(53); - case 54: return $$frameaddress(54); - case 55: return $$frameaddress(55); - case 56: return $$frameaddress(56); - case 57: return $$frameaddress(57); - case 58: return $$frameaddress(58); - case 59: return $$frameaddress(59); - case 60: return $$frameaddress(60); - case 61: return $$frameaddress(61); - case 62: return $$frameaddress(62); - case 63: return $$frameaddress(63); - case 64: return $$frameaddress(64); - case 65: return $$frameaddress(65); - case 66: return $$frameaddress(66); - case 67: return $$frameaddress(67); - case 68: return $$frameaddress(68); - case 69: return $$frameaddress(69); - case 70: return $$frameaddress(70); - case 71: return $$frameaddress(71); - case 72: return $$frameaddress(72); - case 73: return $$frameaddress(73); - case 74: return $$frameaddress(74); - case 75: return $$frameaddress(75); - case 76: return $$frameaddress(76); - case 77: return $$frameaddress(77); - case 78: return $$frameaddress(78); - case 79: return $$frameaddress(79); - case 80: return $$frameaddress(80); - case 81: return $$frameaddress(81); - case 82: return $$frameaddress(82); - case 83: return $$frameaddress(83); - case 84: return $$frameaddress(84); - case 85: return $$frameaddress(85); - case 86: return $$frameaddress(86); - case 87: return $$frameaddress(87); - case 88: return $$frameaddress(88); - case 89: return $$frameaddress(89); - case 90: return $$frameaddress(90); - case 91: return $$frameaddress(91); - case 92: return $$frameaddress(92); - case 93: return $$frameaddress(93); - case 94: return $$frameaddress(94); - case 95: return $$frameaddress(95); - case 96: return $$frameaddress(96); - case 97: return $$frameaddress(97); - case 98: return $$frameaddress(98); - case 99: return $$frameaddress(99); - case 100: return $$frameaddress(100); - case 101: return $$frameaddress(101); - case 102: return $$frameaddress(102); - case 103: return $$frameaddress(103); - case 104: return $$frameaddress(104); - case 105: return $$frameaddress(105); - case 106: return $$frameaddress(106); - case 107: return $$frameaddress(107); - case 108: return $$frameaddress(108); - case 109: return $$frameaddress(109); - case 110: return $$frameaddress(110); - case 111: return $$frameaddress(111); - case 112: return $$frameaddress(112); - case 113: return $$frameaddress(113); - case 114: return $$frameaddress(114); - case 115: return $$frameaddress(115); - case 116: return $$frameaddress(116); - case 117: return $$frameaddress(117); - case 118: return $$frameaddress(118); - case 119: return $$frameaddress(119); - case 120: return $$frameaddress(120); - case 121: return $$frameaddress(121); - case 122: return $$frameaddress(122); - case 123: return $$frameaddress(123); - case 124: return $$frameaddress(124); - case 125: return $$frameaddress(125); - case 126: return $$frameaddress(126); - case 127: return $$frameaddress(127); - case 128: return $$frameaddress(128); - default: unreachable(); - } -} - -<* - @require n >= 0 -*> -macro void* get_returnaddress(int n) -{ - if (n > MAX_FRAMEADDRESS) return null; - switch (n) - { - case 0: return $$returnaddress(0); - case 1: return $$returnaddress(1); - case 2: return $$returnaddress(2); - case 3: return $$returnaddress(3); - case 4: return $$returnaddress(4); - case 5: return $$returnaddress(5); - case 6: return $$returnaddress(6); - case 7: return $$returnaddress(7); - case 8: return $$returnaddress(8); - case 9: return $$returnaddress(9); - case 10: return $$returnaddress(10); - case 11: return $$returnaddress(11); - case 12: return $$returnaddress(12); - case 13: return $$returnaddress(13); - case 14: return $$returnaddress(14); - case 15: return $$returnaddress(15); - case 16: return $$returnaddress(16); - case 17: return $$returnaddress(17); - case 18: return $$returnaddress(18); - case 19: return $$returnaddress(19); - case 20: return $$returnaddress(20); - case 21: return $$returnaddress(21); - case 22: return $$returnaddress(22); - case 23: return $$returnaddress(23); - case 24: return $$returnaddress(24); - case 25: return $$returnaddress(25); - case 26: return $$returnaddress(26); - case 27: return $$returnaddress(27); - case 28: return $$returnaddress(28); - case 29: return $$returnaddress(29); - case 30: return $$returnaddress(30); - case 31: return $$returnaddress(31); - case 32: return $$returnaddress(32); - case 33: return $$returnaddress(33); - case 34: return $$returnaddress(34); - case 35: return $$returnaddress(35); - case 36: return $$returnaddress(36); - case 37: return $$returnaddress(37); - case 38: return $$returnaddress(38); - case 39: return $$returnaddress(39); - case 40: return $$returnaddress(40); - case 41: return $$returnaddress(41); - case 42: return $$returnaddress(42); - case 43: return $$returnaddress(43); - case 44: return $$returnaddress(44); - case 45: return $$returnaddress(45); - case 46: return $$returnaddress(46); - case 47: return $$returnaddress(47); - case 48: return $$returnaddress(48); - case 49: return $$returnaddress(49); - case 50: return $$returnaddress(50); - case 51: return $$returnaddress(51); - case 52: return $$returnaddress(52); - case 53: return $$returnaddress(53); - case 54: return $$returnaddress(54); - case 55: return $$returnaddress(55); - case 56: return $$returnaddress(56); - case 57: return $$returnaddress(57); - case 58: return $$returnaddress(58); - case 59: return $$returnaddress(59); - case 60: return $$returnaddress(60); - case 61: return $$returnaddress(61); - case 62: return $$returnaddress(62); - case 63: return $$returnaddress(63); - case 64: return $$returnaddress(64); - case 65: return $$returnaddress(65); - case 66: return $$returnaddress(66); - case 67: return $$returnaddress(67); - case 68: return $$returnaddress(68); - case 69: return $$returnaddress(69); - case 70: return $$returnaddress(70); - case 71: return $$returnaddress(71); - case 72: return $$returnaddress(72); - case 73: return $$returnaddress(73); - case 74: return $$returnaddress(74); - case 75: return $$returnaddress(75); - case 76: return $$returnaddress(76); - case 77: return $$returnaddress(77); - case 78: return $$returnaddress(78); - case 79: return $$returnaddress(79); - case 80: return $$returnaddress(80); - case 81: return $$returnaddress(81); - case 82: return $$returnaddress(82); - case 83: return $$returnaddress(83); - case 84: return $$returnaddress(84); - case 85: return $$returnaddress(85); - case 86: return $$returnaddress(86); - case 87: return $$returnaddress(87); - case 88: return $$returnaddress(88); - case 89: return $$returnaddress(89); - case 90: return $$returnaddress(90); - case 91: return $$returnaddress(91); - case 92: return $$returnaddress(92); - case 93: return $$returnaddress(93); - case 94: return $$returnaddress(94); - case 95: return $$returnaddress(95); - case 96: return $$returnaddress(96); - case 97: return $$returnaddress(97); - case 98: return $$returnaddress(98); - case 99: return $$returnaddress(99); - case 100: return $$returnaddress(100); - case 101: return $$returnaddress(101); - case 102: return $$returnaddress(102); - case 103: return $$returnaddress(103); - case 104: return $$returnaddress(104); - case 105: return $$returnaddress(105); - case 106: return $$returnaddress(106); - case 107: return $$returnaddress(107); - case 108: return $$returnaddress(108); - case 109: return $$returnaddress(109); - case 110: return $$returnaddress(110); - case 111: return $$returnaddress(111); - case 112: return $$returnaddress(112); - case 113: return $$returnaddress(113); - case 114: return $$returnaddress(114); - case 115: return $$returnaddress(115); - case 116: return $$returnaddress(116); - case 117: return $$returnaddress(117); - case 118: return $$returnaddress(118); - case 119: return $$returnaddress(119); - case 120: return $$returnaddress(120); - case 121: return $$returnaddress(121); - case 122: return $$returnaddress(122); - case 123: return $$returnaddress(123); - case 124: return $$returnaddress(124); - case 125: return $$returnaddress(125); - case 126: return $$returnaddress(126); - case 127: return $$returnaddress(127); - case 128: return $$returnaddress(128); - default: unreachable(); - } -} - -module std::core::builtin @if((env::LINUX || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS); -import libc, std::io; - -fn void sig_panic(String message) -{ - default_panic(message, "???", "???", 0); -} - -SignalFunction old_bus_error; -SignalFunction old_segmentation_fault; - -fn void sig_bus_error(CInt i) -{ - $if !env::NATIVE_STACKTRACE: - sig_panic("Illegal memory access."); - $else - $if $defined(io::stderr): - if (!print_backtrace("Illegal memory access.", 1)) - { - io::eprintn("\nERROR: 'Illegal memory access'."); - } - $endif - $endif - $$trap(); -} - -fn void sig_segmentation_fault(CInt i) -{ - $if !env::NATIVE_STACKTRACE: - sig_panic("Out of bounds memory access."); - $else - $if $defined(io::stderr): - if (!print_backtrace("Out of bounds memory access.", 1)) - { - io::eprintn("\nERROR: Memory error without backtrace, possible stack overflow."); - } - $endif - $endif - $$trap(); -} - -fn void install_signal_handler(CInt signal, SignalFunction func) @local -{ - SignalFunction old = libc::signal(signal, func); - // Restore - if ((iptr)old > 1024) libc::signal(signal, old); -} - -// Clean this up -fn void install_signal_handlers() @init(101) @local @if(env::BACKTRACE) -{ - install_signal_handler(libc::SIGBUS, &sig_bus_error); - install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault); -} - diff --git a/lib7/std/core/builtin_comparison.c3 b/lib7/std/core/builtin_comparison.c3 deleted file mode 100644 index b511fb70e..000000000 --- a/lib7/std/core/builtin_comparison.c3 +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2021-2024 Christoffer Lerno and contributors. 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::core::builtin; - -<* - @require types::@comparable_value(a) && types::@comparable_value(b) -*> -macro less(a, b) @builtin -{ - $switch - $case $defined(a.less): - return a.less(b); - $case $defined(a.compare_to): - return a.compare_to(b) < 0; - $default: - return a < b; - $endswitch -} - -<* - @require types::@comparable_value(a) && types::@comparable_value(b) -*> -macro less_eq(a, b) @builtin -{ - $switch - $case $defined(a.less): - return !b.less(a); - $case $defined(a.compare_to): - return a.compare_to(b) <= 0; - $default: - return a <= b; - $endswitch -} - -<* - @require types::@comparable_value(a) && types::@comparable_value(b) -*> -macro greater(a, b) @builtin -{ - $switch - $case $defined(a.less): - return b.less(a); - $case $defined(a.compare_to): - return a.compare_to(b) > 0; - $default: - return a > b; - $endswitch -} - -<* - @require types::@comparable_value(a) && types::@comparable_value(b) -*> -macro int compare_to(a, b) @builtin -{ - $switch - $case $defined(a.compare_to): - return a.compare_to(b); - $case $defined(a.less): - return (int)b.less(a) - (int)a.less(b); - $default: - return (int)(a > b) - (int)(a < b); - $endswitch -} -<* - @require types::@comparable_value(a) && types::@comparable_value(b) -*> -macro greater_eq(a, b) @builtin -{ - $switch - $case $defined(a.less): - return !a.less(b); - $case $defined(a.compare_to): - return a.compare_to(b) >= 0; - $default: - return a >= b; - $endswitch -} - -<* - @require types::@equatable_value(a) && types::@equatable_value(b) `values must be equatable` -*> -macro bool equals(a, b) @builtin -{ - $switch - $case $defined(a.equals, a.equals(b)): - return a.equals(b); - $case $defined(a.compare_to, a.compare_to(b)): - return a.compare_to(b) == 0; - $case $defined(a.less): - return !a.less(b) && !b.less(a); - $default: - return a == b; - $endswitch -} - -macro min(x, ...) @builtin -{ - $if $vacount == 1: - return less(x, $vaarg[0]) ? x : $vaarg[0]; - $else - var result = x; - $for (var $i = 0; $i < $vacount; $i++) - if (less($vaarg[$i], result)) - { - result = $vaarg[$i]; - } - $endfor - return result; - $endif -} - -macro max(x, ...) @builtin -{ - $if $vacount == 1: - return greater(x, $vaarg[0]) ? x : $vaarg[0]; - $else - var result = x; - $for (var $i = 0; $i < $vacount; $i++) - if (greater($vaarg[$i], result)) - { - result = $vaarg[$i]; - } - $endfor - return result; - $endif -} - diff --git a/lib7/std/core/cinterop.c3 b/lib7/std/core/cinterop.c3 deleted file mode 100644 index 5cb9e6ca9..000000000 --- a/lib7/std/core/cinterop.c3 +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2021-2024 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::core::cinterop; - -const C_INT_SIZE = $$C_INT_SIZE; -const C_LONG_SIZE = $$C_LONG_SIZE; -const C_SHORT_SIZE = $$C_SHORT_SIZE; -const C_LONG_LONG_SIZE = $$C_LONG_LONG_SIZE; - -$assert C_SHORT_SIZE < 32; -$assert C_INT_SIZE < 128; -$assert C_LONG_SIZE < 128; -$assert C_LONG_LONG_SIZE <= 128; -$assert C_SHORT_SIZE <= C_INT_SIZE; -$assert C_INT_SIZE <= C_LONG_SIZE; -$assert C_LONG_SIZE <= C_LONG_LONG_SIZE; - -def CShort = $typefrom(signed_int_from_bitsize($$C_SHORT_SIZE)); -def CUShort = $typefrom(unsigned_int_from_bitsize($$C_SHORT_SIZE)); -def CInt = $typefrom(signed_int_from_bitsize($$C_INT_SIZE)); -def CUInt = $typefrom(unsigned_int_from_bitsize($$C_INT_SIZE)); -def CLong = $typefrom(signed_int_from_bitsize($$C_LONG_SIZE)); -def CULong = $typefrom(unsigned_int_from_bitsize($$C_LONG_SIZE)); -def CLongLong = $typefrom(signed_int_from_bitsize($$C_LONG_LONG_SIZE)); -def CULongLong = $typefrom(unsigned_int_from_bitsize($$C_LONG_LONG_SIZE)); -def CSChar = ichar; -def CUChar = char; - -def CChar = $typefrom($$C_CHAR_IS_SIGNED ? ichar.typeid : char.typeid); - -enum CBool : char -{ - FALSE, - TRUE -} - -// Helper macros -macro typeid signed_int_from_bitsize(usz $bitsize) @private -{ - $switch ($bitsize) - $case 128: return int128.typeid; - $case 64: return long.typeid; - $case 32: return int.typeid; - $case 16: return short.typeid; - $case 8: return ichar.typeid; - $default: $error("Invalid bitsize"); - $endswitch -} - -macro typeid unsigned_int_from_bitsize(usz $bitsize) @private -{ - $switch ($bitsize) - $case 128: return uint128.typeid; - $case 64: return ulong.typeid; - $case 32: return uint.typeid; - $case 16: return ushort.typeid; - $case 8: return char.typeid; - $default: $error("Invalid bitsize"); - $endswitch -} diff --git a/lib7/std/core/conv.c3 b/lib7/std/core/conv.c3 deleted file mode 100644 index b2b593775..000000000 --- a/lib7/std/core/conv.c3 +++ /dev/null @@ -1,416 +0,0 @@ -module std::core::string::conv; - -const uint UTF16_SURROGATE_OFFSET @private = 0x10000; -const uint UTF16_SURROGATE_GENERIC_MASK @private = 0xF800; -const uint UTF16_SURROGATE_GENERIC_VALUE @private = 0xD800; -const uint UTF16_SURROGATE_MASK @private = 0xFC00; -const uint UTF16_SURROGATE_CODEPOINT_MASK @private = 0x03FF; -const uint UTF16_SURROGATE_BITS @private = 10; -const uint UTF16_SURROGATE_LOW_VALUE @private = 0xDC00; -const uint UTF16_SURROGATE_HIGH_VALUE @private = 0xD800; - -<* - @param c `The utf32 codepoint to convert` - @param [out] output `the resulting buffer` -*> -fn usz! char32_to_utf8(Char32 c, char[] output) -{ - if (!output.len) return UnicodeResult.CONVERSION_FAILED?; - switch (true) - { - case c <= 0x7f: - output[0] = (char)c; - return 1; - case c <= 0x7ff: - if (output.len < 2) return UnicodeResult.CONVERSION_FAILED?; - output[0] = (char)(0xC0 | c >> 6); - output[1] = (char)(0x80 | (c & 0x3F)); - return 2; - case c <= 0xffff: - if (output.len < 3) return UnicodeResult.CONVERSION_FAILED?; - output[0] = (char)(0xE0 | c >> 12); - output[1] = (char)(0x80 | (c >> 6 & 0x3F)); - output[2] = (char)(0x80 | (c & 0x3F)); - return 3; - case c <= 0x10ffff: - if (output.len < 4) return UnicodeResult.CONVERSION_FAILED?; - output[0] = (char)(0xF0 | c >> 18); - output[1] = (char)(0x80 | (c >> 12 & 0x3F)); - output[2] = (char)(0x80 | (c >> 6 & 0x3F)); - output[3] = (char)(0x80 | (c & 0x3F)); - return 4; - default: - // 0x10FFFF and above is not defined. - return UnicodeResult.CONVERSION_FAILED?; - } -} - -<* - Convert a code pointer into 1-2 UTF16 characters. - - @param c `The character to convert.` - @param [inout] output `the resulting UTF16 buffer to write to.` -*> -fn void char32_to_utf16_unsafe(Char32 c, Char16** output) -{ - if (c < UTF16_SURROGATE_OFFSET) - { - (*output)++[0] = (Char16)c; - return; - } - c -= UTF16_SURROGATE_OFFSET; - Char16 low = (Char16)(UTF16_SURROGATE_LOW_VALUE | (c & UTF16_SURROGATE_CODEPOINT_MASK)); - c >>= UTF16_SURROGATE_BITS; - Char16 high = (Char16)(UTF16_SURROGATE_HIGH_VALUE | (c & UTF16_SURROGATE_CODEPOINT_MASK)); - (*output)++[0] = (Char16)high; - (*output)++[0] = (Char16)low; -} - -<* - Convert 1-2 UTF16 data points into UTF8. - - @param [in] ptr `The UTF16 data to convert.` - @param [inout] available `amount of UTF16 data available.` - @param [inout] output `the resulting utf8 buffer to write to.` -*> -fn void! char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output) -{ - Char16 high = *ptr; - if (high & UTF16_SURROGATE_GENERIC_MASK != UTF16_SURROGATE_GENERIC_VALUE) - { - char32_to_utf8_unsafe(high, output); - *available = 1; - return; - } - // Low surrogate first is an error - if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return UnicodeResult.INVALID_UTF16?; - - // Unmatched high surrogate is an error - if (*available == 1) return UnicodeResult.INVALID_UTF16?; - - Char16 low = ptr[1]; - - // Unmatched high surrogate, invalid - if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return UnicodeResult.INVALID_UTF16?; - - // The high bits of the codepoint are the value bits of the high surrogate - // The low bits of the codepoint are the value bits of the low surrogate - Char32 uc = (high & UTF16_SURROGATE_CODEPOINT_MASK) << UTF16_SURROGATE_BITS - | (low & UTF16_SURROGATE_CODEPOINT_MASK) + UTF16_SURROGATE_OFFSET; - char32_to_utf8_unsafe(uc, output); - *available = 2; -} -<* - @param c `The utf32 codepoint to convert` - @param [inout] output `the resulting buffer` -*> -fn usz char32_to_utf8_unsafe(Char32 c, char** output) -{ - switch - { - case c <= 0x7f: - (*output)++[0] = (char)c; - return 1; - case c <= 0x7ff: - (*output)++[0] = (char)(0xC0 | c >> 6); - (*output)++[0] = (char)(0x80 | (c & 0x3F)); - return 2; - case c <= 0xffff: - (*output)++[0] = (char)(0xE0 | c >> 12); - (*output)++[0] = (char)(0x80 | (c >> 6 & 0x3F)); - (*output)++[0] = (char)(0x80 | (c & 0x3F)); - return 3; - default: - (*output)++[0] = (char)(0xF0 | c >> 18); - (*output)++[0] = (char)(0x80 | (c >> 12 & 0x3F)); - (*output)++[0] = (char)(0x80 | (c >> 6 & 0x3F)); - (*output)++[0] = (char)(0x80 | (c & 0x3F)); - return 4; - } -} - -<* - @param [in] ptr `pointer to the first character to parse` - @param [inout] size `Set to max characters to read, set to characters read` - @return `the parsed 32 bit codepoint` -*> -fn Char32! utf8_to_char32(char* ptr, usz* size) -{ - usz max_size = *size; - if (max_size < 1) return UnicodeResult.INVALID_UTF8?; - char c = (ptr++)[0]; - - if ((c & 0x80) == 0) - { - *size = 1; - return c; - } - if ((c & 0xE0) == 0xC0) - { - if (max_size < 2) return UnicodeResult.INVALID_UTF8?; - *size = 2; - Char32 uc = (c & 0x1F) << 6; - c = *ptr; - // Overlong sequence or invalid second. - if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?; - return uc + c & 0x3F; - } - if ((c & 0xF0) == 0xE0) - { - if (max_size < 3) return UnicodeResult.INVALID_UTF8?; - *size = 3; - Char32 uc = (c & 0x0F) << 12; - c = ptr++[0]; - if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?; - uc += (c & 0x3F) << 6; - c = ptr++[0]; - // Overlong sequence or invalid last - if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?; - return uc + c & 0x3F; - } - if (max_size < 4) return UnicodeResult.INVALID_UTF8?; - if ((c & 0xF8) != 0xF0) return UnicodeResult.INVALID_UTF8?; - *size = 4; - Char32 uc = (c & 0x07) << 18; - c = ptr++[0]; - if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?; - uc += (c & 0x3F) << 12; - c = ptr++[0]; - if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?; - uc += (c & 0x3F) << 6; - c = ptr++[0]; - // Overlong sequence or invalid last - if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?; - return uc + c & 0x3F; -} - -<* - @param utf8 `An UTF-8 encoded slice of bytes` - @return `the number of encoded code points` -*> -fn usz utf8_codepoints(String utf8) -{ - usz len = 0; - foreach (char c : utf8) - { - if (c & 0xC0 != 0x80) len++; - } - return len; -} - -<* - Calculate the UTF8 length required to encode an UTF32 array. - @param [in] utf32 `the utf32 data to calculate from` - @return `the length of the resulting UTF8 array` -*> -fn usz utf8len_for_utf32(Char32[] utf32) -{ - usz len = 0; - foreach (Char32 uc : utf32) - { - switch (true) - { - case uc <= 0x7f: - len++; - case uc <= 0x7ff: - len += 2; - case uc <= 0xffff: - len += 3; - default: - len += 4; - } - } - return len; -} - -<* - Calculate the UTF8 length required to encode an UTF16 array. - @param [in] utf16 `the utf16 data to calculate from` - @return `the length of the resulting UTF8 array` -*> -fn usz utf8len_for_utf16(Char16[] utf16) -{ - usz len = 0; - usz len16 = utf16.len; - for (usz i = 0; i < len16; i++) - { - Char16 c = utf16[i]; - if (c & UTF16_SURROGATE_GENERIC_MASK != UTF16_SURROGATE_GENERIC_VALUE) - { - if (c <= 0x7f) - { - len++; - continue; - } - if (c <= 0x7ff) - { - len += 2; - continue; - } - len += 3; - continue; - } - len += 4; - } - return len; -} - -<* - Calculate the UTF16 length required to encode a UTF8 array. - @param utf8 `the utf8 data to calculate from` - @return `the length of the resulting UTF16 array` -*> -fn usz utf16len_for_utf8(String utf8) -{ - usz len = utf8.len; - usz len16 = 0; - for (usz i = 0; i < len; i++) - { - len16++; - char c = utf8[i]; - if (c & 0x80 == 0) continue; - i++; - if (c & 0xE0 == 0xC0) continue; - i++; - if (c & 0xF0 == 0xE0) continue; - i++; - len16++; - } - return len16; -} - -<* - @param [in] utf32 `the UTF32 array to check the length for` - @return `the required length of an UTF16 array to hold the UTF32 data.` -*> -fn usz utf16len_for_utf32(Char32[] utf32) -{ - usz len = utf32.len; - foreach (Char32 uc : utf32) - { - if (uc >= UTF16_SURROGATE_OFFSET) len++; - } - return len; -} - -<* - Convert an UTF32 array to an UTF8 array. - - @param [in] utf32 - @param [out] utf8_buffer - @return `the number of bytes written.` -*> -fn usz! utf32to8(Char32[] utf32, char[] utf8_buffer) -{ - char[] buffer = utf8_buffer; - foreach (uc : utf32) - { - usz used = char32_to_utf8(uc, buffer) @inline!; - buffer = buffer[used..]; - } - // Zero terminate if there is space. - if (buffer.len > 0) buffer[0] = 0; - return utf8_buffer.len - buffer.len; -} - -<* - Convert an UTF8 array to an UTF32 array. - - @param [in] utf8 - @param [out] utf32_buffer - @return `the number of Char32s written.` -*> -fn usz! utf8to32(String utf8, Char32[] utf32_buffer) -{ - usz len = utf8.len; - Char32* ptr = utf32_buffer.ptr; - usz len32 = 0; - usz buf_len = utf32_buffer.len; - for (usz i = 0; i < len;) - { - if (len32 == buf_len) return UnicodeResult.CONVERSION_FAILED?; - usz width = len - i; - Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!; - i += width; - ptr[len32++] = uc; - } - // Zero terminate if possible - if (len32 + 1 < buf_len) ptr[len32] = 0; - return len32; -} - -<* - Copy an array of UTF16 data into an UTF8 buffer without bounds - checking. This will assume the buffer is sufficiently large to hold - the converted data. - - @param [in] utf16 `The UTF16 array containing the data to convert.` - @param [out] utf8_buffer `the (sufficiently large) buffer to hold the UTF16 data.` -*> -fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer) -{ - usz len16 = utf16.len; - for (usz i = 0; i < len16;) - { - usz available = len16 - i; - char16_to_utf8_unsafe(&utf16[i], &available, &utf8_buffer) @inline!; - i += available; - } -} - -<* - Copy an array of UTF8 data into an UTF32 buffer without bounds - checking. This will assume the buffer is sufficiently large to hold - the converted data. - - @param [in] utf8 `The UTF8 buffer containing the data to convert.` - @param [out] utf32_buffer `the (sufficiently large) buffer to hold the UTF8 data.` -*> -fn void! utf8to32_unsafe(String utf8, Char32* utf32_buffer) -{ - usz len = utf8.len; - for (usz i = 0; i < len;) - { - usz width = len - i; - Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!; - i += width; - (utf32_buffer++)[0] = uc; - } -} - -<* - Copy an array of UTF8 data into an UTF16 buffer without bounds - checking. This will assume the buffer is sufficiently large to hold - the converted data. - - @param [in] utf8 `The UTF8 buffer containing the data to convert.` - @param [out] utf16_buffer `the (sufficiently large) buffer to hold the UTF8 data.` -*> -fn void! utf8to16_unsafe(String utf8, Char16* utf16_buffer) -{ - usz len = utf8.len; - for (usz i = 0; i < len;) - { - usz width = len - i; - Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!; - char32_to_utf16_unsafe(uc, &utf16_buffer) @inline; - i += width; - } -} - -<* - Copy an array of UTF32 code points into an UTF8 buffer without bounds - checking. This will assume the buffer is sufficiently large to hold - the converted data. - - @param [in] utf32 `The UTF32 buffer containing the data to convert.` - @param [out] utf8_buffer `the (sufficiently large) buffer to hold the UTF8 data.` -*> -fn void utf32to8_unsafe(Char32[] utf32, char* utf8_buffer) -{ - char* start = utf8_buffer; - foreach (Char32 uc : utf32) - { - char32_to_utf8_unsafe(uc, &utf8_buffer) @inline; - } -} diff --git a/lib7/std/core/dstring.c3 b/lib7/std/core/dstring.c3 deleted file mode 100644 index 0bb453343..000000000 --- a/lib7/std/core/dstring.c3 +++ /dev/null @@ -1,658 +0,0 @@ -module std::core::dstring; -import std::io; - -distinct DString (OutStream) = DStringOpaque*; -distinct DStringOpaque = void; - -const usz MIN_CAPACITY @private = 16; - -<* - @require !self.data() "String already initialized" -*> -fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY) -{ - if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; - StringData* data = allocator::alloc_with_padding(allocator, StringData, capacity)!!; - data.allocator = allocator; - data.len = 0; - data.capacity = capacity; - return *self = (DString)data; -} - -<* - @require !self.data() "String already initialized" -*> -fn DString DString.tinit(&self, usz capacity = MIN_CAPACITY) -{ - return self.init(tmem(), capacity) @inline; -} - -fn DString new_with_capacity(Allocator allocator, usz capacity) -{ - return (DString){}.init(allocator, capacity); -} - -fn DString temp_with_capacity(usz capacity) => new_with_capacity(tmem(), capacity) @inline; - -fn DString new(Allocator allocator, String c = "") -{ - usz len = c.len; - StringData* data = (StringData*)new_with_capacity(allocator, len); - if (len) - { - data.len = len; - mem::copy(&data.chars, c.ptr, len); - } - return (DString)data; -} - -fn DString temp(String s = "") => new(tmem(), s) @inline; - - -fn void DString.replace_char(self, char ch, char replacement) -{ - StringData* data = self.data(); - foreach (&c : data.chars[:data.len]) - { - if (*c == ch) *c = replacement; - } -} - -fn void DString.replace(&self, String needle, String replacement) -{ - StringData* data = self.data(); - usz needle_len = needle.len; - if (!data || data.len < needle_len) return; - usz replace_len = replacement.len; - if (needle_len == 1 && replace_len == 1) - { - self.replace_char(needle[0], replacement[0]); - return; - } - @pool(data.allocator) { - String str = self.tcopy_str(); - self.clear(); - usz len = str.len; - usz match = 0; - foreach (i, c : str) - { - if (c == needle[match]) - { - match++; - if (match == needle_len) - { - self.append_chars(replacement); - match = 0; - continue; - } - continue; - } - if (match > 0) - { - self.append_chars(str[i - match:match]); - match = 0; - } - self.append_char(c); - } - if (match > 0) self.append_chars(str[^match:match]); - }; -} - -fn DString DString.concat(self, Allocator allocator, DString b) -{ - DString string; - string.init(allocator, self.len() + b.len()); - string.append(self); - string.append(b); - return string; -} - -fn DString DString.tconcat(self, DString b) => self.concat(tmem(), b); - -fn ZString DString.zstr_view(&self) -{ - StringData* data = self.data(); - if (!data) return ""; - if (data.capacity == data.len) - { - self.reserve(1); - data = self.data(); - data.chars[data.len] = 0; - } - else if (data.chars[data.len] != 0) - { - data.chars[data.len] = 0; - } - return (ZString)&data.chars[0]; -} - -fn usz DString.capacity(self) -{ - if (!self) return 0; - return self.data().capacity; -} - -fn usz DString.len(&self) @dynamic @operator(len) -{ - if (!*self) return 0; - return self.data().len; -} - -<* - @require new_size <= self.len() -*> -fn void DString.chop(self, usz new_size) -{ - if (!self) return; - self.data().len = new_size; -} - -fn String DString.str_view(self) -{ - StringData* data = self.data(); - if (!data) return ""; - return (String)data.chars[:data.len]; -} - -<* - @require index < self.len() - @require self.data() != null "Empty string" -*> -fn char DString.char_at(self, usz index) @operator([]) -{ - return self.data().chars[index]; -} - -<* - @require index < self.len() - @require self.data() != null "Empty string" -*> -fn char* DString.char_ref(&self, usz index) @operator(&[]) -{ - return &self.data().chars[index]; -} - -fn usz DString.append_utf32(&self, Char32[] chars) -{ - self.reserve(chars.len); - usz end = self.len(); - foreach (Char32 c : chars) - { - self.append_char32(c); - } - return self.data().len - end; -} - -<* - @require index < self.len() -*> -fn void DString.set(self, usz index, char c) @operator([]=) -{ - self.data().chars[index] = c; -} - -fn void DString.append_repeat(&self, char c, usz times) -{ - if (times == 0) return; - self.reserve(times); - StringData* data = self.data(); - for (usz i = 0; i < times; i++) - { - data.chars[data.len++] = c; - } -} - -<* - @require c <= 0x10ffff -*> -fn usz DString.append_char32(&self, Char32 c) -{ - char[4] buffer @noinit; - char* p = &buffer; - usz n = conv::char32_to_utf8_unsafe(c, &p); - self.reserve(n); - StringData* data = self.data(); - data.chars[data.len:n] = buffer[:n]; - data.len += n; - return n; -} - -fn DString DString.tcopy(&self) => self.copy(tmem()); - -fn DString DString.copy(self, Allocator allocator) -{ - if (!self) return new(allocator); - StringData* data = self.data(); - DString new_string = new_with_capacity(allocator, data.capacity); - mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len); - return new_string; -} - -fn ZString DString.copy_zstr(self, Allocator allocator) -{ - usz str_len = self.len(); - if (!str_len) - { - return (ZString)allocator::calloc(allocator, 1); - } - char* zstr = allocator::malloc(allocator, str_len + 1); - StringData* data = self.data(); - mem::copy(zstr, &data.chars, str_len); - zstr[str_len] = 0; - return (ZString)zstr; -} - -fn String DString.copy_str(self, Allocator allocator) -{ - return (String)self.copy_zstr(allocator)[:self.len()]; -} - -fn String DString.tcopy_str(self) => self.copy_str(tmem()) @inline; - -fn bool DString.equals(self, DString other_string) -{ - StringData *str1 = self.data(); - StringData *str2 = other_string.data(); - if (str1 == str2) return true; - if (!str1) return str2.len == 0; - if (!str2) return str1.len == 0; - usz str1_len = str1.len; - if (str1_len != str2.len) return false; - for (int i = 0; i < str1_len; i++) - { - if (str1.chars[i] != str2.chars[i]) return false; - } - return true; -} - -fn void DString.free(&self) -{ - if (!*self) return; - StringData* data = self.data(); - if (!data) return; - allocator::free(data.allocator, data); - *self = (DString)null; -} - -fn bool DString.less(self, DString other_string) -{ - StringData* str1 = self.data(); - StringData* str2 = other_string.data(); - if (str1 == str2) return false; - if (!str1) return str2.len != 0; - if (!str2) return str1.len == 0; - usz str1_len = str1.len; - usz str2_len = str2.len; - if (str1_len != str2_len) return str1_len < str2_len; - for (int i = 0; i < str1_len; i++) - { - if (str1.chars[i] >= str2.chars[i]) return false; - } - return true; -} - -fn void DString.append_chars(&self, String str) -{ - usz other_len = str.len; - if (!other_len) return; - if (!*self) - { - *self = temp(str); - return; - } - self.reserve(other_len); - StringData* data = self.data(); - mem::copy(&data.chars[data.len], str.ptr, other_len); - data.len += other_len; -} - -fn Char32[] DString.copy_utf32(&self, Allocator allocator) -{ - return self.str_view().to_utf32_copy(allocator) @inline!!; -} - -fn void DString.append_string(&self, DString str) -{ - StringData* other = str.data(); - if (!other) return; - self.append(str.str_view()); -} - -fn void DString.clear(self) -{ - if (!self) return; - self.data().len = 0; -} - -fn usz! DString.write(&self, char[] buffer) @dynamic -{ - self.append_chars((String)buffer); - return buffer.len; -} - -fn void! DString.write_byte(&self, char c) @dynamic -{ - self.append_char(c); -} - -fn void DString.append_char(&self, char c) -{ - if (!*self) - { - *self = temp_with_capacity(MIN_CAPACITY); - } - self.reserve(1); - StringData* data = self.data(); - data.chars[data.len++] = c; -} - -<* - @require start < self.len() - @require end < self.len() - @require end >= start "End must be same or equal to the start" -*> -fn void DString.delete_range(&self, usz start, usz end) -{ - self.delete(start, end - start + 1); -} - -<* - @require start < self.len() - @require start + len <= self.len() -*> -fn void DString.delete(&self, usz start, usz len = 1) -{ - if (!len) return; - StringData* data = self.data(); - usz new_len = data.len - len; - if (new_len == 0) - { - data.len = 0; - return; - } - usz len_after = data.len - start - len; - if (len_after > 0) - { - data.chars[start:len_after] = data.chars[start + len:len_after]; - } - data.len = new_len; -} - -macro void DString.append(&self, value) -{ - var $Type = $typeof(value); - $switch ($Type) - $case char: - $case ichar: - self.append_char(value); - $case DString: - self.append_string(value); - $case String: - self.append_chars(value); - $case Char32: - self.append_char32(value); - $default: - $switch - $case $defined((Char32)value): - self.append_char32((Char32)value); - $case $defined((String)value): - self.append_chars((String)value); - $default: - $error "Unsupported type for append – use appendf instead."; - $endswitch - $endswitch -} - -<* - @require index <= self.len() -*> -fn void DString.insert_chars_at(&self, usz index, String s) -{ - if (s.len == 0) return; - self.reserve(s.len); - StringData* data = self.data(); - usz len = self.len(); - if (data.chars[:len].ptr == s.ptr) - { - // Source and destination are the same: nothing to do. - return; - } - index = min(index, len); - data.len += s.len; - - char* start = data.chars[index:s.len].ptr; // area to insert into - mem::move(start + s.len, start, len - index); // move existing data - switch - { - case s.ptr <= start && start < s.ptr + s.len: - // Overlapping areas. - foreach_r (i, c : s) - { - data.chars[index + i] = c; - } - case start <= s.ptr && s.ptr < start + len: - // Source has moved. - mem::move(start, s.ptr + s.len, s.len); - default: - mem::move(start, s, s.len); - } -} - -<* - @require index <= self.len() -*> -fn void DString.insert_string_at(&self, usz index, DString str) -{ - StringData* other = str.data(); - if (!other) return; - self.insert_at(index, str.str_view()); -} - -<* - @require index <= self.len() -*> -fn void DString.insert_char_at(&self, usz index, char c) -{ - self.reserve(1); - StringData* data = self.data(); - - char* start = &data.chars[index]; - mem::move(start + 1, start, self.len() - index); - data.chars[index] = c; - data.len++; -} - -<* - @require index <= self.len() -*> -fn usz DString.insert_char32_at(&self, usz index, Char32 c) -{ - char[4] buffer @noinit; - char* p = &buffer; - usz n = conv::char32_to_utf8_unsafe(c, &p); - - self.reserve(n); - StringData* data = self.data(); - - char* start = &data.chars[index]; - mem::move(start + n, start, self.len() - index); - data.chars[index:n] = buffer[:n]; - data.len += n; - - return n; -} - -<* - @require index <= self.len() -*> -fn usz DString.insert_utf32_at(&self, usz index, Char32[] chars) -{ - usz n = conv::utf8len_for_utf32(chars); - - self.reserve(n); - StringData* data = self.data(); - - char* start = &data.chars[index]; - mem::move(start + n, start, self.len() - index); - - char[4] buffer @noinit; - - foreach(c : chars) - { - char* p = &buffer; - usz m = conv::char32_to_utf8_unsafe(c, &p); - data.chars[index:m] = buffer[:m]; - index += m; - } - - data.len += n; - - return n; -} - -macro void DString.insert_at(&self, usz index, value) -{ - var $Type = $typeof(value); - $switch ($Type) - $case char: - $case ichar: - self.insert_char_at(index, value); - $case DString: - self.insert_string_at(index, value); - $case String: - self.insert_chars_at(index, value); - $case Char32: - self.insert_char32_at(index, value); - $default: - $switch - $case $defined((Char32)value): - self.insert_char32_at(index, (Char32)value); - $case $defined((String)value): - self.insert_chars_at(index, (String)value); - $default: - $error "Unsupported type for insert"; - $endswitch - $endswitch -} - -fn usz! DString.appendf(&self, String format, args...) @maydiscard -{ - if (!self.data()) self.tinit(format.len + 20); - @pool(self.data().allocator) - { - Formatter formatter; - formatter.init(&out_string_append_fn, self); - return formatter.vprintf(format, args); - }; -} - -fn usz! DString.appendfn(&self, String format, args...) @maydiscard -{ - if (!self.data()) self.tinit(format.len + 20); - @pool(self.data().allocator) - { - Formatter formatter; - formatter.init(&out_string_append_fn, self); - usz len = formatter.vprintf(format, args)!; - self.append('\n'); - return len + 1; - }; -} - -fn DString join(Allocator allocator, String[] s, String joiner) -{ - if (!s.len) return new(allocator); - usz total_size = joiner.len * s.len; - foreach (String* &str : s) - { - total_size += str.len; - } - DString res = new_with_capacity(allocator, total_size); - res.append(s[0]); - foreach (String* &str : s[1..]) - { - res.append(joiner); - res.append(*str); - } - return res; -} - -fn void! out_string_append_fn(void* data, char c) @private -{ - DString* s = data; - s.append_char(c); -} - -fn void DString.reverse(self) -{ - StringData *data = self.data(); - if (!data) return; - isz mid = data.len / 2; - for (isz i = 0; i < mid; i++) - { - char temp = data.chars[i]; - isz reverse_index = data.len - 1 - i; - data.chars[i] = data.chars[reverse_index]; - data.chars[reverse_index] = temp; - } -} - -fn StringData* DString.data(self) @inline @private -{ - return (StringData*)self; -} - -fn void DString.reserve(&self, usz addition) -{ - StringData* data = self.data(); - if (!data) - { - *self = dstring::temp_with_capacity(addition); - return; - } - usz len = data.len + addition; - if (data.capacity >= len) return; - usz new_capacity = data.capacity * 2; - if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY; - while (new_capacity < len) new_capacity *= 2; - data.capacity = new_capacity; - *self = (DString)allocator::realloc(data.allocator, data, StringData.sizeof + new_capacity); -} - -fn usz! DString.read_from_stream(&self, InStream reader) -{ - if (&reader.available) - { - usz total_read = 0; - while (usz available = reader.available()!) - { - self.reserve(available); - StringData* data = self.data(); - usz len = reader.read(data.chars[data.len..(data.capacity - 1)])!; - total_read += len; - data.len += len; - } - return total_read; - } - usz total_read = 0; - while (true) - { - // Reserve at least 16 bytes - self.reserve(16); - StringData* data = self.data(); - // Read into the rest of the buffer - usz read = reader.read(data.chars[data.len..(data.capacity - 1)])!; - data.len += read; - // Ok, we reached the end. - if (read < 16) return total_read; - // Otherwise go another round - } -} - -struct StringData @private -{ - Allocator allocator; - usz len; - usz capacity; - char[?] chars; -} diff --git a/lib7/std/core/env.c3 b/lib7/std/core/env.c3 deleted file mode 100644 index d6af97c00..000000000 --- a/lib7/std/core/env.c3 +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) 2021-2024 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::core::env; - -enum CompilerOptLevel -{ - O0, - O1, - O2, - O3 -} - -enum MemoryEnvironment -{ - NORMAL, - SMALL, - TINY, - NONE -} - -enum OsType -{ - UNKNOWN, - NONE, - ANANAS, - CLOUD_ABI, - DRAGON_FLY, - FREEBSD, - FUCHSIA, - IOS, - KFREEBSD, - LINUX, - PS3, - MACOS, - NETBSD, - OPENBSD, - SOLARIS, - WIN32, - HAIKU, - MINIX, - RTEMS, - NACL, // Native Client - CNK, // BG/P Compute-Node Kernel - AIX, - CUDA, - NVOPENCL, - AMDHSA, - PS4, - ELFIAMCU, - TVOS, - WATCHOS, - MESA3D, - CONTIKI, - AMDPAL, - HERMITCORE, - HURD, - WASI, - EMSCRIPTEN, -} - -enum ArchType -{ - UNKNOWN, - ARM, // ARM (little endian): arm, armv.*, xscale - ARMB, // ARM (big endian): armeb - AARCH64, // AArch64 (little endian): aarch64 - AARCH64_BE, // AArch64 (big endian): aarch64_be - AARCH64_32, // AArch64 (little endian) ILP32: aarch64_32 - ARC, // ARC: Synopsys ARC - AVR, // AVR: Atmel AVR microcontroller - BPFEL, // eBPF or extended BPF or 64-bit BPF (little endian) - BPFEB, // eBPF or extended BPF or 64-bit BPF (big endian) - HEXAGON, // Hexagon: hexagon - MIPS, // MIPS: mips, mipsallegrex, mipsr6 - MIPSEL, // MIPSEL: mipsel, mipsallegrexe, mipsr6el - MIPS64, // MIPS64: mips64, mips64r6, mipsn32, mipsn32r6 - MIPS64EL, // MIPS64EL: mips64el, mips64r6el, mipsn32el, mipsn32r6el - MSP430, // MSP430: msp430 - PPC, // PPC: powerpc - PPC64, // PPC64: powerpc64, ppu - PPC64LE, // PPC64LE: powerpc64le - R600, // R600: AMD GPUs HD2XXX - HD6XXX - AMDGCN, // AMDGCN: AMD GCN GPUs - RISCV32, // RISC-V (32-bit): riscv32 - RISCV64, // RISC-V (64-bit): riscv64 - SPARC, // Sparc: sparc - SPARCV9, // Sparcv9: Sparcv9 - SPARCEL, // Sparc: (endianness = little). NB: 'Sparcle' is a CPU variant - SYSTEMZ, // SystemZ: s390x - TCE, // TCE (http://tce.cs.tut.fi/): tce - TCELE, // TCE little endian (http://tce.cs.tut.fi/): tcele - THUMB, // Thumb (little endian): thumb, thumbv.* - THUMBEB, // Thumb (big endian): thumbeb - X86, // X86: i[3-9]86 - X86_64, // X86-64: amd64, x86_64 - XCORE, // XCore: xcore - NVPTX, // NVPTX: 32-bit - NVPTX64, // NVPTX: 64-bit - LE32, // le32: generic little-endian 32-bit CPU (PNaCl) - LE64, // le64: generic little-endian 64-bit CPU (PNaCl) - AMDIL, // AMDIL - AMDIL64, // AMDIL with 64-bit pointers - HSAIL, // AMD HSAIL - HSAIL64, // AMD HSAIL with 64-bit pointers - SPIR, // SPIR: standard portable IR for OpenCL 32-bit version - SPIR64, // SPIR: standard portable IR for OpenCL 64-bit version - KALIMBA, // Kalimba: generic kalimba - SHAVE, // SHAVE: Movidius vector VLIW processors - LANAI, // Lanai: Lanai 32-bit - WASM32, // WebAssembly with 32-bit pointers - WASM64, // WebAssembly with 64-bit pointers - RSCRIPT32, // 32-bit RenderScript - RSCRIPT64, // 64-bit RenderScript - XTENSA, // Xtensa -} - -const String COMPILER_BUILD_HASH = $$BUILD_HASH; -const String COMPILER_BUILD_DATE = $$BUILD_DATE; -const OsType OS_TYPE = OsType.from_ordinal($$OS_TYPE); -const ArchType ARCH_TYPE = ArchType.from_ordinal($$ARCH_TYPE); -const bool ARCH_32_BIT = $$REGISTER_SIZE == 32; -const bool ARCH_64_BIT = $$REGISTER_SIZE == 64; -const bool LIBC = $$COMPILER_LIBC_AVAILABLE; -const bool NO_LIBC = !$$COMPILER_LIBC_AVAILABLE; -const CompilerOptLevel COMPILER_OPT_LEVEL = CompilerOptLevel.from_ordinal($$COMPILER_OPT_LEVEL); -const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN; -const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED; -const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED; -const bool F128_SUPPORT = $$PLATFORM_F128_SUPPORTED; -const REGISTER_SIZE = $$REGISTER_SIZE; -const bool COMPILER_SAFE_MODE = $$COMPILER_SAFE_MODE; -const bool DEBUG_SYMBOLS = $$DEBUG_SYMBOLS; -const bool BACKTRACE = $$BACKTRACE; -const usz LLVM_VERSION = $$LLVM_VERSION; -const bool BENCHMARKING = $$BENCHMARKING; -const bool TESTING = $$TESTING; -const MemoryEnvironment MEMORY_ENV = MemoryEnvironment.from_ordinal($$MEMORY_ENVIRONMENT); -const bool TRACK_MEMORY = DEBUG_SYMBOLS && (COMPILER_SAFE_MODE || TESTING); -const bool X86_64 = ARCH_TYPE == X86_64; -const bool X86 = ARCH_TYPE == X86; -const bool AARCH64 = ARCH_TYPE == AARCH64; -const bool NATIVE_STACKTRACE = LINUX || DARWIN || WIN32; -const bool LINUX = LIBC && OS_TYPE == LINUX; -const bool DARWIN = LIBC && os_is_darwin(); -const bool WIN32 = LIBC && OS_TYPE == WIN32; -const bool POSIX = LIBC && os_is_posix(); -const bool OPENBSD = LIBC && OS_TYPE == OPENBSD; -const bool FREEBSD = LIBC && OS_TYPE == FREEBSD; -const bool NETBSD = LIBC && OS_TYPE == NETBSD; -const bool BSD_FAMILY = env::FREEBSD || env::OPENBSD || env::NETBSD; -const bool WASI = LIBC && OS_TYPE == WASI; -const bool WASM_NOLIBC @builtin = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64; -const bool ADDRESS_SANITIZER = $$ADDRESS_SANITIZER; -const bool MEMORY_SANITIZER = $$MEMORY_SANITIZER; -const bool THREAD_SANITIZER = $$THREAD_SANITIZER; -const bool ANY_SANITIZER = ADDRESS_SANITIZER || MEMORY_SANITIZER || THREAD_SANITIZER; - -macro bool os_is_darwin() @const -{ - $switch (OS_TYPE) - $case IOS: - $case MACOS: - $case TVOS: - $case WATCHOS: - return true; - $default: - return false; - $endswitch -} - -macro bool os_is_posix() @const -{ - $switch (OS_TYPE) - $case IOS: - $case MACOS: - $case NETBSD: - $case LINUX: - $case KFREEBSD: - $case FREEBSD: - $case OPENBSD: - $case SOLARIS: - $case TVOS: - $case WATCHOS: - return true; - $case WIN32: - $case WASI: - $case EMSCRIPTEN: - return false; - $default: - $echo("Assuming non-Posix environment"); - return false; - $endswitch -} - -const BUILTIN_EXPECT_IS_DISABLED = $feature(DISABLE_BUILTIN_EXPECT); -const BUILTIN_PREFETCH_IS_DISABLED = $feature(DISABLE_BUILTIN_PREFETCH); diff --git a/lib7/std/core/mem.c3 b/lib7/std/core/mem.c3 deleted file mode 100644 index 5166995bc..000000000 --- a/lib7/std/core/mem.c3 +++ /dev/null @@ -1,911 +0,0 @@ -// Copyright (c) 2021-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::core::mem; -import std::core::mem::allocator @public; -import std::math; - -const MAX_MEMORY_ALIGNMENT = 0x1000_0000; -const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2; - -macro bool @constant_is_power_of_2($x) @const @private -{ - return $x != 0 && ($x & ($x - 1)) == 0; -} - -<* - Load a vector from memory according to a mask assuming default alignment. - - @param ptr "The pointer address to load from." - @param mask "The mask for the load" - @param passthru "The value to use for non masked values" - @require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match" - @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" - @require passthru.len == mask.len : "Mask and passthru must have the same length" - - @return "A vector with the loaded values where the mask is true, passthru where the mask is false" -*> -macro masked_load(ptr, bool[] mask, passthru) -{ - return $$masked_load(ptr, mask, passthru, 0); -} - -<* - Load a vector from memory according to a mask. - - @param ptr "The pointer address to load from." - @param mask "The mask for the load" - @param passthru "The value to use for non masked values" - @param $alignment "The alignment to assume for the pointer" - - @require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match" - @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" - @require passthru.len == mask.len : "Mask and passthru must have the same length" - @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" - - @return "A vector with the loaded values where the mask is true, passthru where the mask is false" -*> -macro @masked_load_aligned(ptr, bool[] mask, passthru, usz $alignment) -{ - return $$masked_load(ptr, mask, passthru, $alignment); -} - -<* - Load values from a pointer vector, assuming default alignment. - - @param ptrvec "The vector of pointers to load from." - @param mask "The mask for the load" - @param passthru "The value to use for non masked values" - - @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" - @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" - @require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match" - @require passthru.len == mask.len : "Mask and passthru must have the same length" - @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" - - @return "A vector with the loaded values where the mask is true, passthru where the mask is false" -*> -macro gather(ptrvec, bool[] mask, passthru) -{ - return $$gather(ptrvec, mask, passthru, 0); -} - - -<* - Load values from a pointer vector. - - @param ptrvec "The vector of pointers to load from." - @param mask "The mask for the load" - @param passthru "The value to use for non masked values" - @param $alignment "The alignment to assume for the pointers" - - @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" - @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" - @require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match" - @require passthru.len == mask.len : "Mask and passthru must have the same length" - @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" - @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" - - @return "A vector with the loaded values where the mask is true, passthru where the mask is false" -*> -macro @gather_aligned(ptrvec, bool[] mask, passthru, usz $alignment) -{ - return $$gather(ptrvec, mask, passthru, $alignment); -} - - -<* - Store parts of a vector according to the mask, assuming default alignment. - - @param ptr "The pointer address to store to." - @param value "The value to store masked" - @param mask "The mask for the store" - - @require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match" - @require @typekind(value) == VECTOR : "Expected value to be a vector" - @require value.len == mask.len : "Mask and value must have the same length" -*> -macro masked_store(ptr, value, bool[] mask) -{ - return $$masked_store(ptr, value, mask, 0); -} - -<* - @param ptr "The pointer address to store to." - @param value "The value to store masked" - @param mask "The mask for the store" - @param $alignment "The alignment of the pointer" - - @require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match" - @require @typekind(value) == VECTOR : "Expected value to be a vector" - @require value.len == mask.len : "Mask and value must have the same length" - @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" - -*> -macro @masked_store_aligned(ptr, value, bool[] mask, usz $alignment) -{ - return $$masked_store(ptr, value, mask, $alignment); -} - -<* - @param ptrvec "The vector pointer containing the addresses to store to." - @param value "The value to store masked" - @param mask "The mask for the store" - @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" - @require @typekind(value) == VECTOR : "Expected value to be a vector" - @require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match" - @require value.len == mask.len : "Mask and value must have the same length" - @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" - -*> -macro scatter(ptrvec, value, bool[] mask) -{ - return $$scatter(ptrvec, value, mask, 0); -} - -<* - @param ptrvec "The vector pointer containing the addresses to store to." - @param value "The value to store masked" - @param mask "The mask for the store" - @param $alignment "The alignment of the load" - - @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" - @require @typekind(value) == VECTOR : "Expected value to be a vector" - @require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match" - @require value.len == mask.len : "Mask and value must have the same length" - @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" - @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" -*> -macro @scatter_aligned(ptrvec, value, bool[] mask, usz $alignment) -{ - return $$scatter(ptrvec, value, mask, $alignment); -} - -<* - @param #x "The variable or dereferenced pointer to load." - @param $alignment "The alignment to assume for the load" - @return "The value of the variable" - - @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" - @require $defined(&#x) : "This must be a variable or dereferenced pointer" -*> -macro @unaligned_load(#x, usz $alignment) @builtin -{ - return $$unaligned_load(&#x, $alignment); -} - -<* - @param #x "The variable or dereferenced pointer to store to." - @param value "The value to store." - @param $alignment "The alignment to assume for the store" - @return "The value stored" - - @require $defined(&#x) : "This must be a variable or dereferenced pointer" - @require $defined(#x = value) : "The value doesn't match the variable" - @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" -*> -macro @unaligned_store(#x, value, usz $alignment) @builtin -{ - return $$unaligned_store(&#x, ($typeof(#x))value, $alignment); -} - -<* - @param #x "The variable or dereferenced pointer to load." - @return "The value of the variable" - - @require $defined(&#x) : "This must be a variable or dereferenced pointer" -*> -macro @volatile_load(#x) @builtin -{ - return $$volatile_load(&#x); -} - -<* - @param #x "The variable or dereferenced pointer to store to." - @param value "The value to store." - @return "The value stored" - - @require $defined(&#x) : "This must be a variable or dereferenced pointer" - @require $defined(#x = value) : "The value doesn't match the variable" -*> -macro @volatile_store(#x, value) @builtin -{ - return $$volatile_store(&#x, ($typeof(#x))value); -} - -enum AtomicOrdering : int -{ - NOT_ATOMIC, // Not atomic - UNORDERED, // No lock - RELAXED, // Consistent ordering - ACQUIRE, // Barrier locking load/store - RELEASE, // Barrier releasing load/store - ACQUIRE_RELEASE, // Barrier fence to load/store - SEQ_CONSISTENT, // Acquire semantics, ordered with other seq_consistent -} - -<* - @param #x "the variable or dereferenced pointer to load." - @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT" - @param $volatile "whether the load should be volatile, defaults to 'false'" - @return "returns the value of x" - - @require $defined(&#x) : "This must be a variable or dereferenced pointer" - @require $ordering != AtomicOrdering.RELEASE "Release ordering is not valid for load." - @require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for load." - @require types::may_load_atomic($typeof(#x)) "Only integer, float and pointers may be used." -*> -macro @atomic_load(#x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin -{ - return $$atomic_load(&#x, $volatile, $ordering.ordinal); -} - -<* - @param #x "the variable or dereferenced pointer to store to." - @param value "the value to store." - @param $ordering "the atomic ordering of the store, defaults to SEQ_CONSISTENT" - @param $volatile "whether the store should be volatile, defaults to 'false'" - - @require $ordering != AtomicOrdering.ACQUIRE "Acquire ordering is not valid for store." - @require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for store." - @require types::may_load_atomic($typeof(#x)) "Only integer, float and pointers may be used." - @require $defined(&#x) : "This must be a variable or dereferenced pointer" - @require $defined(#x = value) : "The value doesn't match the variable" -*> -macro void @atomic_store(#x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin -{ - $$atomic_store(&#x, value, $volatile, $ordering.ordinal); -} - -<* - @require $success != AtomicOrdering.NOT_ATOMIC && $success != AtomicOrdering.UNORDERED "Acquire ordering is not valid." - @require $failure != AtomicOrdering.RELEASE && $failure != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid." -*> -macro compare_exchange(ptr, compare, value, AtomicOrdering $success = SEQ_CONSISTENT, AtomicOrdering $failure = SEQ_CONSISTENT, bool $volatile = true, bool $weak = false, usz $alignment = 0) -{ - return $$compare_exchange(ptr, compare, value, $volatile, $weak, $success.ordinal, $failure.ordinal, $alignment); -} - -<* - @require $success != AtomicOrdering.NOT_ATOMIC && $success != AtomicOrdering.UNORDERED "Acquire ordering is not valid." - @require $failure != AtomicOrdering.RELEASE && $failure != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid." -*> -macro compare_exchange_volatile(ptr, compare, value, AtomicOrdering $success = SEQ_CONSISTENT, AtomicOrdering $failure = SEQ_CONSISTENT) -{ - return compare_exchange(ptr, compare, value, $success, $failure, true); -} - -<* - @require math::is_power_of_2(alignment) -*> -fn usz aligned_offset(usz offset, usz alignment) -{ - return alignment * ((offset + alignment - 1) / alignment); -} - -macro void* aligned_pointer(void* ptr, usz alignment) -{ - return (void*)(uptr)aligned_offset((uptr)ptr, alignment); -} - -<* - @require math::is_power_of_2(alignment) -*> -fn bool ptr_is_aligned(void* ptr, usz alignment) @inline -{ - return (uptr)ptr & ((uptr)alignment - 1) == 0; -} - -macro void zero_volatile(char[] data) -{ - $$memset(data.ptr, (char)0, data.len, true, (usz)1); -} - -macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false) -{ - $$memset(dst, (char)0, len, $is_volatile, $dst_align); -} - -macro void clear_inline(void* dst, usz $len, usz $dst_align = 0, bool $is_volatile = false) -{ - $$memset_inline(dst, (char)0, $len, $is_volatile, $dst_align); -} - -<* - Copy memory from src to dst efficiently, assuming the memory ranges do not overlap. - - @param [&out] dst "The destination to copy to" - @param [&in] src "The source to copy from" - @param len "The number of bytes to copy" - @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default" - @param $src_align "the alignment of the destination if different from the default, 0 assumes the default" - @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away." - - @require len == 0 || dst + len <= src || src + len <= dst : "Ranges may not overlap" -*> -macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false) -{ - $$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align); -} - -<* - Copy memory from src to dst efficiently, assuming the memory ranges do not overlap, it - will always be inlined and never call memcopy - - @param [&out] dst "The destination to copy to" - @param [&in] src "The source to copy from" - @param $len "The number of bytes to copy" - @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default" - @param $src_align "the alignment of the destination if different from the default, 0 assumes the default" - @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away." - - @require $len == 0 || dst + $len <= src || src + $len <= dst : "Ranges may not overlap" -*> -macro void copy_inline(void* dst, void* src, usz $len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false) -{ - $$memcpy_inline(dst, src, $len, $is_volatile, $dst_align, $src_align); -} - -<* - Copy memory from src to dst but correctly handle the possibility of overlapping ranges. - - @param [&out] dst "The destination to copy to" - @param [&in] src "The source to copy from" - @param len "The number of bytes to copy" - @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default" - @param $src_align "the alignment of the destination if different from the default, 0 assumes the default" - @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away." -*> -macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false) -{ - $$memmove(dst, src, len, $is_volatile, $dst_align, $src_align); -} - -<* - Sets all memory in a region to that of the provided byte. - - @param [&out] dst "The destination to copy to" - @param val "The value to copy into memory" - @param len "The number of bytes to copy" - @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default" - @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away." - - @ensure !len || (dst[0] == val && dst[len - 1] == val) -*> -macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false) -{ - $$memset(dst, val, len, $is_volatile, $dst_align); -} - -<* - Sets all memory in a region to that of the provided byte. Never calls OS memset. - - @param [&out] dst "The destination to copy to" - @param val "The value to copy into memory" - @param $len "The number of bytes to copy" - @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default" - @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away." - - @ensure !$len || (dst[0] == val && dst[$len - 1] == val) -*> -macro void set_inline(void* dst, char val, usz $len, usz $dst_align = 0, bool $is_volatile = false) -{ - $$memset_inline(dst, val, $len, $is_volatile, $dst_align); -} -<* - Test if n elements are equal in a slice, pointed to by a pointer etc. - - @require values::@inner_kind(a) == TypeKind.SLICE || values::@inner_kind(a) == TypeKind.POINTER - @require values::@inner_kind(b) == TypeKind.SLICE || values::@inner_kind(b) == TypeKind.POINTER - @require values::@inner_kind(a) != TypeKind.SLICE || len == -1 - @require values::@inner_kind(a) != TypeKind.POINTER || len > -1 - @require values::@assign_to(a, b) && values::@assign_to(b, a) -*> -macro bool equals(a, b, isz len = -1, usz $align = 0) -{ - $if !$align: - $align = $typeof(a[0]).alignof; - $endif - void* x @noinit; - void* y @noinit; - $if values::@inner_kind(a) == TypeKind.SLICE: - len = a.len; - if (len != b.len) return false; - x = a.ptr; - y = b.ptr; - $else - x = a; - y = b; - assert(len >= 0, "A zero or positive length must be given when comparing pointers."); - $endif - - if (!len) return true; - var $Type; - $switch ($align) - $case 1: - $Type = char; - $case 2: - $Type = ushort; - $case 4: - $Type = uint; - $case 8: - $default: - $Type = ulong; - $endswitch - var $step = $Type.sizeof; - usz end = len / $step; - for (usz i = 0; i < end; i++) - { - if ((($Type*)x)[i] != (($Type*)y)[i]) return false; - } - usz last = len % $align; - for (usz i = len - last; i < len; i++) - { - if (((char*)x)[i] != ((char*)y)[i]) return false; - } - return true; -} - -<* - Check if an allocation must be aligned given the type. - - @return `true if the alignment of the type exceeds DEFAULT_MEM_ALIGNMENT.` -*> -macro bool type_alloc_must_be_aligned($Type) -{ - return $Type.alignof > DEFAULT_MEM_ALIGNMENT; -} - -<* - Run with a specific allocator inside of the macro body. -*> -macro void @scoped(Allocator allocator; @body()) -{ - Allocator old_allocator = allocator::thread_allocator; - allocator::thread_allocator = allocator; - defer allocator::thread_allocator = old_allocator; - @body(); -} - -<* - Run the tracking allocator in the scope, then - print out stats. - - @param $enabled "Set to false to disable tracking" -*> -macro void @report_heap_allocs_in_scope($enabled = true; @body()) -{ - $if $enabled: - TrackingAllocator tracker; - tracker.init(allocator::thread_allocator); - Allocator old_allocator = allocator::thread_allocator; - allocator::thread_allocator = &tracker; - defer - { - allocator::thread_allocator = old_allocator; - tracker.print_report(); - tracker.free(); - } - $endif - @body(); -} - -<* - Assert on memory leak in the scope of the macro body. - - @param $report "Set to false to disable memory report" -*> -macro void @assert_leak($report = true; @body()) @builtin -{ - $if env::DEBUG_SYMBOLS || $feature(MEMORY_ASSERTS): - TrackingAllocator tracker; - tracker.init(allocator::thread_allocator); - Allocator old_allocator = allocator::thread_allocator; - allocator::thread_allocator = &tracker; - defer - { - allocator::thread_allocator = old_allocator; - defer tracker.free(); - usz allocated = tracker.allocated(); - if (allocated) - { - DString report = dstring::new(); - defer report.free(); - $if $report: - report.append_char('\n'); - (void)tracker.fprint_report(&report); - $endif - assert(allocated == 0, "Memory leak detected" - " (%d bytes allocated).%s", - allocated, report.str_view()); - } - } - $endif - @body(); -} - -<* - Allocate [size] bytes on the stack to use for allocation, - with the heap allocator as the backing allocator. - - Release everything on scope exit. - - @param $size `the size of the buffer` -*> -macro void @stack_mem(usz $size; @body(Allocator mem)) @builtin -{ - char[$size] buffer; - OnStackAllocator allocator; - allocator.init(&buffer, allocator::heap()); - defer allocator.free(); - @body(&allocator); -} - -macro void @stack_pool(usz $size; @body) @builtin -{ - char[$size] buffer; - OnStackAllocator allocator; - allocator.init(&buffer, allocator::heap()); - defer allocator.free(); - mem::@scoped(&allocator) - { - @body(); - }; -} - -struct TempState -{ - TempAllocator* old; - TempAllocator* current; - usz mark; -} -<* - Push the current temp allocator. A push must always be balanced with a pop using the current state. -*> -fn TempState temp_push(TempAllocator* other = null) -{ - TempAllocator* current = allocator::temp(); - TempAllocator* old = current; - if (other == current) - { - current = allocator::temp_allocator_next(); - } - return { old, current, current.used }; -} - -<* - Pop the current temp allocator. A pop must always be balanced with a push. -*> -fn void temp_pop(TempState old_state) -{ - assert(allocator::thread_temp_allocator == old_state.current, "Tried to pop temp allocators out of order."); - assert(old_state.current.used >= old_state.mark, "Tried to pop temp allocators out of order."); - old_state.current.reset(old_state.mark); - allocator::thread_temp_allocator = old_state.old; -} - -<* - @require @is_empty_macro_slot(#other_temp) ||| $assignable(#other_temp, Allocator) "Must be an allocator" - *> -macro void @pool(#other_temp = EMPTY_MACRO_SLOT; @body) @builtin -{ - TempAllocator* current = allocator::temp(); - $if @is_valid_macro_slot(#other_temp): - TempAllocator* original = current; - if (current == #other_temp.ptr) current = allocator::temp_allocator_next(); - $endif - usz mark = current.used; - defer - { - current.reset(mark); - $if @is_valid_macro_slot(#other_temp): - allocator::thread_temp_allocator = original; - $endif; - } - @body(); -} - -import libc; - - -module std::core::mem @if(WASM_NOLIBC); -import std::core::mem::allocator @public; -SimpleHeapAllocator wasm_allocator @private; -extern int __heap_base; - -fn void initialize_wasm_mem() @init(1024) @private -{ - allocator::wasm_memory.allocate_block(mem::DEFAULT_MEM_ALIGNMENT)!!; // Give us a valid null. - // Check if we need to move the heap. - uptr start = (uptr)&__heap_base; - if (start > mem::DEFAULT_MEM_ALIGNMENT) allocator::wasm_memory.use = start; - wasm_allocator.init(fn (x) => allocator::wasm_memory.allocate_block(x)); - allocator::thread_allocator = &wasm_allocator; - allocator::temp_base_allocator = &wasm_allocator; - allocator::init_default_temp_allocators(); -} - -module std::core::mem; - - -macro TrackingEnv* get_tracking_env() -{ - $if env::TRACK_MEMORY: - return &&TrackingEnv { $$FILE, $$FUNC, $$LINE }; - $else - return null; - $endif -} - -macro @clone(value) @builtin @nodiscard -{ - return allocator::clone(allocator::heap(), value); -} - -macro @tclone(value) @builtin @nodiscard -{ - return temp_new($typeof(value), value); -} - -fn void* malloc(usz size) @builtin @inline @nodiscard -{ - return allocator::malloc(allocator::heap(), size); -} - -<* - Allocate using an aligned allocation. This is necessary for types with a default memory alignment - exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. -*> -fn void* malloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard -{ - return allocator::malloc_aligned(allocator::heap(), size, alignment)!!; -} - -fn void* tmalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard -{ - if (!size) return null; - return allocator::temp().acquire(size, NO_ZERO, alignment)!!; -} - -<* - @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type" - @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead" -*> -macro new($Type, ...) @nodiscard -{ - $if $vacount == 0: - return ($Type*)calloc($Type.sizeof); - $else - $Type* val = malloc($Type.sizeof); - *val = $vaexpr[0]; - return val; - $endif -} - -<* - @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type" - @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead" -*> -macro new_with_padding($Type, usz padding, ...) @nodiscard -{ - $if $vacount == 0: - return ($Type*)calloc($Type.sizeof + padding); - $else - $Type* val = malloc($Type.sizeof + padding); - *val = $vaexpr[0]; - return val; - $endif -} - -<* - Allocate using an aligned allocation. This is necessary for types with a default memory alignment - exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. - @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type" -*> -macro new_aligned($Type, ...) @nodiscard -{ - $if $vacount == 0: - return ($Type*)calloc_aligned($Type.sizeof, $Type.alignof); - $else - $Type* val = malloc_aligned($Type.sizeof, $Type.alignof); - *val = $vaexpr[0]; - return val; - $endif -} - -<* - @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead" -*> -macro alloc($Type) @nodiscard -{ - return ($Type*)malloc($Type.sizeof); -} - -<* - @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead" -*> -macro alloc_with_padding($Type, usz padding) @nodiscard -{ - return ($Type*)malloc($Type.sizeof + padding); -} - -<* - Allocate using an aligned allocation. This is necessary for types with a default memory alignment - exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. -*> -macro alloc_aligned($Type) @nodiscard -{ - return ($Type*)malloc_aligned($Type.sizeof, $Type.alignof); -} - -<* - @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type" -*> -macro temp_new($Type, ...) @nodiscard -{ - $if $vacount == 0: - return ($Type*)tcalloc($Type.sizeof) @inline; - $else - $Type* val = tmalloc($Type.sizeof) @inline; - *val = $vaexpr[0]; - return val; - $endif -} - -<* - @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type" -*> -macro temp_new_with_padding($Type, usz padding, ...) @nodiscard -{ - $if $vacount == 0: - return ($Type*)tcalloc($Type.sizeof + padding) @inline; - $else - $Type* val = tmalloc($Type.sizeof + padding) @inline; - *val = $vaexpr[0]; - return val; - $endif -} - -macro temp_alloc($Type) @nodiscard -{ - return tmalloc($Type.sizeof); -} - -macro temp_alloc_with_padding($Type, usz padding) @nodiscard -{ - return tmalloc($Type.sizeof + padding); -} - -<* - @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead" -*> -macro new_array($Type, usz elements) @nodiscard -{ - return allocator::new_array(allocator::heap(), $Type, elements); -} - -<* - Allocate using an aligned allocation. This is necessary for types with a default memory alignment - exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. -*> -macro new_array_aligned($Type, usz elements) @nodiscard -{ - return allocator::new_array_aligned(allocator::heap(), $Type, elements); -} - -<* - @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead" -*> -macro alloc_array($Type, usz elements) @nodiscard -{ - return allocator::alloc_array(allocator::heap(), $Type, elements); -} - -<* - Allocate using an aligned allocation. This is necessary for types with a default memory alignment - exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. -*> -macro alloc_array_aligned($Type, usz elements) @nodiscard -{ - return allocator::alloc_array_aligned(allocator::heap(), $Type, elements); -} - -macro temp_alloc_array($Type, usz elements) @nodiscard -{ - return (($Type*)tmalloc($Type.sizeof * elements, $Type.alignof))[:elements]; -} - -macro temp_new_array($Type, usz elements) @nodiscard -{ - return (($Type*)tcalloc($Type.sizeof * elements, $Type.alignof))[:elements]; -} - -fn void* calloc(usz size) @builtin @inline @nodiscard -{ - return allocator::calloc(allocator::heap(), size); -} - -<* - Allocate using an aligned allocation. This is necessary for types with a default memory alignment - exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. -*> -fn void* calloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard -{ - return allocator::calloc_aligned(allocator::heap(), size, alignment)!!; -} - -fn void* tcalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard -{ - if (!size) return null; - return allocator::temp().acquire(size, ZERO, alignment)!!; -} - -fn void* realloc(void *ptr, usz new_size) @builtin @inline @nodiscard -{ - return allocator::realloc(allocator::heap(), ptr, new_size); -} - -fn void* realloc_aligned(void *ptr, usz new_size, usz alignment) @builtin @inline @nodiscard -{ - return allocator::realloc_aligned(allocator::heap(), ptr, new_size, alignment)!!; -} - -fn void free(void* ptr) @builtin @inline -{ - return allocator::free(allocator::heap(), ptr); -} - -fn void free_aligned(void* ptr) @builtin @inline -{ - return allocator::free_aligned(allocator::heap(), ptr); -} - -fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline @nodiscard -{ - if (!size) return null; - if (!ptr) return tmalloc(size, alignment); - return allocator::temp().resize(ptr, size, alignment)!!; -} - -module std::core::mem @if(env::NO_LIBC); - -fn CInt __memcmp(void* s1, void* s2, usz n) @weak @export("memcmp") -{ - char* p1 = s1; - char* p2 = s2; - for (usz i = 0; i < n; i++, p1++, p2++) - { - char c1 = *p1; - char c2 = *p2; - if (c1 < c2) return -1; - if (c1 > c2) return 1; - } - return 0; -} - -fn void* __memset(void* str, CInt c, usz n) @weak @export("memset") -{ - char* p = str; - char cc = (char)c; - for (usz i = 0; i < n; i++, p++) - { - *p = cc; - } - return str; -} - -fn void* __memcpy(void* dst, void* src, usz n) @weak @export("memcpy") -{ - char* d = dst; - char* s = src; - for (usz i = 0; i < n; i++, d++, s++) - { - *d = *s; - } - return dst; -} diff --git a/lib7/std/core/mem_allocator.c3 b/lib7/std/core/mem_allocator.c3 deleted file mode 100644 index 9f832fad4..000000000 --- a/lib7/std/core/mem_allocator.c3 +++ /dev/null @@ -1,464 +0,0 @@ -module std::core::mem::allocator; - -const DEFAULT_SIZE_PREFIX = usz.sizeof; -const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof; - -struct TrackingEnv -{ - String file; - String function; - uint line; -} - -enum AllocInitType -{ - NO_ZERO, - ZERO -} - -interface Allocator -{ - fn void reset(usz mark) @optional; - fn usz mark() @optional; - <* - @require !alignment || math::is_power_of_2(alignment) - @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big` - @require size > 0 - *> - fn void*! acquire(usz size, AllocInitType init_type, usz alignment = 0); - <* - @require !alignment || math::is_power_of_2(alignment) - @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big` - @require ptr != null - @require new_size > 0 - *> - fn void*! resize(void* ptr, usz new_size, usz alignment = 0); - <* - @require ptr != null - *> - fn void release(void* ptr, bool aligned); -} - -def MemoryAllocFn = fn char[]!(usz); - -fault AllocationFailure -{ - OUT_OF_MEMORY, - CHUNK_TOO_LARGE, -} - -fn usz alignment_for_allocation(usz alignment) @inline @private -{ - return alignment < mem::DEFAULT_MEM_ALIGNMENT ? mem::DEFAULT_MEM_ALIGNMENT : alignment; -} - -macro void* malloc(Allocator allocator, usz size) @nodiscard -{ - return malloc_try(allocator, size)!!; -} - -macro void*! malloc_try(Allocator allocator, usz size) @nodiscard -{ - if (!size) return null; - $if env::TESTING: - char* data = allocator.acquire(size, NO_ZERO)!; - mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT); - return data; - $else - return allocator.acquire(size, NO_ZERO); - $endif -} - -macro void* calloc(Allocator allocator, usz size) @nodiscard -{ - return calloc_try(allocator, size)!!; -} - -macro void*! calloc_try(Allocator allocator, usz size) @nodiscard -{ - if (!size) return null; - return allocator.acquire(size, ZERO); -} - -macro void* realloc(Allocator allocator, void* ptr, usz new_size) @nodiscard -{ - return realloc_try(allocator, ptr, new_size)!!; -} - -macro void*! realloc_try(Allocator allocator, void* ptr, usz new_size) @nodiscard -{ - if (!new_size) - { - free(allocator, ptr); - return null; - } - if (!ptr) return allocator.acquire(new_size, NO_ZERO); - return allocator.resize(ptr, new_size); -} - -macro void free(Allocator allocator, void* ptr) -{ - if (!ptr) return; - $if env::TESTING: - ((char*)ptr)[0] = 0xBA; - $endif - allocator.release(ptr, false); -} - -macro void*! malloc_aligned(Allocator allocator, usz size, usz alignment) @nodiscard -{ - if (!size) return null; - $if env::TESTING: - char* data = allocator.acquire(size, NO_ZERO, alignment)!; - mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT); - return data; - $else - return allocator.acquire(size, NO_ZERO, alignment); - $endif -} - -macro void*! calloc_aligned(Allocator allocator, usz size, usz alignment) @nodiscard -{ - if (!size) return null; - return allocator.acquire(size, ZERO, alignment); -} - -macro void*! realloc_aligned(Allocator allocator, void* ptr, usz new_size, usz alignment) @nodiscard -{ - if (!new_size) - { - free_aligned(allocator, ptr); - return null; - } - if (!ptr) - { - return malloc_aligned(allocator, new_size, alignment); - } - return allocator.resize(ptr, new_size, alignment); -} - -macro void free_aligned(Allocator allocator, void* ptr) -{ - if (!ptr) return; - $if env::TESTING: - ((char*)ptr)[0] = 0xBA; - $endif - allocator.release(ptr, aligned: true); -} - -<* - @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead" - @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type" -*> -macro new(Allocator allocator, $Type, ...) @nodiscard -{ - $if $vacount == 0: - return ($Type*)calloc(allocator, $Type.sizeof); - $else - $Type* val = malloc(allocator, $Type.sizeof); - *val = $vaexpr[0]; - return val; - $endif -} - -<* - @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead" - @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type" -*> -macro new_try(Allocator allocator, $Type, ...) @nodiscard -{ - $if $vacount == 0: - return ($Type*)calloc_try(allocator, $Type.sizeof); - $else - $Type* val = malloc_try(allocator, $Type.sizeof)!; - *val = $vaexpr[0]; - return val; - $endif -} - -<* - Allocate using an aligned allocation. This is necessary for types with a default memory alignment - exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. - @require $vacount < 2 : "Too many arguments." - @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type" -*> -macro new_aligned(Allocator allocator, $Type, ...) @nodiscard -{ - $if $vacount == 0: - return ($Type*)calloc_aligned(allocator, $Type.sizeof, $Type.alignof); - $else - $Type* val = malloc_aligned(allocator, $Type.sizeof, $Type.alignof)!; - *val = $vaexpr[0]; - return val; - $endif -} - -<* - @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT -*> -macro new_with_padding(Allocator allocator, $Type, usz padding) @nodiscard -{ - return ($Type*)calloc_try(allocator, $Type.sizeof + padding); -} - -<* - @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead" -*> -macro alloc(Allocator allocator, $Type) @nodiscard -{ - return ($Type*)malloc(allocator, $Type.sizeof); -} - -<* - @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead" -*> -macro alloc_try(Allocator allocator, $Type) @nodiscard -{ - return ($Type*)malloc_try(allocator, $Type.sizeof); -} - -<* - Allocate using an aligned allocation. This is necessary for types with a default memory alignment - exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. -*> -macro alloc_aligned(Allocator allocator, $Type) @nodiscard -{ - return ($Type*)malloc_aligned(allocator, $Type.sizeof, $Type.alignof); -} - -<* - @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT -*> -macro alloc_with_padding(Allocator allocator, $Type, usz padding) @nodiscard -{ - return ($Type*)malloc_try(allocator, $Type.sizeof + padding); -} - -<* - @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead" -*> -macro new_array(Allocator allocator, $Type, usz elements) @nodiscard -{ - return new_array_try(allocator, $Type, elements)!!; -} - -<* - @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead" -*> -macro new_array_try(Allocator allocator, $Type, usz elements) @nodiscard -{ - return (($Type*)calloc_try(allocator, $Type.sizeof * elements))[:elements]; -} - -<* - Allocate using an aligned allocation. This is necessary for types with a default memory alignment - exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. -*> -macro new_array_aligned(Allocator allocator, $Type, usz elements) @nodiscard -{ - return (($Type*)calloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!; -} - -<* - @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead" -*> -macro alloc_array(Allocator allocator, $Type, usz elements) @nodiscard -{ - return alloc_array_try(allocator, $Type, elements)!!; -} - -<* - Allocate using an aligned allocation. This is necessary for types with a default memory alignment - exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. -*> -macro alloc_array_aligned(Allocator allocator, $Type, usz elements) @nodiscard -{ - return (($Type*)malloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!; -} - -<* - @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead" -*> -macro alloc_array_try(Allocator allocator, $Type, usz elements) @nodiscard -{ - return (($Type*)malloc_try(allocator, $Type.sizeof * elements))[:elements]; -} - -macro clone(Allocator allocator, value) @nodiscard -{ - return new(allocator, $typeof(value), value); -} - -fn any clone_any(Allocator allocator, any value) @nodiscard -{ - usz size = value.type.sizeof; - void* data = malloc(allocator, size); - mem::copy(data, value.ptr, size); - return any_make(data, value.type); -} - - -<* - @require bytes > 0 - @require alignment > 0 - @require bytes <= isz.max -*> -macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment) -{ - if (alignment < void*.alignof) alignment = void*.alignof; - usz header = AlignedBlock.sizeof + alignment; - usz alignsize = bytes + header; - $if @typekind(#alloc_fn(bytes)) == OPTIONAL: - void* data = #alloc_fn(alignsize)!; - $else - void* data = #alloc_fn(alignsize); - $endif - void* mem = mem::aligned_pointer(data + AlignedBlock.sizeof, alignment); - AlignedBlock* desc = (AlignedBlock*)mem - 1; - assert(mem > data); - *desc = { bytes, data }; - return mem; -} - -struct AlignedBlock -{ - usz len; - void* start; -} - -macro void! @aligned_free(#free_fn, void* old_pointer) -{ - AlignedBlock* desc = (AlignedBlock*)old_pointer - 1; - $if @typekind(#free_fn(desc.start)) == OPTIONAL: - #free_fn(desc.start)!; - $else - #free_fn(desc.start); - $endif -} - -<* - @require bytes > 0 - @require alignment > 0 -*> -macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment) -{ - AlignedBlock* desc = (AlignedBlock*)old_pointer - 1; - void* data_start = desc.start; - void* new_data = @aligned_alloc(#calloc_fn, bytes, alignment)!; - mem::copy(new_data, old_pointer, desc.len < bytes ? desc.len : bytes, 1, 1); - $if @typekind(#free_fn(data_start)) == OPTIONAL: - #free_fn(data_start)!; - $else - #free_fn(data_start); - $endif - return new_data; -} - - -// All allocators - - -def mem = thread_allocator @builtin; -tlocal Allocator thread_allocator @private = base_allocator(); -Allocator temp_base_allocator @private = base_allocator(); - -tlocal TempAllocator* thread_temp_allocator @private = null; -tlocal TempAllocator*[2] temp_allocator_pair @private; - -macro Allocator base_allocator() @private -{ - $if env::LIBC: - return &allocator::LIBC_ALLOCATOR; - $else - return &allocator::NULL_ALLOCATOR; - $endif -} - -macro TempAllocator* create_default_sized_temp_allocator(Allocator allocator) @local -{ - $switch (env::MEMORY_ENV) - $case NORMAL: - return new_temp_allocator(1024 * 256, allocator)!!; - $case SMALL: - return new_temp_allocator(1024 * 16, allocator)!!; - $case TINY: - return new_temp_allocator(1024 * 2, allocator)!!; - $case NONE: - unreachable("Temp allocator must explicitly created when memory-env is set to 'none'."); - $endswitch -} - -macro Allocator heap() => thread_allocator; - -macro TempAllocator* temp() -{ - if (!thread_temp_allocator) - { - init_default_temp_allocators(); - } - 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); - temp_allocator_pair[1] = create_default_sized_temp_allocator(temp_base_allocator); - thread_temp_allocator = temp_allocator_pair[0]; -} - -fn void destroy_temp_allocators_after_exit() @finalizer(65535) @local @if(env::LIBC) -{ - destroy_temp_allocators(); -} - -<* - Call this to destroy any memory used by the temp allocators. This will invalidate all temp memory. -*> -fn void destroy_temp_allocators() -{ - if (!thread_temp_allocator) return; - temp_allocator_pair[0].destroy(); - temp_allocator_pair[1].destroy(); - temp_allocator_pair[..] = null; - thread_temp_allocator = null; -} - -fn TempAllocator* temp_allocator_next() @private -{ - if (!thread_temp_allocator) - { - init_default_temp_allocators(); - return thread_temp_allocator; - } - usz index = thread_temp_allocator == temp_allocator_pair[0] ? 1 : 0; - return thread_temp_allocator = temp_allocator_pair[index]; -} - -const NullAllocator NULL_ALLOCATOR = {}; -distinct NullAllocator (Allocator) = uptr; - -fn void*! NullAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic -{ - return AllocationFailure.OUT_OF_MEMORY?; -} - -fn void*! NullAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic -{ - return AllocationFailure.OUT_OF_MEMORY?; -} - -fn void NullAllocator.release(&self, void* old_ptr, bool aligned) @dynamic -{ -} - diff --git a/lib7/std/core/os/wasm_memory.c3 b/lib7/std/core/os/wasm_memory.c3 deleted file mode 100644 index bf1db6935..000000000 --- a/lib7/std/core/os/wasm_memory.c3 +++ /dev/null @@ -1,32 +0,0 @@ -module std::core::mem::allocator; - - -const usz WASM_BLOCK_SIZE = 65536; - -WasmMemory wasm_memory; - -struct WasmMemory -{ - usz allocation; - uptr use; -} - -fn char[]! WasmMemory.allocate_block(&self, usz bytes) -{ - if (!self.allocation) - { - self.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE; - } - isz bytes_required = bytes + self.use - self.allocation; - if (bytes_required <= 0) - { - defer self.use += bytes; - return ((char*)self.use)[:bytes]; - } - - usz blocks_required = (bytes_required + WASM_BLOCK_SIZE + 1) / WASM_BLOCK_SIZE; - if ($$wasm_memory_grow(0, blocks_required) == -1) return AllocationFailure.OUT_OF_MEMORY?; - self.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE; - defer self.use += bytes; - return ((char*)self.use)[:bytes]; -} \ No newline at end of file diff --git a/lib7/std/core/private/cpu_detect.c3 b/lib7/std/core/private/cpu_detect.c3 deleted file mode 100644 index 75d20ce77..000000000 --- a/lib7/std/core/private/cpu_detect.c3 +++ /dev/null @@ -1,268 +0,0 @@ -module std::core::cpudetect @if(env::X86 || env::X86_64); - -struct CpuId -{ - uint eax, ebx, ecx, edx; -} -fn CpuId x86_cpuid(uint eax, uint ecx = 0) -{ - int edx; - int ebx; - asm - { - movl $eax, eax; - movl $ecx, ecx; - cpuid; - movl eax, $eax; - movl ebx, $ebx; - movl ecx, $ecx; - movl edx, $edx; - } - return { eax, ebx, ecx, edx }; -} - -enum X86Feature -{ - ADX, - AES, - AMX_AVX512, - AMX_FP8, - AMX_MOVRS, - AMX_TF32, - AMX_TRANSPOSE, - AMX_BF16, - AMX_COMPLEX, - AMX_FP16, - AMX_INT8, - AMX_TILE, - APXF, - AVX, - AVX10_1_256, - AVX10_1_512, - AVX10_2_256, - AVX10_2_512, - AVX2, - AVX5124FMAPS, - AVX5124VNNIW, - AVX512BF16, - AVX512BITALG, - AVX512BW, - AVX512CD, - AVX512DQ, - AVX512ER, - AVX512F, - AVX512FP16, - AVX512IFMA, - AVX512PF, - AVX512VBMI, - AVX512VBMI2, - AVX512VL, - AVX512VNNI, - AVX512VP2INTERSECT, - AVX512VPOPCNTDQ, - AVXIFMA, - AVXNECONVERT, - AVXVNNI, - AVXVNNIINT16, - AVXVNNIINT8, - BMI, - BMI2, - CLDEMOTE, - CLFLUSHOPT, - CLWB, - CLZERO, - CMOV, - CMPCCXADD, - CMPXCHG16B, - CX8, - ENQCMD, - F16C, - FMA, - FMA4, - FSGSBASE, - FXSR, - GFNI, - HRESET, - INVPCID, - KL, - LWP, - LZCNT, - MMX, - MOVBE, - MOVDIR64B, - MOVDIRI, - MOVRS, - MWAITX, - PCLMUL, - PCONFIG, - PKU, - POPCNT, - PREFETCHI, - PREFETCHWT1, - PRFCHW, - PTWRITE, - RAOINT, - RDPID, - RDPRU, - RDRND, - RDSEED, - RTM, - SAHF, - SERIALIZE, - SGX, - SHA, - SHA512, - SHSTK, - SM3, - SM4, - SSE, - SSE2, - SSE3, - SSE4_1, - SSE4_2, - SSE4_A, - SSSE3, - TBM, - TSXLDTRK, - UINTR, - USERMSR, - VAES, - VPCLMULQDQ, - WAITPKG, - WBNOINVD, - WIDEKL, - X87, - XOP, - XSAVE, - XSAVEC, - XSAVEOPT, - XSAVES, -} - -uint128 x86_features; - -fn void add_feature_if_bit(X86Feature feature, uint register, int bit) -{ - if (register & 1U << bit) x86_features |= 1u128 << feature.ordinal; -} - -fn void x86_initialize_cpu_features() -{ - uint max_level = x86_cpuid(0).eax; - CpuId feat = x86_cpuid(1); - CpuId leaf7 = max_level >= 8 ? x86_cpuid(7) : {}; - CpuId leaf7s1 = leaf7.eax >= 1 ? x86_cpuid(7, 1) : {}; - CpuId ext1 = x86_cpuid(0x80000000).eax >= 0x80000001 ? x86_cpuid(0x80000001) : {}; - CpuId ext8 = x86_cpuid(0x80000000).eax >= 0x80000008 ? x86_cpuid(0x80000008) : {}; - CpuId leaf_d = max_level >= 0xd ? x86_cpuid(0xd, 0x1) : {}; - CpuId leaf_14 = max_level >= 0x14 ? x86_cpuid(0x14) : {}; - CpuId leaf_19 = max_level >= 0x19 ? x86_cpuid(0x19) : {}; - CpuId leaf_24 = max_level >= 0x24 ? x86_cpuid(0x24) : {}; - add_feature_if_bit(ADX, leaf7.ebx, 19); - add_feature_if_bit(AES, feat.ecx, 25); - add_feature_if_bit(AMX_BF16, leaf7.edx, 22); - add_feature_if_bit(AMX_COMPLEX, leaf7s1.edx, 8); - add_feature_if_bit(AMX_FP16, leaf7s1.eax, 21); - add_feature_if_bit(AMX_INT8, leaf7.edx, 25); - add_feature_if_bit(AMX_TILE, leaf7.edx, 24); - add_feature_if_bit(APXF, leaf7s1.edx, 21); - add_feature_if_bit(AVX, feat.ecx, 28); - add_feature_if_bit(AVX10_1_256, leaf7s1.edx, 19); - add_feature_if_bit(AVX10_1_512, leaf_24.ebx, 18); - add_feature_if_bit(AVX2, leaf7.ebx, 5); - add_feature_if_bit(AVX5124FMAPS, leaf7.edx, 3); - add_feature_if_bit(AVX5124VNNIW, leaf7.edx, 2); - add_feature_if_bit(AVX512BF16, leaf7s1.eax, 5); - add_feature_if_bit(AVX512BITALG, leaf7.ecx, 12); - add_feature_if_bit(AVX512BW, leaf7.ebx, 30); - add_feature_if_bit(AVX512CD, leaf7.ebx, 28); - add_feature_if_bit(AVX512DQ, leaf7.ebx, 17); - add_feature_if_bit(AVX512ER, leaf7.ebx, 27); - add_feature_if_bit(AVX512F, leaf7.ebx, 16); - add_feature_if_bit(AVX512FP16, leaf7.edx, 23); - add_feature_if_bit(AVX512IFMA, leaf7.ebx, 21); - add_feature_if_bit(AVX512PF, leaf7.ebx, 26); - add_feature_if_bit(AVX512VBMI, leaf7.ecx, 1); - add_feature_if_bit(AVX512VBMI2, leaf7.ecx, 6); - add_feature_if_bit(AVX512VL, leaf7.ebx, 31); - add_feature_if_bit(AVX512VNNI, leaf7.ecx, 11); - add_feature_if_bit(AVX512VP2INTERSECT, leaf7.edx, 8); - add_feature_if_bit(AVX512VPOPCNTDQ, leaf7.ecx, 14); - add_feature_if_bit(AVXIFMA, leaf7s1.eax, 23); - add_feature_if_bit(AVXNECONVERT, leaf7s1.edx, 5); - add_feature_if_bit(AVXVNNI, leaf7s1.eax, 4); - add_feature_if_bit(AVXVNNIINT16, leaf7s1.edx, 10); - add_feature_if_bit(AVXVNNIINT8, leaf7s1.edx, 4); - add_feature_if_bit(BMI, leaf7.ebx, 3); - add_feature_if_bit(BMI2, leaf7.ebx, 8); - add_feature_if_bit(CLDEMOTE, leaf7.ecx, 25); - add_feature_if_bit(CLFLUSHOPT, leaf7.ebx, 23); - add_feature_if_bit(CLWB, leaf7.ebx, 24); - add_feature_if_bit(CLZERO, ext8.ecx, 0); - add_feature_if_bit(CMOV, feat.edx, 15); - add_feature_if_bit(CMPCCXADD, leaf7s1.eax, 7); - add_feature_if_bit(CMPXCHG16B, feat.ecx, 12); - add_feature_if_bit(CX8, feat.edx, 8); - add_feature_if_bit(ENQCMD, leaf7.ecx, 29); - add_feature_if_bit(F16C, feat.ecx, 29); - add_feature_if_bit(FMA, feat.ecx, 12); - add_feature_if_bit(FMA4, ext1.ecx, 16); - add_feature_if_bit(FSGSBASE, leaf7.ebx, 0); - add_feature_if_bit(FXSR, feat.edx, 24); - add_feature_if_bit(GFNI, leaf7.ecx, 8); - add_feature_if_bit(HRESET, leaf7s1.eax, 22); - add_feature_if_bit(INVPCID, leaf7.ebx, 10); - add_feature_if_bit(KL, leaf7.ecx, 23); - add_feature_if_bit(LWP, ext1.ecx, 15); - add_feature_if_bit(LZCNT, ext1.ecx, 5); - add_feature_if_bit(MMX, feat.edx, 23); - add_feature_if_bit(MOVBE, feat.ecx, 22); - add_feature_if_bit(MOVDIR64B, leaf7.ecx, 28); - add_feature_if_bit(MOVDIRI, leaf7.ecx, 27); - add_feature_if_bit(MWAITX, ext1.ecx, 29); - add_feature_if_bit(PCLMUL, feat.ecx, 1); - add_feature_if_bit(PCONFIG, leaf7.edx, 18); - add_feature_if_bit(PKU, leaf7.ecx, 4); - add_feature_if_bit(POPCNT, feat.ecx, 23); - add_feature_if_bit(PREFETCHI, leaf7s1.edx, 14); - add_feature_if_bit(PREFETCHWT1, leaf7.ecx, 0); - add_feature_if_bit(PRFCHW, ext1.ecx, 8); - add_feature_if_bit(PTWRITE, leaf_14.ebx, 4); - add_feature_if_bit(RAOINT, leaf7s1.eax, 3); - add_feature_if_bit(RDPID, leaf7.ecx, 22); - add_feature_if_bit(RDPRU, ext8.ecx, 4); - add_feature_if_bit(RDRND, feat.ecx, 30); - add_feature_if_bit(RDSEED, leaf7.ebx, 18); - add_feature_if_bit(RTM, leaf7.ebx, 11); - add_feature_if_bit(SAHF, ext1.ecx, 0); - add_feature_if_bit(SERIALIZE, leaf7.edx, 14); - add_feature_if_bit(SGX, leaf7.ebx, 2); - add_feature_if_bit(SHA, leaf7.ebx, 29); - add_feature_if_bit(SHA512, leaf7s1.eax, 0); - add_feature_if_bit(SHSTK, leaf7.ecx, 7); - add_feature_if_bit(SM3, leaf7s1.eax, 1); - add_feature_if_bit(SM4, leaf7s1.eax, 2); - add_feature_if_bit(SSE, feat.edx, 25); - add_feature_if_bit(SSE2, feat.edx, 26); - add_feature_if_bit(SSE3, feat.ecx, 0); - add_feature_if_bit(SSE4_1, feat.ecx, 19); - add_feature_if_bit(SSE4_2, feat.ecx, 20); - add_feature_if_bit(SSE4_A, ext1.ecx, 6); - add_feature_if_bit(SSSE3, feat.ecx, 9); - add_feature_if_bit(TBM, ext1.ecx, 21); - add_feature_if_bit(TSXLDTRK, leaf7.edx, 16); - add_feature_if_bit(UINTR, leaf7.edx, 5); - add_feature_if_bit(USERMSR, leaf7s1.edx, 15); - add_feature_if_bit(VAES, leaf7.ecx, 9); - add_feature_if_bit(VPCLMULQDQ, leaf7.ecx, 10); - add_feature_if_bit(WAITPKG, leaf7.ecx, 5); - add_feature_if_bit(WBNOINVD, ext8.ecx, 9); - add_feature_if_bit(WIDEKL, leaf_19.ebx, 2); - add_feature_if_bit(X87, feat.edx, 0); - add_feature_if_bit(XOP, ext1.ecx, 11); - add_feature_if_bit(XSAVE, feat.ecx, 26); - add_feature_if_bit(XSAVEC, leaf_d.eax, 1); - add_feature_if_bit(XSAVEOPT, leaf_d.eax, 0); - add_feature_if_bit(XSAVES, leaf_d.eax, 3); - -} diff --git a/lib7/std/core/private/macho_runtime.c3 b/lib7/std/core/private/macho_runtime.c3 deleted file mode 100644 index d21b9ff46..000000000 --- a/lib7/std/core/private/macho_runtime.c3 +++ /dev/null @@ -1,254 +0,0 @@ -module std::core::machoruntime @if(env::DARWIN) @private; - -struct SegmentCommand64 -{ - uint cmd; - uint cmdsize; - char[16] segname; - ulong vmaddr; - ulong vmsize; - ulong fileoff; - ulong filesize; - uint maxprot; - uint initprot; - uint nsects; - uint flags; -} - -struct LoadCommand -{ - uint cmd; - uint cmdsize; -} - -struct Section64 -{ - char[16] sectname; - char[16] segname; - ulong addr; - ulong size; - uint offset; - uint align; - uint reloff; - uint nreloc; - uint flags; - uint reserved1; - uint reserved2; - uint reserved3; -} - -struct MachHeader -{ - uint magic; - uint cputype; - uint cpusubtype; - uint filetype; - uint ncmds; - uint sizeofcmds; - uint flags; -} - -struct MachHeader64 -{ - inline MachHeader header; - uint reserved; -} - -const LC_SEGMENT_64 = 0x19; - -fault MachoSearch -{ - NOT_FOUND -} -fn bool name_cmp(char* a, char[16]* b) -{ - for (usz i = 0; i < 16; i++) - { - if (a[i] != (*b)[i]) return false; - if (a[i] == '\0') return true; - } - return false; -} - -fn SegmentCommand64*! find_segment(MachHeader* header, char* segname) -{ - LoadCommand* command = (void*)header + MachHeader64.sizeof; - for (uint i = 0; i < header.ncmds; i++) - { - if (command.cmd == LC_SEGMENT_64) - { - SegmentCommand64* segment = (SegmentCommand64*)command; - if (name_cmp(segname, &segment.segname)) return segment; - } - command = (void*)command + command.cmdsize; - } - return MachoSearch.NOT_FOUND?; -} -fn Section64*! find_section(SegmentCommand64* command, char* sectname) -{ - Section64* section = (void*)command + SegmentCommand64.sizeof; - for (uint i = 0; i < command.nsects; i++) - { - if (name_cmp(sectname, §ion.sectname)) return section; - section++; - } - return MachoSearch.NOT_FOUND?; -} - -macro find_segment_section_body(MachHeader* header, char* segname, char* sectname, $Type) -{ - - Section64*! section = find_section(find_segment(header, segname), sectname); - if (catch section) - { - return ($Type[]){}; - } - $Type* ptr = (void*)header + section.offset; - return ptr[:section.size / $Type.sizeof]; -} - -def DyldCallback = fn void (MachHeader* mh, isz vmaddr_slide); - -extern fn void _dyld_register_func_for_add_image(DyldCallback); - - -struct DlInfo -{ - char* dli_fname; - void* dli_fbase; - char* dli_sname; - void* dli_saddr; -} - -extern fn void printf(char*, ...); -extern fn int dladdr(MachHeader* mh, DlInfo* dlinfo); -extern fn void* realloc(void* ptr, usz size); -extern fn void* malloc(usz size); -extern fn void free(void* ptr); - -def CallbackFn = fn void(); -struct Callback -{ - uint priority; - CallbackFn xtor; - Callback* next; -} -struct DynamicMethod -{ - void* fn_ptr; - char* sel; - union - { - DynamicMethod* next; - TypeId* type; - } -} - -enum StartupState -{ - NOT_STARTED, - INIT, - RUN_CTORS, - READ_DYLIB, - RUN_DYLIB_CTORS, - RUN_DTORS, - SHUTDOWN -} - -StartupState runtime_state = NOT_STARTED; - -Callback* ctor_first; -Callback* dtor_first; - -fn void runtime_startup() @public @export("__c3_runtime_startup") -{ - if (runtime_state != NOT_STARTED) return; - runtime_state = INIT; - _dyld_register_func_for_add_image(&dl_reg_callback); - assert(runtime_state == INIT); - runtime_state = RUN_CTORS; - Callback* ctor = ctor_first; - while (ctor) - { - ctor.xtor(); - ctor = ctor.next; - } - assert(runtime_state == RUN_CTORS); - runtime_state = READ_DYLIB; - ctor_first = null; -} - -fn void runtime_finalize() @public @export("__c3_runtime_finalize") -{ - if (runtime_state != READ_DYLIB) return; - runtime_state = RUN_DTORS; - Callback* dtor = dtor_first; - while (dtor) - { - dtor.xtor(); - dtor = dtor.next; - } - assert(runtime_state == RUN_DTORS); - runtime_state = SHUTDOWN; -} - -fn void append_xxlizer(Callback** ref, Callback* cb) -{ - while (Callback* current = *ref, current) - { - if (current.priority > cb.priority) - { - cb.next = current; - break; - } - ref = ¤t.next; - } - *ref = cb; -} - -struct TypeId -{ - char type; - TypeId* parentof; - DynamicMethod* dtable; - usz sizeof; - TypeId* inner; - usz len; - typeid[?] additional; -} - -fn void dl_reg_callback(MachHeader* mh, isz vmaddr_slide) -{ - usz size = 0; - assert(runtime_state == INIT || runtime_state == READ_DYLIB, "State was %s", runtime_state); - foreach (&dm : find_segment_section_body(mh, "__DATA", "__c3_dynamic", DynamicMethod)) - { - TypeId* type = dm.type; - dm.next = type.dtable; - type.dtable = dm; - DynamicMethod* m = dm; - while (m) - { - m = m.next; - } - } - foreach (&cb : find_segment_section_body(mh, "__DATA", "__c3dtor", Callback)) - { - append_xxlizer(&dtor_first, cb); - } - foreach (&cb : find_segment_section_body(mh, "__DATA", "__c3ctor", Callback)) - { - append_xxlizer(&ctor_first, cb); - } - if (runtime_state != READ_DYLIB) return; - runtime_state = RUN_DYLIB_CTORS; - Callback* ctor = ctor_first; - ctor_first = null; - while (ctor) - { - ctor.xtor(); - ctor = ctor.next; - } - assert(runtime_state == RUN_DYLIB_CTORS); - runtime_state = READ_DYLIB; -} diff --git a/lib7/std/core/private/main_stub.c3 b/lib7/std/core/private/main_stub.c3 deleted file mode 100644 index 1cdf2d4f8..000000000 --- a/lib7/std/core/private/main_stub.c3 +++ /dev/null @@ -1,180 +0,0 @@ -module std::core::main_stub; - -macro usz _strlen(ptr) @private -{ - usz len = 0; - while (ptr[len]) len++; - return len; -} - -macro int @main_to_err_main(#m, int, char**) -{ - if (catch #m()) return 1; - return 0; -} -macro int @main_to_int_main(#m, int, char**) => #m(); -macro int @main_to_void_main(#m, int, char**) -{ - #m(); - return 0; -} - -macro String[] args_to_strings(int argc, char** argv) @private -{ - String[] list = mem::alloc_array(String, argc); - for (int i = 0; i < argc; i++) - { - char* arg = argv[i]; - usz len = 0; - list[i] = (String)arg[:_strlen(arg)]; - } - return list; -} - - -macro int @main_to_err_main_args(#m, int argc, char** argv) -{ - String[] list = args_to_strings(argc, argv); - defer free(list.ptr); - if (catch #m(list)) return 1; - return 0; -} - -macro int @main_to_int_main_args(#m, int argc, char** argv) -{ - String[] list = args_to_strings(argc, argv); - defer free(list.ptr); - return #m(list); -} - -macro int @_main_runner(#m, int argc, char** argv) -{ - String[] list = args_to_strings(argc, argv); - defer free(list.ptr); - return #m(list) ? 0 : 1; -} - -macro int @main_to_void_main_args(#m, int argc, char** argv) -{ - String[] list = args_to_strings(argc, argv); - defer free(list.ptr); - #m(list); - return 0; -} - -module std::core::main_stub @if(env::WIN32); - -extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @extern("CommandLineToArgvW"); - -macro String[] win_command_line_to_strings(ushort* cmd_line) @private -{ - int argc; - Char16** argv = _win_command_line_to_argv_w(cmd_line, &argc); - return wargs_strings(argc, argv); -} - -macro String[] wargs_strings(int argc, Char16** argv) @private -{ - String[] list = mem::alloc_array(String, argc); - for (int i = 0; i < argc; i++) - { - Char16* arg = argv[i]; - Char16[] argstring = arg[:_strlen(arg)]; - list[i] = string::new_from_utf16(mem, argstring) ?? "?".copy(mem); - } - return list[:argc]; -} - -macro void release_wargs(String[] list) @private -{ - foreach (s : list) free(s.ptr); - free(list.ptr); -} - -macro int @win_to_err_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) -{ - if (catch #m()) return 1; - return 0; -} -macro int @win_to_int_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) => #m(); -macro int @win_to_void_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) -{ - #m(); - return 0; -} - -macro int @win_to_err_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) -{ - String[] args = win_command_line_to_strings(cmd_line); - defer release_wargs(args); - if (catch #m(args)) return 1; - return 0; -} - -macro int @win_to_int_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) -{ - String[] args = win_command_line_to_strings(cmd_line); - defer release_wargs(args); - return #m(args); -} - -macro int @win_to_void_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) -{ - String[] args = win_command_line_to_strings(cmd_line); - defer release_wargs(args); - #m(args); - return 0; -} - -macro int @win_to_err_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) -{ - String[] args = win_command_line_to_strings(cmd_line); - defer release_wargs(args); - if (catch #m(handle, prev_handle, args, show_cmd)) return 1; - return 0; -} - -macro int @win_to_int_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) -{ - String[] args = win_command_line_to_strings(cmd_line); - defer release_wargs(args); - return #m(handle, prev_handle, args, show_cmd); -} - -macro int @win_to_void_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) -{ - String[] args = win_command_line_to_strings(cmd_line); - defer release_wargs(args); - #m(handle, prev_handle, args, show_cmd); - return 0; -} - -macro int @wmain_to_err_main_args(#m, int argc, Char16** argv) -{ - String[] args = wargs_strings(argc, argv); - defer release_wargs(args); - if (catch #m(args)) return 1; - return 1; -} - -macro int @wmain_to_int_main_args(#m, int argc, Char16** argv) -{ - String[] args = wargs_strings(argc, argv); - defer release_wargs(args); - return #m(args); -} - -macro int @_wmain_runner(#m, int argc, Char16** argv) -{ - String[] args = wargs_strings(argc, argv); - defer release_wargs(args); - return #m(args) ? 0 : 1; -} - -macro int @wmain_to_void_main_args(#m, int argc, Char16** argv) -{ - String[] args = wargs_strings(argc, argv); - defer release_wargs(args); - #m(args); - return 0; -} diff --git a/lib7/std/core/runtime.c3 b/lib7/std/core/runtime.c3 deleted file mode 100644 index 65852328a..000000000 --- a/lib7/std/core/runtime.c3 +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2021 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::core::runtime; -import libc, std::time, std::io, std::sort; - -struct ReflectedParam (Printable) @if(!$defined(ReflectedParam)) -{ - String name; - typeid type; -} - -struct AnyRaw -{ - void* ptr; - typeid type; -} - -struct SliceRaw -{ - void* ptr; - usz len; -} - - -module std::core::runtime @if(WASM_NOLIBC); - -extern fn void __wasm_call_ctors(); -fn void wasm_initialize() @extern("_initialize") @wasm -{ - // The linker synthesizes this to call constructors. - __wasm_call_ctors(); -} \ No newline at end of file diff --git a/lib7/std/core/runtime_benchmark.c3 b/lib7/std/core/runtime_benchmark.c3 deleted file mode 100644 index f60a25b7b..000000000 --- a/lib7/std/core/runtime_benchmark.c3 +++ /dev/null @@ -1,174 +0,0 @@ -module std::core::runtime; -import libc, std::time, std::io, std::sort; - -def BenchmarkFn = fn void!() @if($$OLD_TEST); -def BenchmarkFn = fn void() @if(!$$OLD_TEST); - -struct BenchmarkUnit -{ - String name; - BenchmarkFn func; -} - -fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator) -{ - BenchmarkFn[] fns = $$BENCHMARK_FNS; - String[] names = $$BENCHMARK_NAMES; - BenchmarkUnit[] benchmarks = allocator::alloc_array(allocator, BenchmarkUnit, names.len); - foreach (i, benchmark : fns) - { - benchmarks[i] = { names[i], fns[i] }; - } - return benchmarks; -} - -const DEFAULT_BENCHMARK_WARMUP_ITERATIONS = 3; -const DEFAULT_BENCHMARK_MAX_ITERATIONS = 10000; - -uint benchmark_warmup_iterations @private = DEFAULT_BENCHMARK_WARMUP_ITERATIONS; -uint benchmark_max_iterations @private = DEFAULT_BENCHMARK_MAX_ITERATIONS; - -fn void set_benchmark_warmup_iterations(uint value) @builtin -{ - benchmark_warmup_iterations = value; -} - -fn void set_benchmark_max_iterations(uint value) @builtin -{ - assert(value > 0); - benchmark_max_iterations = value; -} - -fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if($$OLD_TEST) -{ - int benchmarks_passed = 0; - int benchmark_count = benchmarks.len; - usz max_name; - - foreach (&unit : benchmarks) - { - if (max_name < unit.name.len) max_name = unit.name.len; - } - - usz len = max_name + 9; - - DString name = dstring::temp_with_capacity(64); - name.append_repeat('-', len / 2); - name.append(" BENCHMARKS "); - name.append_repeat('-', len - len / 2); - - io::printn(name); - - name.clear(); - - long sys_clock_started; - long sys_clock_finished; - long sys_clocks; - Clock clock; - anyfault err; - - foreach(unit : benchmarks) - { - defer name.clear(); - name.appendf("Benchmarking %s ", unit.name); - name.append_repeat('.', max_name - unit.name.len + 2); - io::printf("%s ", name.str_view()); - - for (uint i = 0; i < benchmark_warmup_iterations; i++) - { - err = @catch(unit.func()) @inline; - @volatile_load(err); - } - - clock = std::time::clock::now(); - sys_clock_started = $$sysclock(); - - for (uint i = 0; i < benchmark_max_iterations; i++) - { - err = @catch(unit.func()) @inline; - @volatile_load(err); - } - - sys_clock_finished = $$sysclock(); - NanoDuration nano_seconds = clock.mark(); - sys_clocks = sys_clock_finished - sys_clock_started; - - if (err) - { - io::printfn("[failed] Failed due to: %s", err); - continue; - } - - io::printfn("[ok] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations); - benchmarks_passed++; - } - - io::printfn("\n%d benchmark%s run.\n", benchmark_count, benchmark_count > 1 ? "s" : ""); - io::printfn("Benchmarks Result: %s. %d passed, %d failed.", - benchmarks_passed < benchmark_count ? "FAILED" : "ok", - benchmarks_passed, - benchmark_count - benchmarks_passed); - - return benchmark_count == benchmarks_passed; -} - -fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if(!$$OLD_TEST) -{ - usz max_name; - - foreach (&unit : benchmarks) - { - if (max_name < unit.name.len) max_name = unit.name.len; - } - - usz len = max_name + 9; - - DString name = dstring::temp_with_capacity(64); - name.append_repeat('-', len / 2); - name.append(" BENCHMARKS "); - name.append_repeat('-', len - len / 2); - - io::printn(name); - - name.clear(); - - long sys_clock_started; - long sys_clock_finished; - long sys_clocks; - Clock clock; - - foreach(unit : benchmarks) - { - defer name.clear(); - name.appendf("Benchmarking %s ", unit.name); - name.append_repeat('.', max_name - unit.name.len + 2); - io::printf("%s ", name.str_view()); - - for (uint i = 0; i < benchmark_warmup_iterations; i++) - { - unit.func() @inline; - } - - clock = std::time::clock::now(); - sys_clock_started = $$sysclock(); - - for (uint i = 0; i < benchmark_max_iterations; i++) - { - unit.func() @inline; - } - - sys_clock_finished = $$sysclock(); - NanoDuration nano_seconds = clock.mark(); - sys_clocks = sys_clock_finished - sys_clock_started; - - io::printfn("[COMPLETE] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations); - } - - io::printfn("\n%d benchmark%s run.\n", benchmarks.len, benchmarks.len > 1 ? "s" : ""); - return true; -} - -fn bool default_benchmark_runner(String[] args) => @pool() -{ - return run_benchmarks(benchmark_collection_create(tmem())); -} diff --git a/lib7/std/core/runtime_test.c3 b/lib7/std/core/runtime_test.c3 deleted file mode 100644 index 77bff489a..000000000 --- a/lib7/std/core/runtime_test.c3 +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright (c) 2025 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::core::runtime; -import std::core::test @public; -import std::core::mem::allocator @public; -import libc, std::time, std::io, std::sort; -import std::os::env; - -def TestFn = fn void!() @if($$OLD_TEST); -def TestFn = fn void() @if(!$$OLD_TEST); - -TestContext* test_context @private; - -struct TestContext -{ - JmpBuf buf; - // Allows filtering test cased or modules by substring, e.g. 'foo::', 'foo::test_add' - String test_filter; - // Triggers debugger breakpoint when assert or test:: checks failed - bool breakpoint_on_assert; - - // internal state - bool assert_print_backtrace; - bool has_ansi_codes; - bool is_in_panic; - bool is_quiet_mode; - bool is_no_capture; - String current_test_name; - TestFn setup_fn; - TestFn teardown_fn; - - char* error_buffer; - usz error_buffer_capacity; - File fake_stdout; - struct stored - { - File stdout; - File stderr; - Allocator allocator; - } -} - -struct TestUnit -{ - String name; - TestFn func; -} - -fn TestUnit[] test_collection_create(Allocator allocator) -{ - TestFn[] fns = $$TEST_FNS; - String[] names = $$TEST_NAMES; - TestUnit[] tests = allocator::alloc_array(allocator, TestUnit, names.len); - foreach (i, test : fns) - { - tests[i] = { names[i], fns[i] }; - } - return tests; -} - -// Sort the tests by their name in ascending order. -fn int cmp_test_unit(TestUnit a, TestUnit b) -{ - usz an = a.name.len; - usz bn = b.name.len; - if (an > bn) @swap(a, b); - foreach (i, ac : a.name) - { - char bc = b.name[i]; - if (ac != bc) return an > bn ? bc - ac : ac - bc; - } - return (int)(an - bn); -} - -fn bool terminal_has_ansi_codes() @local => @pool() -{ - - if (try v = env::tget_var("TERM")) - { - if (v.contains("xterm") || v.contains("vt100") || v.contains("screen")) return true; - } - $if env::WIN32 || env::NO_LIBC: - return false; - $else - return io::stdout().isatty(); - $endif -} - -fn void test_panic(String message, String file, String function, uint line) @local -{ - if (test_context.is_in_panic) return; - test_context.is_in_panic = true; - - unmute_output(true); - (void)io::stdout().flush(); - if (test_context.assert_print_backtrace) - { - $if env::NATIVE_STACKTRACE: - builtin::print_backtrace(message, 0); - $endif - } - io::printf("\nTest failed ^^^ ( %s:%s ) %s\n", file, line, message); - test_context.assert_print_backtrace = true; - - if (test_context.breakpoint_on_assert) - { - breakpoint(); - } - - if (test_context.teardown_fn) - { - test_context.teardown_fn(); - } - - test_context.is_in_panic = false; - allocator::thread_allocator = test_context.stored.allocator; - libc::longjmp(&test_context.buf, 1); -} - -fn void mute_output() @local -{ - if (test_context.is_no_capture || !test_context.fake_stdout.file) return; - File* stdout = io::stdout(); - File* stderr = io::stderr(); - *stderr = test_context.fake_stdout; - *stdout = test_context.fake_stdout; - (void)test_context.fake_stdout.seek(0, Seek.SET)!!; -} - -fn void unmute_output(bool has_error) @local -{ - if (test_context.is_no_capture || !test_context.fake_stdout.file) return; - - File* stdout = io::stdout(); - File* stderr = io::stderr(); - - *stderr = test_context.stored.stderr; - *stdout = test_context.stored.stdout; - - usz log_size = test_context.fake_stdout.seek(0, Seek.CURSOR)!!; - if (has_error) - { - io::printf("\nTesting %s ", test_context.current_test_name); - io::printn(test_context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]"); - } - - if (has_error && log_size > 0) - { - test_context.fake_stdout.write_byte('\n')!!; - test_context.fake_stdout.write_byte('\0')!!; - (void)test_context.fake_stdout.seek(0, Seek.SET)!!; - - io::printfn("\n========== TEST LOG ============"); - io::printfn("%s\n", test_context.current_test_name); - while (try c = test_context.fake_stdout.read_byte()) - { - if (@unlikely(c == '\0')) - { - // ignore junk from previous tests - break; - } - libc::putchar(c); - } - io::printf("========== TEST END ============"); - } - (void)stdout.flush(); -} - -fn bool run_tests(String[] args, TestUnit[] tests) @private -{ - usz max_name; - bool sort_tests = true; - bool check_leaks = true; - foreach (&unit : tests) - { - if (max_name < unit.name.len) max_name = unit.name.len; - } - TestContext context = - { - .assert_print_backtrace = true, - .breakpoint_on_assert = false, - .test_filter = "", - .has_ansi_codes = terminal_has_ansi_codes(), - .stored.allocator = allocator::heap(), - .stored.stderr = *io::stderr(), - .stored.stdout = *io::stdout(), - }; - for (int i = 1; i < args.len; i++) - { - switch (args[i]) - { - case "--test-breakpoint": - context.breakpoint_on_assert = true; - case "--test-nosort": - sort_tests = false; - case "--test-noleak": - check_leaks = false; - case "--test-nocapture": - context.is_no_capture = true; - case "--noansi": - context.has_ansi_codes = false; - case "--useansi": - context.has_ansi_codes = true; - case "--test-quiet": - context.is_quiet_mode = true; - case "--test-filter": - if (i == args.len - 1) - { - io::printn("Invalid arguments to test runner."); - return false; - } - context.test_filter = args[i + 1]; - i++; - default: - io::printfn("Unknown argument: %s", args[i]); - } - } - test_context = &context; - - if (sort_tests) - { - quicksort(tests, &cmp_test_unit); - } - - // Buffer for hijacking the output - $if (!env::NO_LIBC): - context.fake_stdout.file = libc::tmpfile(); - $endif - if (context.fake_stdout.file == null) - { - io::print("Failed to hijack stdout, tests will print everything"); - } - - PanicFn old_panic = builtin::panic; - defer builtin::panic = old_panic; - builtin::panic = &test_panic; - int tests_passed = 0; - int tests_skipped = 0; - int test_count = tests.len; - DString name = dstring::temp_with_capacity(64); - usz len = max_name + 9; - name.append_repeat('-', len / 2); - name.append(" TESTS "); - name.append_repeat('-', len - len / 2); - if (!context.is_quiet_mode) io::printn(name); - name.clear(); - TempState temp_state = mem::temp_push(); - defer mem::temp_pop(temp_state); - foreach(unit : tests) - { - mem::temp_pop(temp_state); - if (context.test_filter && !unit.name.contains(context.test_filter)) - { - tests_skipped++; - continue; - } - context.setup_fn = null; - context.teardown_fn = null; - context.current_test_name = unit.name; - - defer name.clear(); - name.appendf("Testing %s ", unit.name); - name.append_repeat('.', max_name - unit.name.len + 2); - if (context.is_quiet_mode) - { - io::print("."); - } - else - { - io::printf("%s ", name.str_view()); - } - (void)io::stdout().flush(); - TrackingAllocator mem; - - mem.init(context.stored.allocator); - if (libc::setjmp(&context.buf) == 0) - { - mute_output(); - mem.clear(); - if (check_leaks) allocator::thread_allocator = &mem; - $if(!$$OLD_TEST): - unit.func(); - $else - if (catch err = unit.func()) - { - io::printf("[FAIL] Failed due to: %s", err); - continue; - } - $endif - // track cleanup that may take place in teardown_fn - if (context.teardown_fn) - { - context.teardown_fn(); - } - if (check_leaks) allocator::thread_allocator = context.stored.allocator; - - unmute_output(false); // all good, discard output - if (mem.has_leaks()) - { - if (context.is_quiet_mode) io::printf("\n%s ", context.current_test_name); - io::print(context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]"); - io::printn(" LEAKS DETECTED!"); - mem.print_report(); - } - else - { - if (!context.is_quiet_mode) - { - io::printfn(context.has_ansi_codes ? "[\e[0;32mPASS\e[0m]" : "[PASS]"); - } - tests_passed++; - } - } - mem.free(); - } - io::printfn("\n%d test%s run.\n", test_count-tests_skipped, test_count > 1 ? "s" : ""); - - int n_failed = test_count - tests_passed - tests_skipped; - io::printf("Test Result: %s%s%s: ", - context.has_ansi_codes ? (n_failed ? "\e[0;31m" : "\e[0;32m") : "", - n_failed ? "FAILED" : "PASSED", - context.has_ansi_codes ? "\e[0m" : "", - ); - - io::printfn("%d passed, %d failed, %d skipped.", - tests_passed, - n_failed, - tests_skipped); - - // cleanup fake_stdout file - if (context.fake_stdout.file) libc::fclose(context.fake_stdout.file); - context.fake_stdout.file = null; - - return n_failed == 0; -} - -fn bool default_test_runner(String[] args) => @pool() -{ - assert(test_context == null, "test suite is already running"); - return run_tests(args, test_collection_create(tmem())); -} - diff --git a/lib7/std/core/sanitizer/asan.c3 b/lib7/std/core/sanitizer/asan.c3 deleted file mode 100644 index 49986536f..000000000 --- a/lib7/std/core/sanitizer/asan.c3 +++ /dev/null @@ -1,127 +0,0 @@ -// Add this to your code to suppress leak detection or set other default options -// fn ZString __asan_default_options() @export("__asan_default_options") @if(env::ADDRESS_SANITIZER) -// { -// return "detect_leaks=0"; -// } - -// Add this to break on error -// asan::set_error_report_callback(fn (ZString err) -// { -// breakpoint(); -// }); - -module std::core::sanitizer::asan; - -def ErrorCallback = fn void (ZString); - -<* - Marks a memory region ([addr, addr+size)) as unaddressable. - - This memory must be previously allocated by your program. Instrumented - code is forbidden from accessing addresses in this region until it is - unpoisoned. This function is not guaranteed to poison the entire region - - it could poison only a subregion of [addr, addr+size) due to ASan - alignment restrictions. - - NOTE This function is not thread-safe because no two threads can poison or - unpoison memory in the same memory region simultaneously. - - @param addr "Start of memory region." - @param size "Size of memory region." -*> -macro poison_memory_region(void* addr, usz size) -{ - $if env::ADDRESS_SANITIZER: - __asan_poison_memory_region(addr, size); - $endif -} - -<* - Marks a memory region ([addr, addr+size)) as addressable. - - This memory must be previously allocated by your program. Accessing - addresses in this region is allowed until this region is poisoned again. - This function could unpoison a super-region of [addr, addr+size) due - to ASan alignment restrictions. - - NOTE This function is not thread-safe because no two threads can - poison or unpoison memory in the same memory region simultaneously. - - @param addr "Start of memory region." - @param size "Size of memory region." -*> -macro unpoison_memory_region(void* addr, usz size) -{ - $if env::ADDRESS_SANITIZER: - __asan_unpoison_memory_region(addr, size); - $endif -} - -<* - Checks if an address is poisoned. - @return "True if 'addr' is poisoned (that is, 1-byte read/write access to this address would result in an error report from ASan). Otherwise returns false." - @param addr "Address to check." -*> -macro bool address_is_poisoned(void* addr) -{ - $if env::ADDRESS_SANITIZER: - return (bool)__asan_address_is_poisoned(addr); - $else - return false; - $endif -} - -<* - Checks if a region is poisoned. - - If at least one byte in [beg, beg+size) is poisoned, returns the - address of the first such byte. Otherwise returns 0. - - @param beg "Start of memory region." - @param size "Start of memory region." - @return "Address of first poisoned byte." -*> -macro void* region_is_poisoned(void* beg, usz size) -{ - $if env::ADDRESS_SANITIZER: - return __asan_region_is_poisoned(beg, size); - $else - return null; - $endif -} - -<* - Sets the callback function to be called during ASan error reporting. -*> -fn void set_error_report_callback(ErrorCallback callback) -{ - $if env::ADDRESS_SANITIZER: - __asan_set_error_report_callback(callback); - $endif -} - -module std::core::sanitizer::asan @if(env::ADDRESS_SANITIZER); - -extern fn void __asan_poison_memory_region(void* addr, usz size); -extern fn void __asan_unpoison_memory_region(void* addr, usz size); -extern fn CInt __asan_address_is_poisoned(void* addr); -extern fn void* __asan_region_is_poisoned(void* beg, usz size); -extern fn void __asan_describe_address(void* addr); -extern fn CInt __asan_report_present(); -extern fn void* __asan_get_report_pc(); -extern fn void* __asan_get_report_bp(); -extern fn void* __asan_get_report_sp(); -extern fn void* __asan_get_report_address(); -extern fn CInt __asan_get_report_access_type(); -extern fn usz __asan_get_report_access_size(); -extern fn ZString __asan_get_report_description(); -extern fn ZString __asan_locate_address(void* addr, char* name, usz name_size, void** region_address, usz* region_size); -extern fn usz __asan_get_alloc_stack(void* addr, void** trace, usz size, CInt* thread_id); -extern fn usz __asan_get_free_stack(void* addr, void** trace, usz size, CInt* thread_id); -extern fn void __asan_get_shadow_mapping(usz* shadow_scale, usz* shadow_offset); -extern fn void __asan_set_error_report_callback(ErrorCallback callback); -extern fn void __asan_print_accumulated_stats(); -extern fn void* __asan_get_current_fake_stack(); -extern fn void* __asan_addr_is_in_fake_stack(void* fake_stack, void* addr, void** beg, void** end); -extern fn void __asan_handle_no_return(); -extern fn CInt __asan_update_allocation_context(void* addr); diff --git a/lib7/std/core/sanitizer/sanitizer.c3 b/lib7/std/core/sanitizer/sanitizer.c3 deleted file mode 100644 index 6e253868b..000000000 --- a/lib7/std/core/sanitizer/sanitizer.c3 +++ /dev/null @@ -1,80 +0,0 @@ -module std::core::sanitizer; - -macro void annotate_contiguous_container(void* beg, void* end, void* old_mid, void* new_mid) -{ - $if env::ADDRESS_SANITIZER: - __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid); - $endif -} - -macro void annotate_double_ended_contiguous_container(void* storage_beg, void* storage_end, void* old_container_beg, void* old_container_end, void* new_container_beg, void* new_container_end) -{ - $if env::ADDRESS_SANITIZER: - __sanitizer_annotate_double_ended_contiguous_container(storage_beg, storage_end, old_container_beg, old_container_end, new_container_beg, new_container_end); - $endif -} - -macro void print_stack_trace() -{ - $if env::ADDRESS_SANITIZER: - __sanitizer_print_stack_trace(); - $endif -} - -fn void set_death_callback(VoidFn callback) -{ - $if env::ANY_SANITIZER: - __sanitizer_set_death_callback(callback); - $endif -} - -module std::core::sanitizer @if (env::ANY_SANITIZER); - -struct __Sanitizer_sandbox_arguments -{ - CInt coverage_sandboxed; - iptr coverage_fd; - CUInt coverage_max_block_size; -} - -extern fn void __sanitizer_set_report_path(ZString path); -extern fn void __sanitizer_set_report_fd(void* fd); -extern fn ZString __sanitizer_get_report_path(); -extern fn void __sanitizer_sandbox_on_notify(__Sanitizer_sandbox_arguments* args); -extern fn void __sanitizer_report_error_summary(ZString error_summary); -extern fn ushort __sanitizer_unaligned_load16(void* p); -extern fn uint __sanitizer_unaligned_load32(void* p); -extern fn ulong __sanitizer_unaligned_load64(void* p); -extern fn void __sanitizer_unaligned_store16(void* p, ushort x); -extern fn void __sanitizer_unaligned_store32(void* p, uint x); -extern fn void __sanitizer_unaligned_store64(void* p, ulong x); -extern fn CInt __sanitizer_acquire_crash_state(); -extern fn void __sanitizer_annotate_contiguous_container(void* beg, void* end, void* old_mid, void* new_mid); -extern fn void __sanitizer_annotate_double_ended_contiguous_container(void* storage_beg, void* storage_end, - void* old_container_beg, void* old_container_end, - void* new_container_beg, void* new_container_end); -extern fn CInt __sanitizer_verify_contiguous_container(void* beg, void* mid, void* end); -extern fn CInt __sanitizer_verify_double_ended_contiguous_container( - void* storage_beg, void* container_beg, - void* container_end, void* storage_end); -extern fn void* __sanitizer_contiguous_container_find_bad_address(void* beg, void* mid, void* end); -extern fn void* __sanitizer_double_ended_contiguous_container_find_bad_address( - void* storage_beg, void* container_beg, - void* container_end, void* storage_end); - -extern fn void __sanitizer_print_stack_trace(); -extern fn void __sanitizer_symbolize_pc(void* pc, ZString fmt, char* out_buf, usz out_buf_size); -extern fn void __sanitizer_symbolize_global(void* data_ptr, ZString fmt, char* out_buf, usz out_buf_size); -extern fn void __sanitizer_set_death_callback(VoidFn callback); -extern fn void __sanitizer_weak_hook_memcmp(void* called_pc, void* s1, void* s2, usz n, CInt result); -extern fn void __sanitizer_weak_hook_strncmp(void* called_pc, ZString s1, ZString s2, usz n, CInt result); -extern fn void __sanitizer_weak_hook_strncasecmp(void* called_pc, ZString s1, ZString s2, usz n, CInt result); -extern fn void __sanitizer_weak_hook_strcmp(void* called_pc, ZString s1, ZString s2, CInt result); -extern fn void __sanitizer_weak_hook_strcasecmp(void* called_pc, ZString s1, ZString s2, CInt result); -extern fn void __sanitizer_weak_hook_strstr(void* called_pc, ZString s1, ZString s2, char* result); -extern fn void __sanitizer_weak_hook_strcasestr(void* called_pc, ZString s1, ZString s2, char* result); -extern fn void __sanitizer_weak_hook_memmem(void* called_pc, void* s1, usz len1, void* s2, usz len2, void* result); -extern fn void __sanitizer_print_memory_profile(usz top_percent, usz max_number_of_contexts); -extern fn void __sanitizer_start_switch_fiber(void** fake_stack_save, void* bottom, usz size); -extern fn void __sanitizer_finish_switch_fiber(void* fake_stack_save, void** bottom_old, usz* size_old); -extern fn CInt __sanitizer_get_module_and_offset_for_pc(void* pc, char* module_path, usz module_path_len, void** pc_offset); diff --git a/lib7/std/core/sanitizer/tsan.c3 b/lib7/std/core/sanitizer/tsan.c3 deleted file mode 100644 index b9bf3da7b..000000000 --- a/lib7/std/core/sanitizer/tsan.c3 +++ /dev/null @@ -1,39 +0,0 @@ -module std::core::sanitizer::tsan; - -distinct MutexFlags = inline CUInt; - -const MutexFlags MUTEX_LINKER_INIT = 1 << 0; -const MutexFlags MUTEX_WRITE_REENTRANT = 1 << 1; -const MutexFlags MUTEX_READ_REENTRANT = 1 << 2; -const MutexFlags MUTEX_NOT_STATIC = 1 << 8; -const MutexFlags MUTEX_READ_LOCK = 1 << 3; -const MutexFlags MUTEX_TRY_LOCK = 1 << 4; -const MutexFlags MUTEX_TRY_LOCK_FAILED = 1 << 5; -const MutexFlags MUTEX_RECURSIVE_LOCK = 1 << 6; -const MutexFlags MUTEX_RECURSIVE_UNLOCK = 1 << 7; -const MutexFlags MUTEX_TRY_READ_LOCK = MUTEX_READ_LOCK | MUTEX_TRY_LOCK; -const MutexFlags MUTEX_TRY_READ_LOCK_FAILED = MUTEX_TRY_READ_LOCK | MUTEX_TRY_LOCK_FAILED; - -macro void mutex_create(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_create(addr, flags); $endif } -macro void mutex_destroy(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_destroy(addr, flags); $endif } -macro void mutex_pre_lock(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_pre_lock(addr, flags); $endif } -macro void mutex_post_lock(void* addr, MutexFlags flags, CInt recursion) { $if env::THREAD_SANITIZER: __tsan_mutex_post_lock(addr, flags, recursion); $endif } -macro CInt mutex_pre_unlock(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: return __tsan_mutex_pre_unlock(addr, flags); $else return 0; $endif } -macro void mutex_post_unlock(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_post_unlock(addr, flags); $endif } -macro void mutex_pre_signal(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_pre_signal(addr, flags); $endif } -macro void mutex_post_signal(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_post_signal(addr, flags); $endif } -macro void mutex_pre_divert(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_pre_divert(addr, flags); $endif } -macro void mutex_post_divert(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_post_divert(addr, flags); $endif } - -module std::core::sanitizer::tsan @if(env::THREAD_SANITIZER) @private; - -extern fn void __tsan_mutex_create(void* addr, CUInt flags); -extern fn void __tsan_mutex_destroy(void* addr, CUInt flags); -extern fn void __tsan_mutex_pre_lock(void* addr, CUInt flags); -extern fn void __tsan_mutex_post_lock(void* addr, CUInt flags, CInt recursion); -extern fn CInt __tsan_mutex_pre_unlock(void* addr, CUInt flags); -extern fn void __tsan_mutex_post_unlock(void* addr, CUInt flags); -extern fn void __tsan_mutex_pre_signal(void* addr, CUInt flags); -extern fn void __tsan_mutex_post_signal(void* addr, CUInt flags); -extern fn void __tsan_mutex_pre_divert(void* addr, CUInt flags); -extern fn void __tsan_mutex_post_divert(void* addr, CUInt flags); diff --git a/lib7/std/core/string.c3 b/lib7/std/core/string.c3 deleted file mode 100644 index 2980b56fc..000000000 --- a/lib7/std/core/string.c3 +++ /dev/null @@ -1,834 +0,0 @@ -module std::core::string; -import std::ascii; -import std::io; - -distinct String @if(!$defined(String)) = inline char[]; -distinct ZString = inline char*; -distinct WString = inline Char16*; -def Char32 = uint; -def Char16 = ushort; - -fault UnicodeResult -{ - INVALID_UTF8, - INVALID_UTF16, - CONVERSION_FAILED, -} - -const uint SURROGATE_OFFSET @private = 0x10000; -const uint SURROGATE_GENERIC_MASK @private = 0xF800; -const uint SURROGATE_MASK @private = 0xFC00; -const uint SURROGATE_CODEPOINT_MASK @private = 0x03FF; -const uint SURROGATE_BITS @private = 10; -const uint SURROGATE_LOW_VALUE @private = 0xDC00; -const uint SURROGATE_HIGH_VALUE @private = 0xD800; - -fault NumberConversion -{ - EMPTY_STRING, - NEGATIVE_VALUE, - MALFORMED_INTEGER, - INTEGER_OVERFLOW, - MALFORMED_FLOAT, - FLOAT_OUT_OF_RANGE, -} - - -<* - Return a temporary ZString created using the formatting function. - - @param [in] fmt `The formatting string` -*> -fn ZString tformat_zstr(String fmt, args...) -{ - DString str = dstring::temp_with_capacity(fmt.len + args.len * 8); - str.appendf(fmt, ...args); - return str.zstr_view(); -} - -<* - Return a new String created using the formatting function. - - @param [inout] allocator `The allocator to use` - @param [in] fmt `The formatting string` -*> -fn String format(Allocator allocator, String fmt, args...) => @pool(allocator) -{ - DString str = dstring::temp_with_capacity(fmt.len + args.len * 8); - str.appendf(fmt, ...args); - return str.copy_str(allocator); -} - -<* - Return a temporary String created using the formatting function. - - @param [in] fmt `The formatting string` -*> -fn String tformat(String fmt, args...) -{ - DString str = dstring::temp_with_capacity(fmt.len + args.len * 8); - str.appendf(fmt, ...args); - return str.str_view(); -} - -<* - Check if a character is in a set. - - @param c `the character to check` - @param [in] set `The formatting string` - @pure - @return `True if a character is in the set` -*> -macro bool char_in_set(char c, String set) -{ - foreach (ch : set) if (ch == c) return true; - return false; -} - -fn String join(Allocator allocator, String[] s, String joiner) -{ - if (!s) - { - return (String)allocator::new_array(allocator, char, 2)[:0]; - } - - usz total_size = joiner.len * s.len; - foreach (String* &str : s) - { - total_size += str.len; - } - @pool(allocator) - { - DString res = dstring::temp_with_capacity(total_size); - res.append(s[0]); - foreach (String* &str : s[1..]) - { - res.append(joiner); - res.append(*str); - } - return res.copy_str(allocator); - }; -} - -<* - Remove characters from the front and end of a string. - - @param [in] string `The string to trim` - @param [in] to_trim `The set of characters to trim, defaults to whitespace` - @pure - @return `a substring of the string passed in` -*> -fn String String.trim(string, String to_trim = "\t\n\r ") -{ - return string.trim_left(to_trim).trim_right(to_trim); -} - -<* - Remove characters from the front of a string. - - @param [in] string `The string to trim` - @param [in] to_trim `The set of characters to trim, defaults to whitespace` - @pure - @return `a substring of the string passed in` -*> -fn String String.trim_left(string, String to_trim = "\t\n\r ") -{ - usz start = 0; - usz len = string.len; - while (start < len && char_in_set(string[start], to_trim)) start++; - if (start == len) return string[:0]; - return string[start..]; -} - -<* - Remove characters from the end of a string. - - @param [in] string `The string to trim` - @param [in] to_trim `The set of characters to trim, defaults to whitespace` - @pure - @return `a substring of the string passed in` -*> -fn String String.trim_right(string, String to_trim = "\t\n\r ") -{ - usz len = string.len; - while (len > 0 && char_in_set(string[len - 1], to_trim)) len--; - return string[:len]; -} - -<* - Check if the String starts with the needle. - - @param [in] string - @param [in] needle - @pure - @return `'true' if the string starts with the needle` -*> -fn bool String.starts_with(string, String needle) -{ - if (needle.len > string.len) return false; - if (!needle.len) return true; - return string[:needle.len] == needle; -} - -<* - Check if the String ends with the needle. - - @param [in] string - @param [in] needle - @pure - @return `'true' if the string ends with the needle` -*> -fn bool String.ends_with(string, String needle) -{ - if (needle.len > string.len) return false; - if (!needle.len) return true; - return string[^needle.len..] == needle; -} - -<* - Strip the front of the string if the prefix exists. - - @param [in] string - @param [in] needle - @pure - @return `the substring with the prefix removed` -*> -fn String String.strip(string, String needle) -{ - if (!needle.len || !string.starts_with(needle)) return string; - return string[needle.len..]; -} - -<* - Strip the end of the string if the suffix exists. - - @param [in] string - @param [in] needle - @pure - @return `the substring with the suffix removed` -*> -fn String String.strip_end(string, String needle) -{ - if (!needle.len || !string.ends_with(needle)) return string; - // Note that this is the safe way if we want to support zero length. - return string[:(string.len - needle.len)]; -} - -<* - Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" } - - @param [in] s - @param [in] needle - @param max "Max number of elements, 0 means no limit, defaults to 0" - @param skip_empty "True to skip empty elements" - @param [&inout] allocator "The allocator to use for the String[]" - - @require needle.len > 0 "The needle must be at least 1 character long" - @ensure return.len > 0 -*> -fn String[] String.split(s, Allocator allocator, String needle, usz max = 0, bool skip_empty = false) -{ - usz capacity = 16; - usz i = 0; - String* holder = allocator::alloc_array(allocator, String, capacity); - bool no_more = false; - while (!no_more) - { - usz! index = i == max - 1 ? SearchResult.MISSING? : s.index_of(needle); - String res @noinit; - if (try index) - { - res = s[:index]; - s = s[index + needle.len..]; - } - else - { - res = s; - no_more = true; - } - if (!res.len && skip_empty) - { - continue; - } - - if (i == capacity) - { - capacity *= 2; - holder = allocator::realloc(allocator, holder, String.sizeof * capacity); - } - holder[i++] = res; - } - return holder[:i]; -} - - -<* - This function is identical to String.split, but implicitly uses the - temporary allocator. - - @param [in] s - @param [in] needle - @param max "Max number of elements, 0 means no limit, defaults to 0" - @param skip_empty "True to skip empty elements" -*> -fn String[] String.tsplit(s, String needle, usz max = 0, bool skip_empty = false) => s.split(tmem(), needle, max, skip_empty) @inline; - -fault SplitResult { BUFFER_EXCEEDED } - -<* - Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" } - - @param [in] s - @param [in] needle - @param [inout] buffer - @param max "Max number of elements, 0 means no limit, defaults to 0" - @require needle.len > 0 "The needle must be at least 1 character long" - @ensure return.len > 0 - @return! SplitResult.BUFFER_EXCEEDED `If there are more elements than would fit the buffer` -*> -fn String[]! String.split_to_buffer(s, String needle, String[] buffer, usz max = 0, bool skip_empty = false) -{ - usz max_capacity = buffer.len; - usz i = 0; - bool no_more = false; - while (!no_more) - { - usz! index = i == max - 1 ? SearchResult.MISSING? : s.index_of(needle); - String res @noinit; - if (try index) - { - res = s[:index]; - s = s[index + needle.len..]; - } - else - { - res = s; - no_more = true; - } - if (!res.len && skip_empty) - { - continue; - } - if (i == max_capacity) - { - return SplitResult.BUFFER_EXCEEDED?; - } - buffer[i++] = res; - } - return buffer[:i]; -} - -<* - Check if a substring is found in the string. - - @param [in] s - @param [in] needle "The string to look for." - @pure - @return "true if the string contains the substring, false otherwise" -*> -fn bool String.contains(s, String needle) -{ - return @ok(s.index_of(needle)); -} - -<* - Find the index of the first incidence of a string. - - @param [in] s - @param needle "The character to look for" - @pure - @ensure return < s.len - @return "the index of the needle" - @return! SearchResult.MISSING "if the needle cannot be found" -*> -fn usz! String.index_of_char(s, char needle) -{ - foreach (i, c : s) - { - if (c == needle) return i; - } - return SearchResult.MISSING?; -} - -<* - Find the index of the first incidence of a one of the chars. - - @param [in] s - @param [in] needle "The characters to look for" - @pure - @ensure return < s.len - @return "the index of the needle" - @return! SearchResult.MISSING "if the needle cannot be found" -*> -fn usz! String.index_of_chars(String s, char[] needle) -{ - foreach (i, c : s) - { - foreach (j, pin : needle) - { - if (c == pin) return i; - } - } - - return SearchResult.MISSING?; -} - -<* - Find the index of the first incidence of a character. - - @param [in] s - @param needle "The character to look for" - @param start_index "The index to start with, may exceed max index." - @pure - @ensure return < s.len - @return "the index of the needle" - @return! SearchResult.MISSING "if the needle cannot be found starting from the start_index" -*> -fn usz! String.index_of_char_from(s, char needle, usz start_index) -{ - usz len = s.len; - if (len <= start_index) return SearchResult.MISSING?; - for (usz i = start_index; i < len; i++) - { - if (s[i] == needle) return i; - } - return SearchResult.MISSING?; -} - -<* - Find the index of the first incidence of a character starting from the end. - - @param [in] s - @param needle "the character to find" - @pure - @ensure return < s.len - @return "the index of the needle" - @return! SearchResult.MISSING "if the needle cannot be found" -*> -fn usz! String.rindex_of_char(s, char needle) -{ - foreach_r (i, c : s) - { - if (c == needle) return i; - } - return SearchResult.MISSING?; -} - -<* - Find the index of the first incidence of a string. - - @param [in] s - @param [in] needle - @pure - @ensure return < s.len - @require needle.len > 0 : "The needle must be len 1 or more" - @return "the index of the needle" - @return! SearchResult.MISSING "if the needle cannot be found" -*> -fn usz! String.index_of(s, String needle) -{ - usz needed = needle.len; - if (needed > 0 && s.len >= needed) - { - char first = needle[0]; - foreach (i, c: s[..^needed]) - { - if (c == first && s[i:needed] == needle) return i; - } - } - return SearchResult.MISSING?; -} - -<* - Find the index of the last incidence of a string. - - @param [in] s - @param [in] needle - @pure - @ensure return < s.len - @require needle.len > 0 "The needle must be len 1 or more" - @return "the index of the needle" - @return! SearchResult.MISSING "if the needle cannot be found" -*> -fn usz! String.rindex_of(s, String needle) -{ - usz needed = needle.len; - if (needed > 0 && s.len >= needed) - { - char first = needle[0]; - foreach_r (i, c: s[..^needed]) - { - if (c == first && s[i:needed] == needle) return i; - } - } - return SearchResult.MISSING?; -} - -fn String ZString.str_view(str) -{ - return (String)(str[:str.len()]); -} - -fn usz ZString.char_len(str) -{ - usz len = 0; - char* ptr = (char*)str; - while (char c = ptr++[0]) - { - if (c & 0xC0 != 0x80) len++; - } - return len; -} - -fn usz ZString.len(str) -{ - usz len = 0; - char* ptr = (char*)str; - while (char c = ptr++[0]) len++; - return len; -} - - -fn ZString String.zstr_copy(s, Allocator allocator) -{ - usz len = s.len; - char* str = allocator::malloc(allocator, len + 1); - mem::copy(str, s.ptr, len); - str[len] = 0; - return (ZString)str; -} - -fn String String.concat(s1, Allocator allocator, String s2) -{ - usz full_len = s1.len + s2.len; - char* str = allocator::malloc(allocator, full_len + 1); - usz s1_len = s1.len; - mem::copy(str, s1.ptr, s1_len); - mem::copy(str + s1_len, s2.ptr, s2.len); - str[full_len] = 0; - return (String)str[:full_len]; -} - -fn String String.tconcat(s1, String s2) => s1.concat(tmem(), s2); - - -fn ZString String.zstr_tcopy(s) => s.zstr_copy(tmem()) @inline; - -<* - Copy this string, by duplicating the string, always adding a zero byte - sentinel, so that it safely can be converted to a ZString by a - cast. -*> -fn String String.copy(s, Allocator allocator) -{ - usz len = s.len; - char* str = allocator::malloc(allocator, len + 1); - mem::copy(str, s.ptr, len); - str[len] = 0; - return (String)str[:len]; -} - -fn void String.free(&s, Allocator allocator) -{ - if (!s.ptr) return; - allocator::free(allocator, s.ptr); - *s = ""; -} - -fn String String.tcopy(s) => s.copy(tmem()) @inline; - -fn String ZString.copy(z, Allocator allocator) -{ - return z.str_view().copy(allocator) @inline; -} - -fn String ZString.tcopy(z) -{ - return z.str_view().copy(tmem()) @inline; -} - -<* - Convert an UTF-8 string to UTF-16 - @return "The UTF-16 string as a slice, allocated using the given allocator" - @return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence" - @return! AllocationFailure "If allocation of the string fails" -*> -fn Char16[]! String.to_utf16_copy(s, Allocator allocator) -{ - usz len16 = conv::utf16len_for_utf8(s); - Char16* data = allocator::alloc_array_try(allocator, Char16, len16 + 1)!; - conv::utf8to16_unsafe(s, data)!; - data[len16] = 0; - return data[:len16]; -} - -fn Char16[]! String.to_utf16_tcopy(s) => s.to_utf16_copy(tmem()); - -fn WString! String.to_wstring_copy(s, Allocator allocator) -{ - return (WString)s.to_utf16_copy(allocator).ptr; -} - -fn WString! String.to_wstring_tcopy(s) => s.to_wstring_copy(tmem()); - -fn Char32[]! String.to_utf32_copy(s, Allocator allocator) -{ - usz codepoints = conv::utf8_codepoints(s); - Char32* data = allocator::alloc_array_try(allocator, Char32, codepoints + 1)!; - conv::utf8to32_unsafe(s, data)!; - data[codepoints] = 0; - return data[:codepoints]; -} - -<* - Convert a string to ASCII lower case in place. - - @param [inout] s - @pure -*> -fn void String.convert_to_lower(s) -{ - foreach (&c : s) if (c.is_upper() @pure) *c += 'a' - 'A'; -} - -fn String String.to_lower_copy(s, Allocator allocator) -{ - String copy = s.copy(allocator); - copy.convert_to_lower(); - return copy; -} - -fn String String.to_lower_tcopy(s) -{ - return s.to_lower_copy(tmem()); -} - -<* - Convert a string to ASCII upper case. - - @param [inout] s - @pure -*> -fn void String.convert_to_upper(s) -{ - foreach (&c : s) if (c.is_lower() @pure) *c -= 'a' - 'A'; -} - -<* - Returns a string converted to ASCII upper case. - - @param [in] s - @param [inout] allocator - - @return `a new String converted to ASCII upper case.` -*> -fn String String.to_upper_copy(s, Allocator allocator) -{ - String copy = s.copy(allocator); - copy.convert_to_upper(); - return copy; -} - -fn StringIterator String.iterator(s) -{ - return { s, 0 }; -} - -<* - @param [in] s - @return `a temporary String converted to ASCII upper case.` -*> -fn String String.to_upper_tcopy(s) -{ - return s.to_upper_copy(tmem()); -} - -fn String! new_from_utf32(Allocator allocator, Char32[] utf32) -{ - usz len = conv::utf8len_for_utf32(utf32); - char* data = allocator::malloc_try(allocator, len + 1)!; - defer catch allocator::free(allocator, data); - conv::utf32to8_unsafe(utf32, data); - data[len] = 0; - return (String)data[:len]; -} - -fn String! new_from_utf16(Allocator allocator, Char16[] utf16) -{ - usz len = conv::utf8len_for_utf16(utf16); - char* data = allocator::malloc_try(allocator, len + 1)!; - defer catch allocator::free(allocator, data); - conv::utf16to8_unsafe(utf16, data)!; - data[len] = 0; - return (String)data[:len]; -} - -fn String! new_from_wstring(Allocator allocator, WString wstring) -{ - usz utf16_len; - while (wstring[utf16_len] != 0) utf16_len++; - Char16[] utf16 = wstring[:utf16_len]; - return new_from_utf16(allocator, utf16); -} - -fn String! temp_from_wstring(WString wstring) => new_from_wstring(tmem(), wstring) @inline; -fn String! temp_from_utf16(Char16[] utf16) => new_from_utf16(tmem(), utf16) @inline; - -fn usz String.utf8_codepoints(s) -{ - usz len = 0; - foreach (char c : s) - { - if (c & 0xC0 != 0x80) len++; - } - return len; -} - - -<* - @require (base <= 10 && base > 1) || base == 16 : "Unsupported base" -*> -macro String.to_integer(string, $Type, int base = 10) -{ - usz len = string.len; - usz index = 0; - char* ptr = string.ptr; - while (index < len && ascii::is_blank_m(ptr[index])) index++; - if (len == index) return NumberConversion.EMPTY_STRING?; - bool is_negative; - switch (string[index]) - { - case '-': - if ($Type.min == 0) return NumberConversion.NEGATIVE_VALUE?; - is_negative = true; - index++; - case '+': - index++; - default: - break; - } - if (len == index) return NumberConversion.MALFORMED_INTEGER?; - $Type base_used = ($Type)base; - if (string[index] == '0' && base == 10) - { - index++; - if (index == len) return ($Type)0; - switch (string[index]) - { - case 'x': - case 'X': - base_used = 16; - index++; - case 'b': - case 'B': - base_used = 2; - index++; - case 'o': - case 'O': - base_used = 8; - index++; - default: - break; - } - if (len == index) return NumberConversion.MALFORMED_INTEGER?; - } - $Type value = 0; - while (index != len) - { - char c = string[index++]; - switch - { - case base_used != 16 || c < 'A': c -= '0'; - case c <= 'F': c -= 'A' - 10; - case c < 'a' || c > 'f': return NumberConversion.MALFORMED_INTEGER?; - default: c -= 'a' - 10; - } - if (c >= base_used) return NumberConversion.MALFORMED_INTEGER?; - do - { - if (is_negative) - { - $Type new_value = value * base_used - c; - if (new_value > value) return NumberConversion.INTEGER_OVERFLOW?; - value = new_value; - break; - } - $Type new_value = value * base_used + c; - if (new_value < value) return NumberConversion.INTEGER_OVERFLOW?; - value = new_value; - }; - } - return value; -} - -fn int128! String.to_int128(s, int base = 10) => s.to_integer(int128, base); -fn long! String.to_long(s, int base = 10) => s.to_integer(long, base); -fn int! String.to_int(s, int base = 10) => s.to_integer(int, base); -fn short! String.to_short(s, int base = 10) => s.to_integer(short, base); -fn ichar! String.to_ichar(s, int base = 10) => s.to_integer(ichar, base); - -fn uint128! String.to_uint128(s, int base = 10) => s.to_integer(uint128, base); -fn ulong! String.to_ulong(s, int base = 10) => s.to_integer(ulong, base); -fn uint! String.to_uint(s, int base = 10) => s.to_integer(uint, base); -fn ushort! String.to_ushort(s, int base = 10) => s.to_integer(ushort, base); -fn char! String.to_uchar(s, int base = 10) => s.to_integer(char, base); - -fn double! String.to_double(s) => s.to_real(double); -fn float! String.to_float(s) => s.to_real(float); - -fn Splitter String.splitter(self, String split) -{ - return { .string = self, .split = split }; -} - -fn Splitter String.tokenize(self, String split) -{ - return { .string = self, .split = split, .tokenize = true }; -} - -struct Splitter -{ - String string; - String split; - usz current; - bool tokenize; - int last_index; -} - -fn void Splitter.reset(&self) -{ - self.current = 0; -} - -fn String! Splitter.next(&self) -{ - while (true) - { - usz len = self.string.len; - usz current = self.current; - if (current >= len) return IteratorResult.NO_MORE_ELEMENT?; - String remaining = self.string[current..]; - usz! next = remaining.index_of(self.split); - if (try next) - { - self.current = current + next + self.split.len; - if (!next && self.tokenize) continue; - return remaining[:next]; - } - self.current = len; - return remaining; - } -} - -macro String new_from_struct(Allocator allocator, x) -{ - DString s; - @stack_mem(512; Allocator mem) - { - s.init(allocator: mem); - io::fprint(&s, x)!!; - return s.copy_str(allocator); - }; -} - -macro String temp_from_struct(x) => new_from_struct(tmem(), x); diff --git a/lib7/std/core/string_iterator.c3 b/lib7/std/core/string_iterator.c3 deleted file mode 100644 index abd7aa4a3..000000000 --- a/lib7/std/core/string_iterator.c3 +++ /dev/null @@ -1,49 +0,0 @@ -module std::core::string::iterator; - -struct StringIterator -{ - String utf8; - usz current; -} - -fn void StringIterator.reset(&self) -{ - self.current = 0; -} - -fn Char32! StringIterator.next(&self) -{ - usz len = self.utf8.len; - usz current = self.current; - if (current >= len) return IteratorResult.NO_MORE_ELEMENT?; - usz read = (len - current < 4 ? len - current : 4); - Char32 res = conv::utf8_to_char32(&self.utf8[current], &read)!; - self.current += read; - return res; -} - -fn Char32! StringIterator.peek(&self) -{ - usz len = self.utf8.len; - usz current = self.current; - if (current >= len) return IteratorResult.NO_MORE_ELEMENT?; - usz read = (len - current < 4 ? len - current : 4); - Char32 res = conv::utf8_to_char32(&self.utf8[current], &read)!; - return res; -} - -fn bool StringIterator.has_next(&self) -{ - return self.current < self.utf8.len; -} - -fn Char32! StringIterator.get(&self) -{ - usz len = self.utf8.len; - usz current = self.current; - usz read = (len - current < 4 ? len - current : 4); - usz index = current > read ? current - read : 0; - if (index >= len) return IteratorResult.NO_MORE_ELEMENT?; - Char32 res = conv::utf8_to_char32(&self.utf8[index], &read)!; - return res; -} diff --git a/lib7/std/core/string_to_real.c3 b/lib7/std/core/string_to_real.c3 deleted file mode 100644 index 952b82432..000000000 --- a/lib7/std/core/string_to_real.c3 +++ /dev/null @@ -1,484 +0,0 @@ -module std::core::string; -import std::math; - -// Float parsing based on code in Musl floatscan.c by Rich Felker. -// Musl uses the MIT license, copied below: -// ---------------------------------------------------------------------- -// Copyright © 2005-2014 Rich Felker, et al. -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -// CLAIM, DAMAGES OR OTHER 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. -// ---------------------------------------------------------------------- - -const KMAX = 128; -const MASK = KMAX - 1; -const B1B_DIG = 2; -const uint[2] B1B_MAX = { 9007199, 254740991 }; - -<* - @require chars.len > 0 -*> -macro double! decfloat(char[] chars, int $bits, int $emin, int sign) -{ - uint[KMAX] x; - const uint[2] TH = B1B_MAX; - int emax = - $emin - $bits + 3; - - const int[?] P10S = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 }; - usz index; - bool got_digit = chars[0] == '0'; - bool got_rad; - long lrp, dc; - int k, j, lnz; - usz len = chars.len; - usz last_char = len - 1; - - assert(len); - - char c @noinit; - // Skip past first characters - while ((c = chars[index]) == '0') - { - if (index == last_char) return sign * 0.0; - index++; - } - - if (c == '.') - { - got_rad = true; - if (index == last_char) - { - if (!got_digit) return NumberConversion.MALFORMED_FLOAT?; - return sign * 0.0; - } - if (index != last_char && (c = chars[++index]) == '0') - { - lrp--; - got_digit = true; - while (last_char != index && (c = chars[++index]) == '0') - { - lrp--; - } - } - } - - while (c - '0' < 10u || c == '.') - { - switch - { - case c == '.': - if (got_rad) return NumberConversion.MALFORMED_FLOAT?; - got_rad = true; - lrp = dc; - case k < KMAX - 3: - dc++; - if (c != '0') lnz = (int)dc; - if (j) - { - x[k] = x[k] * 10 + c - '0'; - } - else - { - x[k] = c - '0'; - } - if (++j == 9) - { - k++; - j = 0; - } - got_digit = true; - default: - dc++; - if (c != '0') x[KMAX - 4] |= 1; - - } - if (index == last_char) break; - assert(index < last_char); - c = chars[++index]; - } - if (!got_rad) lrp = dc; - if (!got_digit) return NumberConversion.MALFORMED_FLOAT?; - if ((c | 32) == 'e') - { - if (last_char == index) return NumberConversion.MALFORMED_FLOAT?; - long e10 = String.to_long((String)chars[index + 1..]) ?? NumberConversion.MALFORMED_FLOAT?!; - lrp += e10; - } - else if (index != last_char) - { - return NumberConversion.MALFORMED_FLOAT?; - } - // Handle zero specially to avoid nasty special cases later - if (!x[0]) return sign * 0.0; - - // Optimize small integers (w/no exponent) and over/under-flow - if (lrp == dc && dc < 10 && ($bits > 30 || (ulong)x[0] >> $bits == 0)) return sign * (double)x[0]; - if (lrp > - $emin / 2) return NumberConversion.FLOAT_OUT_OF_RANGE?; - if (lrp < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?; - - // Align incomplete final B1B digit - if (j) - { - for (; j < 9; j++) x[k] *= 10; - k++; - j = 0; - } - - int a; - int z = k; - int e2; - long rp = lrp; - - // Optimize small to mid-size integers (even in exp. notation) - if (lnz < 9 && lnz <= rp && rp < 18) - { - if (rp == 9) return sign * (double)x[0]; - if (rp < 9) return sign * (double)x[0] / P10S[8 - rp]; - int bitlim = $bits - 3 * (int)(rp - 9); - if (bitlim > 30 || x[0] >> bitlim == 0) return sign * (double)x[0] * P10S[rp - 10]; - } - - // Align radix point to B1B digit boundary - if (rp % 9) - { - long rpm9 = rp >= 0 ? rp % 9 : rp % 9 + 9; - int p10 = P10S[8 - rpm9]; - uint carry = 0; - for (k = a; k != z; k++) - { - uint tmp = x[k] % p10; - x[k] = x[k] / p10 + carry; - carry = 1000000000 / p10 * tmp; - if (k == a && !x[k]) - { - a = (a + 1) & MASK; - rp -= 9; - } - } - if (carry) x[z++] = carry; - rp += 9 - rpm9; - } - - // Upscale until desired number of bits are left of radix point - while (rp < 9 * B1B_DIG || (rp == 9 * B1B_DIG && x[a] < TH[0])) - { - uint carry = 0; - e2 -= 29; - for (k = (z - 1) & MASK; ; k = (k - 1) & MASK) - { - ulong tmp = (ulong)x[k] << 29 + carry; - if (tmp > 1000000000) - { - carry = (uint)(tmp / 1000000000); - x[k] = (uint)(tmp % 1000000000); - } - else - { - carry = 0; - x[k] = (uint)tmp; - } - if (k == (z - 1) & MASK && k != a && !x[k]) z = k; - if (k == a) break; - } - if (carry) - { - rp += 9; - a = (a - 1) & MASK; - if (a == z) - { - z = (z - 1) & MASK; - x[(z - 1) & MASK] |= x[z]; - } - x[a] = carry; - } - } - - // Downscale until exactly number of bits are left of radix point - while (true) - { - uint carry = 0; - int sh = 1; - int i; - for (i = 0; i < B1B_DIG; i++) - { - k = (a + i) & MASK; - if (k == z || x[k] < TH[i]) - { - i = B1B_DIG; - break; - } - if (x[(a + i) & MASK] > TH[i]) break; - } - if (i == B1B_DIG && rp == 9 * B1B_DIG) break; - if (rp > 9 + 9 * B1B_DIG) sh = 9; - e2 += sh; - for (k = a; k != z; k = (k+1) & MASK) - { - uint tmp = x[k] & (1 << sh - 1); - x[k] = x[k] >> sh + carry; - carry = (1000000000 >> sh) * tmp; - if (k == a && !x[k]) - { - a = (a + 1) & MASK; - i--; - rp -= 9; - } - } - if (carry) - { - if ((z + 1) & MASK != a) - { - x[z] = carry; - z = (z + 1) & MASK; - } - else - { - x[(z - 1) & MASK] |= 1; - } - } - } - - // Assemble desired bits into floating point variable - double y; - int i; - for (i = 0; i < B1B_DIG; i++) - { - if ((a + i) & MASK == z) x[(z = (z + 1) & MASK) - 1] = 0; - y = 1000000000.0 * y + x[(a + i) & MASK]; - } - - y *= sign; - - bool denormal; - // Limit precision for denormal results - uint bits = $bits; - if (bits > math::DOUBLE_MANT_DIG + e2 - $emin) - { - bits = math::DOUBLE_MANT_DIG + e2 - $emin; - if (bits < 0) bits = 0; - denormal = true; - } - - // Calculate bias term to force rounding, move out lower bits - double bias; - double frac; - if (bits < math::DOUBLE_MANT_DIG) - { - bias = math::copysign(math::scalbn(1, 2 * math::DOUBLE_MANT_DIG - bits - 1), y); - frac = y % math::scalbn(1, math::DOUBLE_MANT_DIG - bits); - y -= frac; - y += bias; - } - - // Process tail of decimal input so it can affect rounding - if ((a + i) & MASK != z) - { - uint t = x[(a + i) & MASK]; - switch - { - case t < 500000000 && (t || (a + i + 1) & MASK != z): - frac += 0.25 * sign; - case t > 500000000: - frac += 0.75 * sign; - case t == 500000000: - if ((a + i + 1) & MASK == z) - { - frac += 0.5 * sign; - } - else - { - frac += 0.75 * sign; - } - } - if (math::DOUBLE_MANT_DIG - bits >= 2 && !(frac % 1)) frac++; - } - - y += frac; - y -= bias; - - if (((e2 + math::DOUBLE_MANT_DIG) & int.max) > emax - 5) - { - if (math::abs(y) >= 0x1p53) - { - if (denormal && bits == math::DOUBLE_MANT_DIG + e2 - $emin) denormal = false; - y *= 0.5; - e2++; - } - if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return NumberConversion.MALFORMED_FLOAT?; - } - return math::scalbn(y, e2); -} - -macro double! hexfloat(char[] chars, int $bits, int $emin, int sign) -{ - double scale = 1; - uint x; - long rp; - long dc; - char c @noinit; - bool got_rad; - bool got_digit; - bool got_tail; - usz len = chars.len; - usz last_char = len - 1; - usz index; - double y; - - // Skip past first characters - while ((c = chars[index]) == '0') - { - if (index == last_char) return 0.0; - index++; - } - if (c == '.') - { - got_rad = true; - if (index == last_char) - { - if (!got_digit) return NumberConversion.MALFORMED_FLOAT?; - return sign * 0.0; - } - if (index != last_char && (c = chars[++index]) == '0') - { - rp--; - got_digit = true; - while (last_char != index && (c = chars[++index]) == '0') - { - rp--; - } - } - } - - while ((c - '0') < 10u || ((c | 32) - 'a') < 6u || c == '.') - { - if (c == '.') - { - if (got_rad) return NumberConversion.MALFORMED_FLOAT?; - got_rad = true; - rp = dc; - } - else - { - got_digit = true; - int d = c > '9' ? ((c | 32) + 10 - 'a') : (c - '0'); - switch - { - case dc < 8: - x = x * 16 + d; - case dc < math::DOUBLE_MANT_DIG / 4 + 1: - y += d * (scale /= 16); - got_tail = true; - case d && !got_tail: - y += 0.5 * scale; - got_tail = true; - } - dc++; - } - if (index == last_char) break; - c = chars[++index]; - } - if (!got_digit) return NumberConversion.MALFORMED_FLOAT?; - if (!got_rad) rp = dc; - for (; dc < 8; dc++) x *= 16; - - long e2; - if ((c | 32) == 'p') - { - long e2val = String.to_long((String)chars[index + 1..]) ?? (NumberConversion.MALFORMED_FLOAT?)!; - e2 = e2val; - } - e2 += 4 * rp - 32; - if (!x) return sign * 0.0; - if (e2 > -$emin) return NumberConversion.FLOAT_OUT_OF_RANGE?; - if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?; - - while (x < 0x80000000) - { - if (y >= 0.5) - { - x += x + 1; - y += y - 1; - } - else - { - x += x; - y += y; - } - e2--; - } - int bits = $bits; - if ($bits > 32 + e2 - $emin) - { - bits = (int)(32 + e2 - $emin); - if (bits < 0) bits = 0; - } - double bias; - if (bits < math::DOUBLE_MANT_DIG) - { - bias = math::copysign(math::scalbn(1, 32 + math::DOUBLE_MANT_DIG - bits - 1), (double)sign); - } - - if (bits < 32 && y && !(x & 1)) - { - x++; - y = 0; - } - y = bias + sign * (double)x + sign * y; - y -= bias; - if (!y) return NumberConversion.FLOAT_OUT_OF_RANGE?; - - return math::scalbn(y, (int)e2); -} - -macro String.to_real(chars, $Type) @private -{ - int sign = 1; - $switch ($Type) - $case float: - const int BITS = math::FLOAT_MANT_DIG; - const int EMIN = math::FLOAT_MIN_EXP - BITS; - $case double: - const int BITS = math::DOUBLE_MANT_DIG; - const int EMIN = math::DOUBLE_MIN_EXP - BITS; - $case float128: - $error "Not yet supported"; - $default: - $error "Unexpected type"; - $endswitch - - while (chars.len && chars[0] == ' ') chars = chars[1..]; - if (!chars.len) return NumberConversion.MALFORMED_FLOAT?; - switch (chars[0]) - { - case '-': - sign = -1; - nextcase; - case '+': - chars = chars[1..]; - } - if (chars == "infinity" || chars == "INFINITY") return sign * $Type.inf; - if (chars == "NAN" || chars == "nan") return $Type.nan; - - if (chars.len > 2 && chars[0] == '0' && (chars[1] | 32) == 'x') - { - return ($Type)hexfloat((char[])chars[2..], BITS, EMIN, sign); - } - return ($Type)decfloat((char[])chars, BITS, EMIN, sign); -} - diff --git a/lib7/std/core/test.c3 b/lib7/std/core/test.c3 deleted file mode 100644 index 0f183fa24..000000000 --- a/lib7/std/core/test.c3 +++ /dev/null @@ -1,223 +0,0 @@ -<* -Unit test module - -This module provides a toolset of macros for running unit test checks - -Example: -```c3 -module sample::m; -import std::io; - -fault MathError -{ - DIVISION_BY_ZERO -} - -fn double! divide(int a, int b) -{ - if (b == 0) return MathError.DIVISION_BY_ZERO?; - return (double)(a) / (double)(b); -} - -fn void! test_div() @test -{ - test::eq(2, divide(6, 3)!); - test::ne(1, 2); - test::ge(3, 3); - test::gt(2, divide(3, 3)!); - test::lt(2, 3); - test::le(2, 3); - test::eq_approx(m::divide(1, 3)!, 0.333, places: 3); - test::@check(2 == 2, "divide: %d", divide(6, 3)!); - test::@error(m::divide(3, 0), MathError.DIVISION_BY_ZERO); -} - -``` -*> -// Copyright (c) 2025 Alex Veden . 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::core::test; -import std::core::runtime @public; -import std::math, std::io, libc; - -<* - Initializes test case context. - - @param setup_fn `initializer function for test case` - @param teardown_fn `cleanup function for test context (may be null)` - - @require runtime::test_context != null "Only allowed in @test functions" - @require setup_fn != null "setup_fn must always be set" -*> -macro @setup(TestFn setup_fn, TestFn teardown_fn = null) -{ - runtime::test_context.setup_fn = setup_fn; - runtime::test_context.teardown_fn = teardown_fn; - runtime::test_context.setup_fn(); -} - -<* - Checks condition and fails assertion if not true - - @param #condition `any boolean condition, will be expanded by text` - @param format `printf compatible format` - @param args `vargs for format` - @require runtime::test_context != null "Only allowed in @test functions" -*> -macro @check(#condition, String format = "", args...) -{ - if (!#condition) - { - @stack_mem(512; Allocator allocator) - { - DString s; - s.init(allocator); - s.appendf("check `%s` failed. ", $stringify(#condition)); - s.appendf(format, ...args); - print_panicf(s.str_view()); - }; - } -} - -<* - Check if function returns specific error - - @param #funcresult `result of function execution` - @param error_expected `expected error of function execution` - @require runtime::test_context != null "Only allowed in @test functions" -*> -macro @error(#funcresult, anyfault error_expected) -{ - if (catch err = #funcresult) - { - if (err != error_expected) - { - print_panicf("`%s` expected to return error [%s], got [%s]", - $stringify(#funcresult), error_expected, err); - } - return; - } - print_panicf("`%s` error [%s] was not returned.", $stringify(#funcresult), error_expected); -} - -<* - Check if left == right - - @param left `left argument of any comparable type` - @param right `right argument of any comparable type` - @require runtime::test_context != null "Only allowed in @test functions" -*> -macro eq(left, right) -{ - if (!equals(left, right)) - { - print_panicf("`%s` != `%s`", left, right); - } -} - -<* - Check left floating point value is approximately equals to right value - - @param places `number of decimal places to compare (default: 7)` - @param delta `minimal allowed difference (overrides places parameter)` - @param equal_nan `allows comparing nan values, if left and right both nans result is ok` - - @require places > 0, places <= 20 "too many decimal places" - @require delta >= 0, delta <= 1 "delta must be a small number" - @require runtime::test_context != null "Only allowed in @test functions" -*> -macro void eq_approx(double left, double right, uint places = 7, double delta = 0, bool equal_nan = true) -{ - double diff = left - right; - double eps = delta; - if (eps == 0) eps = 1.0 / math::pow(10.0, places); - - if (!math::is_approx(left, right, eps)) - { - if (equal_nan && math::is_nan(left) && math::is_nan(right)) return; - print_panicf("Not almost equal: `%s` !~~ `%s` delta=%e diff: %e", left, right, eps, diff); - } -} - -<* - Check if left != right - - @param left `left argument of any comparable type` - @param right `right argument of any comparable type` - @require runtime::test_context != null "Only allowed in @test functions" -*> -macro void ne(left, right) -{ - if (equals(left, right)) - { - print_panicf("`%s` == `%s`", left, right); - } -} - -<* - Check if left > right - - @param left `left argument of any comparable type` - @param right `right argument of any comparable type` - @require runtime::test_context != null "Only allowed in @test functions" -*> -macro gt(left, right) -{ - if (!builtin::greater(left, right)) - { - print_panicf("`%s` <= `%s`", left, right); - } -} - -<* - Check if left >= right - - @param left `left argument of any comparable type` - @param right `right argument of any comparable type` - @require runtime::test_context != null "Only allowed in @test functions" -*> -macro ge(left, right) -{ - if (!builtin::greater_eq(left, right)) - { - print_panicf("`%s` < `%s`", left, right); - } -} - -<* - Check if left < right - - @param left `left argument of any comparable type` - @param right `right argument of any comparable type` - @require runtime::test_context != null "Only allowed in @test functions" -*> -macro lt(left, right) -{ - if (!builtin::less(left, right)) - { - print_panicf("`%s` >= `%s`", left, right); - } -} - -<* - Check if left <= right - - @param left `left argument of any comparable type` - @param right `right argument of any comparable type` - @require runtime::test_context != null "Only allowed in @test functions" -*> -macro le(left, right) -{ - if (!builtin::less_eq(left, right)) - { - print_panicf("`%s` > `%s`", left, right); - } -} - -macro void print_panicf(format, ...) @local -{ - runtime::test_context.assert_print_backtrace = false; - builtin::panicf(format, $$FILE, $$FUNC, $$LINE, $vasplat); -} - diff --git a/lib7/std/core/types.c3 b/lib7/std/core/types.c3 deleted file mode 100644 index 3600e4a10..000000000 --- a/lib7/std/core/types.c3 +++ /dev/null @@ -1,380 +0,0 @@ - -module std::core::types; -import libc; - - -fault ConversionResult -{ - VALUE_OUT_OF_RANGE, - VALUE_OUT_OF_UNSIGNED_RANGE, -} - -<* - @require $Type.kindof.is_int() "Type was not an integer" - @require v.type.kindof == ENUM "Value was not an enum" -*> -macro any_to_enum_ordinal(any v, $Type) -{ - return any_to_int(v.as_inner(), $Type); -} - -<* - @require $Type.kindof.is_int() "Type was not an integer" - @require v.type.kindof.is_int() "Value was not an integer" -*> -macro any_to_int(any v, $Type) -{ - typeid any_type = v.type; - TypeKind kind = any_type.kindof; - bool is_mixed_signed = $Type.kindof != any_type.kindof; - $Type max = $Type.max; - $Type min = $Type.min; - switch (any_type) - { - case ichar: - ichar c = *(char*)v.ptr; - if (is_mixed_signed && c < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?; - return ($Type)c; - case short: - short s = *(short*)v.ptr; - if (is_mixed_signed && s < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?; - if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?; - return ($Type)s; - case int: - int i = *(int*)v.ptr; - if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?; - if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?; - return ($Type)i; - case long: - long l = *(long*)v.ptr; - if (is_mixed_signed && l < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?; - if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?; - return ($Type)l; - case int128: - int128 i = *(int128*)v.ptr; - if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?; - if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?; - return ($Type)i; - case char: - char c = *(char*)v.ptr; - if (c > max) return ConversionResult.VALUE_OUT_OF_RANGE?; - return ($Type)c; - case ushort: - ushort s = *(ushort*)v.ptr; - if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?; - return ($Type)s; - case uint: - uint i = *(uint*)v.ptr; - if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?; - return ($Type)i; - case ulong: - ulong l = *(ulong*)v.ptr; - if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?; - return ($Type)l; - case uint128: - uint128 i = *(uint128*)v.ptr; - if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?; - return ($Type)i; - default: - unreachable(); - } -} - -fn bool typeid.is_subtype_of(self, typeid other) -{ - while (self != void.typeid) - { - if (self == other) return true; - self = self.parentof; - } - return false; -} - -macro bool is_subtype_of($Type, $OtherType) -{ - var $typeid = $Type.typeid; - $switch ($Type) - $case $OtherType: return true; - $default: return false; - $endswitch -} -macro bool is_numerical($Type) -{ - var $kind = $Type.kindof; - $if $kind == TypeKind.DISTINCT: - return is_numerical($typefrom($Type.inner)); - $else - return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT - || $kind == TypeKind.VECTOR; - $endif -} - -fn bool TypeKind.is_int(kind) @inline -{ - return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT; -} - -macro bool is_slice_convertable($Type) -{ - $switch ($Type.kindof) - $case SLICE: - return true; - $case POINTER: - return $Type.inner.kindof == TypeKind.ARRAY; - $default: - return false; - $endswitch -} - -macro bool is_bool($Type) @const => $Type.kindof == TypeKind.BOOL; -macro bool is_int($Type) @const => $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT; - -<* - @require is_numerical($Type) "Expected a numerical type" -*> -macro bool is_signed($Type) @const -{ - $switch (inner_kind($Type)) - $case SIGNED_INT: - $case FLOAT: - return true; - $case VECTOR: - return is_signed($typefrom($Type.inner)); - $default: - return false; - $endswitch -} - -<* - @require is_numerical($Type) "Expected a numerical type" -*> -macro bool is_unsigned($Type) @const -{ - $switch (inner_kind($Type)) - $case UNSIGNED_INT: - return true; - $case VECTOR: - return is_unsigned($typefrom($Type.inner)); - $default: - return false; - $endswitch -} - -macro bool is_indexable($Type) @const -{ - return $defined(($Type){}[0]); -} - -macro bool is_ref_indexable($Type) @const -{ - return $defined(&($Type){}[0]); -} - -macro bool is_intlike($Type) @const -{ - $switch ($Type.kindof) - $case SIGNED_INT: - $case UNSIGNED_INT: - return true; - $case VECTOR: - return $Type.inner.kindof == TypeKind.SIGNED_INT || $Type.inner.kindof == TypeKind.UNSIGNED_INT; - $default: - return false; - $endswitch -} - -macro bool is_underlying_int($Type) @const -{ - $switch ($Type.kindof) - $case SIGNED_INT: - $case UNSIGNED_INT: - return true; - $case DISTINCT: - return is_underlying_int($typefrom($Type.inner)); - $default: - return false; - $endswitch -} - -macro bool is_float($Type) @const => $Type.kindof == TypeKind.FLOAT; - -macro bool is_floatlike($Type) @const -{ - $switch ($Type.kindof) - $case FLOAT: - return true; - $case VECTOR: - return $Type.inner.kindof == TypeKind.FLOAT; - $default: - return false; - $endswitch -} - -macro bool is_vector($Type) @const -{ - return $Type.kindof == TypeKind.VECTOR; -} - -macro typeid inner_type($Type) @const -{ - $if $Type.kindof == TypeKind.DISTINCT: - return inner_type($typefrom($Type.inner)); - $else - return $Type.typeid; - $endif -} - -macro TypeKind inner_kind($Type) @const -{ - return inner_type($Type).kindof; -} - -macro bool is_same($TypeA, $TypeB) @const -{ - return $TypeA.typeid == $TypeB.typeid; -} - -macro bool @has_same(#a, #b, ...) @const -{ - var $type_a = @typeid(#a); - $if $type_a != @typeid(#b): - return false; - $endif - $for (var $i = 0; $i < $vacount; $i++) - $if @typeid($vaexpr[$i]) != $type_a: - return false; - $endif - $endfor - return true; -} - -macro bool may_load_atomic($Type) @const -{ - $switch ($Type.kindof) - $case BOOL: - $case SIGNED_INT: - $case UNSIGNED_INT: - $case POINTER: - $case FLOAT: - return true; - $case DISTINCT: - return may_load_atomic($Type.inner); - $default: - return false; - $endswitch -} - -macro lower_to_atomic_compatible_type($Type) @const -{ - $switch ($Type.kindof) - $case SIGNED_INT: - $case UNSIGNED_INT: - return $Type.typeid; - $case DISTINCT: - return lower_to_atomic_compatible_type($Type.inner); - $case FLOAT: - $switch ($Type) - $case float16: - return ushort.typeid; - $case float: - return uint.typeid; - $case double: - return ulong.typeid; - $case float128: - return uint128.typeid; - $default: - return void.typeid; - $endswitch - $default: - return void.typeid; - $endswitch -} - -macro bool is_promotable_to_floatlike($Type) @const => types::is_floatlike($Type) || types::is_int($Type); -macro bool is_promotable_to_float($Type) @const => types::is_float($Type) || types::is_int($Type); - -macro bool is_same_vector_type($Type1, $Type2) @const -{ - $if $Type1.kindof != TypeKind.VECTOR: - return $Type2.kindof != TypeKind.VECTOR; - $else - return $Type1.inner == $Type2.inner && $Type1.len == $Type2.len; - $endif -} - -macro bool is_equatable_type($Type) @const -{ - $if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals): - return true; - $else - return $Type.is_eq; - $endif -} - -<* - Checks if a type implements the copy protocol. -*> -macro bool implements_copy($Type) @const -{ - return $defined($Type.copy) && $defined($Type.free); -} - -macro bool is_equatable_value(value) @deprecated -{ - return is_equatable_type($typeof(value)); -} - -macro bool @equatable_value(#value) @const -{ - return is_equatable_type($typeof(#value)); -} - -macro bool @comparable_value(#value) @const -{ - $if $defined(#value.less) || $defined(#value.compare_to): - return true; - $else - return $typeof(#value).is_ordered; - $endif -} - -macro bool is_comparable_value(value) @deprecated -{ - $if $defined(value.less) || $defined(value.compare_to): - return true; - $else - return $typeof(value).is_ordered; - $endif -} - -enum TypeKind : char -{ - VOID, - BOOL, - SIGNED_INT, - UNSIGNED_INT, - FLOAT, - TYPEID, - ANYFAULT, - ANY, - ENUM, - FAULT, - STRUCT, - UNION, - BITSTRUCT, - FUNC, - OPTIONAL, - ARRAY, - SLICE, - VECTOR, - DISTINCT, - POINTER, - INTERFACE, -} - -struct TypeEnum -{ - TypeKind type; - usz elements; -} - diff --git a/lib7/std/core/values.c3 b/lib7/std/core/values.c3 deleted file mode 100644 index e053e459c..000000000 --- a/lib7/std/core/values.c3 +++ /dev/null @@ -1,66 +0,0 @@ -module std::core::values; - -macro typeid @typeid(#value) @const @builtin => $typeof(#value).typeid; -macro TypeKind @typekind(#value) @const @builtin => $typeof(#value).kindof; -macro bool @typeis(#value, $Type) @const @builtin => $typeof(#value).typeid == $Type.typeid; -<* - Return true if two values have the same type before any conversions. -*> -macro bool @is_same_type(#value1, #value2) @const => $typeof(#value1).typeid == $typeof(#value2).typeid; -macro bool @is_bool(#value) @const => types::is_bool($typeof(#value)); -macro bool @is_int(#value) @const => types::is_int($typeof(#value)); -macro bool @is_floatlike(#value) @const => types::is_floatlike($typeof(#value)); -macro bool @is_float(#value) @const => types::is_float($typeof(#value)); -macro bool @is_promotable_to_floatlike(#value) @const => types::is_promotable_to_floatlike($typeof(#value)); -macro bool @is_promotable_to_float(#value) @const => types::is_promotable_to_float($typeof(#value)); -macro bool @is_vector(#value) @const => types::is_vector($typeof(#value)); -macro bool @is_same_vector_type(#value1, #value2) @const => types::is_same_vector_type($typeof(#value1), $typeof(#value2)); -macro bool @assign_to(#value1, #value2) @const => $assignable(#value1, $typeof(#value2)); -macro bool @is_lvalue(#value) => $defined(#value = #value); - -macro promote_int(x) -{ - $if @is_int(x): - return (double)x; - $else - return x; - $endif -} - -<* - Select between two values at compile time, - the values do not have to be of the same type. - - This acts like `$bool ? #value_1 : #value_2` but at compile time. - - @param $bool `true for picking the first value, false for the other` - @param #value_1 - @param #value_2 - @returns `The selected value.` -*> -macro @select(bool $bool, #value_1, #value_2) @builtin -{ - $if $bool: - return #value_1; - $else - return #value_2; - $endif -} -macro promote_int_same(x, y) -{ - $if @is_int(x): - $switch - $case @is_vector(y) &&& $typeof(y).inner == float.typeid: - return (float)x; - $case $typeof(y).typeid == float.typeid: - return (float)x; - $default: - return (double)x; - $endswitch - $else - return x; - $endif -} - -macro TypeKind @inner_kind(#value) @const => types::inner_kind($typeof(#value)); - diff --git a/lib7/std/crypto/crypto.c3 b/lib7/std/crypto/crypto.c3 deleted file mode 100644 index b6080dc54..000000000 --- a/lib7/std/crypto/crypto.c3 +++ /dev/null @@ -1,12 +0,0 @@ -module std::crypto; - -fn bool safe_compare(void* data1, void* data2, usz len) -{ - char match = 0; - for (usz i = 0; i < len; i++) - { - match = match | (mem::@volatile_load(((char*)data1)[i]) ^ mem::@volatile_load(((char*)data2)[i])); - } - return match == 0; -} - diff --git a/lib7/std/crypto/dh.c3 b/lib7/std/crypto/dh.c3 deleted file mode 100644 index 6d7649e98..000000000 --- a/lib7/std/crypto/dh.c3 +++ /dev/null @@ -1,12 +0,0 @@ -module std::crypto::dh; -import std::math::bigint; - -fn BigInt generate_secret(BigInt p, BigInt x, BigInt y) -{ - return y.mod_pow(x, p); -} - -fn BigInt public_key(BigInt p, BigInt g, BigInt x) -{ - return g.mod_pow(x, p); -} diff --git a/lib7/std/crypto/rc4.c3 b/lib7/std/crypto/rc4.c3 deleted file mode 100644 index 62787d3ea..000000000 --- a/lib7/std/crypto/rc4.c3 +++ /dev/null @@ -1,75 +0,0 @@ -module std::crypto::rc4; -// Copyright (c) 2021 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. - -struct Rc4 -{ - uint i, j; - char[256] state; -} - -<* - Initialize the RC4 state. - - @param [in] key "The key to use" - @require key.len > 0 "The key must be at least 1 byte long" -*> -fn void Rc4.init(&self, char[] key) -{ - // Init the state matrix - foreach (char i, &c : self.state) *c = i; - for (int i = 0, int j = 0; i < 256; i++) - { - j = (j + self.state[i] + key[i % key.len]) & 0xFF; - @swap(self.state[i], self.state[j]); - } - self.i = 0; - self.j = 0; -} - -<* - Run a single pass of en/decryption using a particular key. - @param [in] key - @param [inout] data -*> -fn void crypt(char[] key, char[] data) -{ - Rc4 rc4; - rc4.init(key); - rc4.crypt(data, data); -} - -<* - Encrypt or decrypt a sequence of bytes. - - @param [in] in "The input" - @param [out] out "The output" - @require in.len <= out.len "Output would overflow" -*> -fn void Rc4.crypt(&self, char[] in, char[] out) -{ - uint i = self.i; - uint j = self.j; - char* state = &self.state; - isz len = in.len; - foreach (idx, c : in) - { - i = (i + 1) & 0xFF; - j = (j + state[i]) & 0xFF; - @swap(state[i], state[j]); - out[idx] = in[idx] ^ state[(state[i] + state[j]) & 0xFF]; - } - self.i = i; - self.j = j; -} - -<* - Clear the rc4 state. - - @param [&out] self "The RC4 State" -*> -fn void Rc4.destroy(&self) -{ - *self = {}; -} diff --git a/lib7/std/encoding/base32.c3 b/lib7/std/encoding/base32.c3 deleted file mode 100644 index 1900f2bcb..000000000 --- a/lib7/std/encoding/base32.c3 +++ /dev/null @@ -1,404 +0,0 @@ -module std::encoding::base32; - -// This module implements base32 encoding according to RFC 4648 -// (https://www.rfc-editor.org/rfc/rfc4648) - -struct Base32Alphabet -{ - char[32] encoding; - char[256] reverse; -} - -const char NO_PAD = 0; -const char DEFAULT_PAD = '='; - -<* - Encode the content of src into a newly allocated string - @param [in] src "The input to be encoded." - @param padding "The padding character or 0 if none" - @param alphabet "The alphabet to use" - @require padding < 0xFF "Invalid padding character" - @return "The encoded string." -*> -fn String! encode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) -{ - char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding)); - return encode_buffer(src, dst, padding, alphabet); -} - -<* - Decode the content of src into a newly allocated char array. - @param [in] src "The input to be encoded." - @param padding "The padding character or 0 if none" - @param alphabet "The alphabet to use" - @require padding < 0xFF "Invalid padding character" - @return "The decoded data." -*> -fn char[]! decode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) -{ - char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding)); - return decode_buffer(src, dst, padding, alphabet); -} - -fn String! tencode(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(tmem(), code, padding, alphabet); -fn char[]! tdecode(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(tmem(), code, padding, alphabet); - -<* - Calculate the length in bytes of the decoded data. - @param n "Length in bytes of input." - @param padding "The padding character or 0 if none" - @require padding < 0xFF "Invalid padding character" - @return "Length in bytes of the decoded data." -*> -fn usz decode_len(usz n, char padding) -{ - if (padding) return (n / 8) * 5; - // no padding - usz trailing = n % 8; - return n / 8 * 5 + (trailing * 5 ) / 8; -} - -<* - Calculate the length in bytes of the encoded data. - @param n "Length in bytes on input." - @param padding "The padding character or 0 if none" - @require padding < 0xFF "Invalid padding character" - @return "Length in bytes of the encoded data." -*> -fn usz encode_len(usz n, char padding) -{ - // A character is encoded into 8 x 5-bit blocks. - if (padding) return (n + 4) / 5 * 8; - - // no padding - usz trailing = n % 5; - return n / 5 * 8 + (trailing * 8 + 4) / 5; -} - -<* - Decode the content of src into dst, which must be properly sized. - @param src "The input to be decoded." - @param dst "The decoded input." - @param padding "The padding character or 0 if none" - @param alphabet "The alphabet to use" - @require padding < 0xFF "Invalid padding character" - @require dst.len >= decode_len(src.len, padding) "Destination buffer too small" - @return "The resulting dst buffer" - @return! DecodingFailure -*> -fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) -{ - if (src.len == 0) return dst[:0]; - char* dst_ptr = dst; - usz dn = decode_len(src.len, padding); - usz n; - char[8] buf; - while (src.len > 0 && dst.len > 0) - { - usz i @noinit; - // load 8 bytes into buffer - for (i = 0; i < 8; i++) - { - if (src.len == 0) - { - if (padding > 0) return DecodingFailure.INVALID_PADDING?; - break; - } - if (src[0] == padding) break; - buf[i] = alphabet.reverse[src[0]]; - if (buf[i] == INVALID) return DecodingFailure.INVALID_CHARACTER?; - src = src[1..]; - } - - // extract 5-bytes from the buffer which contains 8 x 5 bit chunks - switch (i) - { - case 8: - // |66677777| dst[4] - // | 77777| buf[7] - // |666 | buf[6] << 5 - dst[4] = buf[7] | buf[6] << 5; - n++; - nextcase 7; - case 7: - // |45555566| dst[3] - // | 66| buf[6] >> 3 - // | 55555 | buf[5] << 2 - // |4 | buf[4] << 7 - dst[3] = buf[6] >> 3 | buf[5] << 2 | buf[4] << 7; - n++; - nextcase 5; - case 5: - // |33334444| dst[2] - // | 4444| buf[4] >> 1 - // |3333 | buf[3] << 4 - dst[2] = buf[4] >> 1 | buf[3] << 4; - n++; - nextcase 4; - case 4: - // |11222223| dst[1] - // | 3| buf[3] >> 4 - // | 22222 | buf[2] << 1 - // |11 | buf[1] << 6 - dst[1] = buf[3] >> 4 | buf[2] << 1 | buf[1] << 6; - n++; - nextcase 2; - case 2: - // |00000111| dst[0] - // | 111| buf[1] >> 2 - // |00000 | buf[0] << 3 - dst[0] = buf[1] >> 2 | buf[0] << 3; - n++; - default: - return DecodingFailure.INVALID_CHARACTER?; - } - if (dst.len < 5) break; - dst = dst[5..]; - } - return dst_ptr[:n]; -} - -<* - Encode the content of src into dst, which must be properly sized. - @param [in] src "The input to be encoded." - @param [inout] dst "The encoded input." - @param padding "The padding character or 0 if none" - @param alphabet "The alphabet to use" - @require padding < 0xFF "Invalid padding character" - @require dst.len >= encode_len(src.len, padding) "Destination buffer too small" - @return "The encoded size." -*> -fn String encode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) -{ - if (src.len == 0) return (String)dst[:0]; - - char* dst_ptr = dst; - usz n = (src.len / 5) * 5; - usz dn = encode_len(src.len, padding); - - uint msb, lsb; - for (usz i = 0; i < n; i += 5) - { - // to fit 40 bits we need two 32-bit uints - msb = (uint)src[i] << 24 | (uint)src[i+1] << 16 - | (uint)src[i+2] << 8 | (uint)src[i+3]; - lsb = msb << 8 | (uint)src[i+4]; - - // now slice them into 5-bit chunks and translate to the - // alphabet. - dst[0] = alphabet.encoding[(msb >> 27) & MASK]; - dst[1] = alphabet.encoding[(msb >> 22) & MASK]; - dst[2] = alphabet.encoding[(msb >> 17) & MASK]; - dst[3] = alphabet.encoding[(msb >> 12) & MASK]; - dst[4] = alphabet.encoding[(msb >> 7) & MASK]; - dst[5] = alphabet.encoding[(msb >> 2) & MASK]; - dst[6] = alphabet.encoding[(lsb >> 5) & MASK]; - dst[7] = alphabet.encoding[lsb & MASK]; - - dst = dst[8..]; - } - - usz trailing = src.len - n; - if (trailing == 0) return (String)dst_ptr[:dn]; - - msb = 0; - switch (trailing) - { - case 4: - msb |= (uint)src[n+3]; - lsb = msb << 8; - dst[6] = alphabet.encoding[(lsb >> 5) & MASK]; - dst[5] = alphabet.encoding[(msb >> 2) & MASK]; - nextcase 3; - case 3: - msb |= (uint)src[n+2] << 8; - dst[4] = alphabet.encoding[(msb >> 7) & MASK]; - nextcase 2; - case 2: - msb |= (uint)src[n+1] << 16; - dst[3] = alphabet.encoding[(msb >> 12) & MASK]; - dst[2] = alphabet.encoding[(msb >> 17) & MASK]; - nextcase 1; - case 1: - msb |= (uint)src[n] << 24; - dst[1] = alphabet.encoding[(msb >> 22) & MASK]; - dst[0] = alphabet.encoding[(msb >> 27) & MASK]; - } - - // add the padding - if (padding > 0) - { - for (usz i = (trailing * 8 / 5) + 1; i < 8; i++) - { - dst[i] = padding; - } - } - return (String)dst_ptr[:dn]; -} - -const uint MASK @private = 0b11111; -const char INVALID @private = 0xff; - -const int STD_PADDING = '='; -const int NO_PADDING = -1; - -fault Base32Error -{ - DUPLICATE_IN_ALPHABET, - PADDING_IN_ALPHABET, - INVALID_CHARACTER_IN_ALPHABET, - DESTINATION_TOO_SMALL, - INVALID_PADDING, - CORRUPT_INPUT -} - -struct Base32Encoder @deprecated -{ - Base32Alphabet alphabet; - char padding; -} - -<* - @param encoder "The 32-character alphabet for encoding." - @param padding "Set to a negative value to disable padding." - @require padding < 256 -*> -fn void! Base32Encoder.init(&self, Alphabet encoder = STD_ALPHABET, int padding = STD_PADDING) -{ - encoder.validate(padding)!; - *self = { .alphabet = { .encoding = (char[32])encoder }, .padding = padding < 0 ? (char)0 : (char)padding}; -} - -<* - Calculate the length in bytes of the encoded data. - @param n "Length in bytes on input." - @return "Length in bytes of the encoded data." -*> -fn usz Base32Encoder.encode_len(&self, usz n) -{ - return encode_len(n, self.padding); -} - -<* - Encode the content of src into dst, which must be properly sized. - @param [in] src "The input to be encoded." - @param [inout] dst "The encoded input." - @return "The encoded size." - @return! Base32Error.DESTINATION_TOO_SMALL -*> -fn usz! Base32Encoder.encode(&self, char[] src, char[] dst) -{ - usz dn = self.encode_len(src.len); - if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?; - return encode_buffer(src, dst, self.padding, &self.alphabet).len; -} - -struct Base32Decoder @deprecated -{ - Base32Alphabet alphabet; - char padding; -} - -<* - @param decoder "The alphabet used for decoding." - @param padding "Set to a negative value to disable padding." - @require padding < 256 -*> -fn void! Base32Decoder.init(&self, Alphabet decoder = STD_ALPHABET, int padding = STD_PADDING) -{ - decoder.validate(padding)!; - *self = { .alphabet = { .encoding = (char[32])decoder }, .padding = padding < 0 ? (char)0 : (char)padding }; - - self.alphabet.reverse[..] = INVALID; - foreach (char i, c : decoder) - { - self.alphabet.reverse[c] = i; - } -} - -<* - Calculate the length in bytes of the decoded data. - @param n "Length in bytes of input." - @return "Length in bytes of the decoded data." -*> -fn usz Base32Decoder.decode_len(&self, usz n) -{ - return decode_len(n, self.padding); -} - -<* - Decode the content of src into dst, which must be properly sized. - @param src "The input to be decoded." - @param dst "The decoded input." - @return "The decoded size." - @return! Base32Error.DESTINATION_TOO_SMALL, Base32Error.CORRUPT_INPUT -*> -fn usz! Base32Decoder.decode(&self, char[] src, char[] dst) -{ - if (src.len == 0) return 0; - usz dn = self.decode_len(src.len); - if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?; - return decode_buffer(src, dst, self.padding, &self.alphabet).len; -} - - -// Validate the 32-character alphabet to make sure that no character occurs -// twice and that the padding is not present in the alphabet. -fn void! Alphabet.validate(&self, int padding) -{ - bool[256] checked; - foreach (c : self) - { - if (checked[c]) - { - return Base32Error.DUPLICATE_IN_ALPHABET?; - } - checked[c] = true; - if (c == '\r' || c == '\n') - { - return Base32Error.INVALID_CHARACTER_IN_ALPHABET?; - } - } - if (padding >= 0) - { - char pad = (char)padding; - if (pad == '\r' || pad == '\n') - { - return Base32Error.INVALID_PADDING?; - } - if (checked[pad]) - { - return Base32Error.PADDING_IN_ALPHABET?; - } - } -} - -distinct Alphabet = char[32]; -// Standard base32 Alphabet -const Alphabet STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; -// Extended Hex Alphabet -const Alphabet HEX_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; - -const Base32Alphabet STANDARD = { - .encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", - .reverse = x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffff1a1b1c1d1e1fffffffffffffffff - ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` -}; - -const Base32Alphabet HEX = { - .encoding = "0123456789ABCDEFGHIJKLMNOPQRSTUV", - .reverse = x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffff00010203040506070809ffffffffffff - ff0a0b0c0d0e0f101112131415161718191a1b1c1d1e1fffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` -}; diff --git a/lib7/std/encoding/base64.c3 b/lib7/std/encoding/base64.c3 deleted file mode 100644 index 9659439c9..000000000 --- a/lib7/std/encoding/base64.c3 +++ /dev/null @@ -1,399 +0,0 @@ -module std::encoding::base64; -import std::core::bitorder; - -// The implementation is based on https://www.rfc-editor.org/rfc/rfc4648 -// Specifically this section: -// https://www.rfc-editor.org/rfc/rfc4648#section-4 - -const char NO_PAD = 0; -const char DEFAULT_PAD = '='; - -struct Base64Alphabet -{ - char[64] encoding; - char[256] reverse; -} - -const Base64Alphabet STANDARD = { - .encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", - .reverse = - x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffff3effffff3f3435363738393a3b3c3dffffffffffff - ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffffff - ff1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233ffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` -}; - -const Base64Alphabet URL = { - .encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", - .reverse = - x`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffff3effff3435363738393a3b3c3dffffffffffff - ff000102030405060708090a0b0c0d0e0f10111213141516171819ffffffff3f - ff1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233ffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` -}; - -const STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -const URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; - -fn String encode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) -{ - char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding)); - return encode_buffer(src, dst, padding, alphabet); -} - -fn char[]! decode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) -{ - char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding))!; - return decode_buffer(src, dst, padding, alphabet); -} - -fn String tencode(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(tmem(), code, padding, alphabet); -fn char[]! tdecode(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(tmem(), code, padding, alphabet); - - -<* - Calculate the size of the encoded data. - @param n "Size of the input to be encoded." - @param padding "The padding character or 0 if none" - @require padding < 0xFF "Invalid padding character" - @return "The size of the input once encoded." -*> -fn usz encode_len(usz n, char padding) -{ - if (padding) return (n + 2) / 3 * 4; - usz trailing = n % 3; - return n / 3 * 4 + (trailing * 4 + 2) / 3; -} - -<* - Calculate the size of the decoded data. - @param n "Size of the input to be decoded." - @param padding "The padding character or 0 if none" - @require padding < 0xFF "Invalid padding character" - @return "The size of the input once decoded." - @return! DecodingFailure.INVALID_PADDING -*> -fn usz! decode_len(usz n, char padding) -{ - usz dn = n / 4 * 3; - usz trailing = n % 4; - if (padding) - { - if (trailing != 0) return DecodingFailure.INVALID_PADDING?; - // source size is multiple of 4 - return dn; - } - if (trailing == 1) return DecodingFailure.INVALID_PADDING?; - return dn + trailing * 3 / 4; -} - -<* - Encode the content of src into dst, which must be properly sized. - @param src "The input to be encoded." - @param dst "The encoded input." - @param padding "The padding character or 0 if none" - @param alphabet "The alphabet to use" - @require padding < 0xFF "Invalid padding character" - @return "The encoded size." - @return! Base64Error.DESTINATION_TOO_SMALL -*> -fn String encode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) -{ - if (src.len == 0) return (String)dst[:0]; - usz dn = encode_len(src.len, padding); - char* dst_ptr = dst; - assert(dst.len >= dn); - usz trailing = src.len % 3; - char[] src3 = src[:^trailing]; - - while (src3.len > 0) - { - uint group = (uint)src3[0] << 16 | (uint)src3[1] << 8 | (uint)src3[2]; - dst[0] = alphabet.encoding[group >> 18 & MASK]; - dst[1] = alphabet.encoding[group >> 12 & MASK]; - dst[2] = alphabet.encoding[group >> 6 & MASK]; - dst[3] = alphabet.encoding[group & MASK]; - dst = dst[4..]; - src3 = src3[3..]; - } - - // Encode the remaining bytes according to: - // https://www.rfc-editor.org/rfc/rfc4648#section-3.5 - switch (trailing) - { - case 1: - uint group = (uint)src[^1] << 16; - dst[0] = alphabet.encoding[group >> 18 & MASK]; - dst[1] = alphabet.encoding[group >> 12 & MASK]; - if (padding > 0) - { - dst[2] = padding; - dst[3] = padding; - } - case 2: - uint group = (uint)src[^2] << 16 | (uint)src[^1] << 8; - dst[0] = alphabet.encoding[group >> 18 & MASK]; - dst[1] = alphabet.encoding[group >> 12 & MASK]; - dst[2] = alphabet.encoding[group >> 6 & MASK]; - if (padding > 0) - { - dst[3] = padding; - } - case 0: - break; - default: - unreachable(); - } - return (String)dst_ptr[:dn]; -} - -<* - Decode the content of src into dst, which must be properly sized. - @param src "The input to be decoded." - @param dst "The decoded input." - @param padding "The padding character or 0 if none" - @param alphabet "The alphabet to use" - @require (decode_len(src.len, padding) ?? 0) <= dst.len "Destination buffer too small" - @require padding < 0xFF "Invalid padding character" - @return "The decoded data." - @return! DecodingFailure -*> -fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) -{ - if (src.len == 0) return dst[:0]; - usz dn = decode_len(src.len, padding)!; - assert(dst.len >= dn); - - usz trailing = src.len % 4; - char* dst_ptr = dst; - char[] src4 = src; - switch - { - case !padding: - src4 = src[:^trailing]; - default: - // If there is padding, keep the last 4 bytes for later. - // NB. src.len >= 4 as decode_len passed - trailing = 4; - if (src[^1] == padding) src4 = src[:^4]; - } - while (src4.len > 0) - { - char c0 = alphabet.reverse[src4[0]]; - char c1 = alphabet.reverse[src4[1]]; - char c2 = alphabet.reverse[src4[2]]; - char c3 = alphabet.reverse[src4[3]]; - switch (0xFF) - { - case c0: - case c1: - case c2: - case c3: - return DecodingFailure.INVALID_CHARACTER?; - } - uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6 | (uint)c3; - dst[0] = (char)(group >> 16); - dst[1] = (char)(group >> 8); - dst[2] = (char)group; - dst = dst[3..]; - src4 = src4[4..]; - } - - if (trailing == 0) return dst_ptr[:dn]; - - src = src[^trailing..]; - char c0 = alphabet.reverse[src[0]]; - char c1 = alphabet.reverse[src[1]]; - if (c0 == 0xFF || c1 == 0xFF) return DecodingFailure.INVALID_PADDING?; - if (!padding) - { - switch (src.len) - { - case 2: - uint group = (uint)c0 << 18 | (uint)c1 << 12; - dst[0] = (char)(group >> 16); - case 3: - char c2 = alphabet.reverse[src[2]]; - if (c2 == 0xFF) return DecodingFailure.INVALID_CHARACTER?; - uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6; - dst[0] = (char)(group >> 16); - dst[1] = (char)(group >> 8); - } - } - else - { - // Valid paddings are: - // 2: xx== - // 1: xxx= - switch (padding) - { - case src[2]: - if (src[3] != padding) return DecodingFailure.INVALID_PADDING?; - uint group = (uint)c0 << 18 | (uint)c1 << 12; - dst[0] = (char)(group >> 16); - dn -= 2; - case src[3]: - char c2 = alphabet.reverse[src[2]]; - if (c2 == 0xFF) return DecodingFailure.INVALID_CHARACTER?; - uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6; - dst[0] = (char)(group >> 16); - dst[1] = (char)(group >> 8); - dn -= 1; - } - } - return dst_ptr[:dn]; -} - -const MASK @private = 0b111111; - -struct Base64Encoder @deprecated -{ - char padding; - String alphabet; -} - -fault Base64Error -{ - DUPLICATE_IN_ALPHABET, - PADDING_IN_ALPHABET, - DESTINATION_TOO_SMALL, - INVALID_PADDING, - INVALID_CHARACTER, -} - -<* - @param alphabet "The alphabet used for encoding." - @param padding "Set to a negative value to disable padding." - @require alphabet.len == 64 - @require padding < 256 - @return! Base64Error.DUPLICATE_IN_ALPHABET, Base64Error.PADDING_IN_ALPHABET -*> -fn Base64Encoder*! Base64Encoder.init(&self, String alphabet, int padding = '=') -{ - check_alphabet(alphabet, padding)!; - *self = { .padding = padding < 0 ? 0 : (char)padding, .alphabet = alphabet }; - return self; -} - -<* - Calculate the size of the encoded data. - @param n "Size of the input to be encoded." - @return "The size of the input once encoded." -*> -fn usz Base64Encoder.encode_len(&self, usz n) -{ - return encode_len(n, self.padding); -} - -<* - Encode the content of src into dst, which must be properly sized. - @param src "The input to be encoded." - @param dst "The encoded input." - @return "The encoded size." - @return! Base64Error.DESTINATION_TOO_SMALL -*> -fn usz! Base64Encoder.encode(&self, char[] src, char[] dst) -{ - if (src.len == 0) return 0; - usz dn = self.encode_len(src.len); - if (dst.len < dn) return Base64Error.DESTINATION_TOO_SMALL?; - Base64Alphabet a = { .encoding = self.alphabet[:64] }; - return encode_buffer(src, dst, self.padding, &a).len; -} - -struct Base64Decoder @deprecated -{ - char padding; - Base64Alphabet encoding; - bool init_done; -} - -import std; -<* - @param alphabet "The alphabet used for encoding." - @param padding "Set to a negative value to disable padding." - @require alphabet.len == 64 - @require padding < 256 - @return! Base64Error.DUPLICATE_IN_ALPHABET, Base64Error.PADDING_IN_ALPHABET -*> -fn void! Base64Decoder.init(&self, String alphabet, int padding = '=') -{ - self.init_done = true; - check_alphabet(alphabet, padding)!; - *self = { .padding = padding < 0 ? 0 : (char)padding, .encoding.encoding = alphabet[:64] }; - - self.encoding.reverse[..] = 0xFF; - - foreach (i, c : alphabet) - { - self.encoding.reverse[c] = (char)i; - } -} - -<* - Calculate the size of the decoded data. - @param n "Size of the input to be decoded." - @return "The size of the input once decoded." - @return! Base64Error.INVALID_PADDING -*> -fn usz! Base64Decoder.decode_len(&self, usz n) -{ - return decode_len(n, self.padding) ?? Base64Error.INVALID_PADDING?; -} - -<* - Decode the content of src into dst, which must be properly sized. - @param src "The input to be decoded." - @param dst "The decoded input." - @return "The decoded size." - @return! Base64Error.DESTINATION_TOO_SMALL, Base64Error.INVALID_PADDING, Base64Error.INVALID_CHARACTER -*> -fn usz! Base64Decoder.decode(&self, char[] src, char[] dst) -{ - if (src.len == 0) return 0; - usz dn = self.decode_len(src.len)!; - if (dst.len < dn) return Base64Error.DESTINATION_TOO_SMALL?; - char[]! decoded = decode_buffer(src, dst, self.padding, &self.encoding); - if (catch err = decoded) - { - case DecodingFailure.INVALID_PADDING: - return Base64Error.INVALID_PADDING?; - case DecodingFailure.INVALID_CHARACTER: - return Base64Error.INVALID_CHARACTER?; - default: - return err?; - } - return decoded.len; -} - -// Make sure that all bytes in the alphabet are unique and -// the padding is not present in the alphabet. -fn void! check_alphabet(String alphabet, int padding) @local -{ - bool[256] checked; - if (padding < 0) - { - foreach (c : alphabet) - { - if (checked[c]) return Base64Error.DUPLICATE_IN_ALPHABET?; - checked[c] = true; - } - return; - } - char pad = (char)padding; - foreach (c : alphabet) - { - if (c == pad) return Base64Error.PADDING_IN_ALPHABET?; - if (checked[c]) return Base64Error.DUPLICATE_IN_ALPHABET?; - checked[c] = true; - } -} - diff --git a/lib7/std/encoding/csv.c3 b/lib7/std/encoding/csv.c3 deleted file mode 100644 index b24380c29..000000000 --- a/lib7/std/encoding/csv.c3 +++ /dev/null @@ -1,89 +0,0 @@ -module std::encoding::csv; -import std::io; - - -struct CsvReader -{ - InStream stream; - String separator; -} - -struct CsvRow (Printable) -{ - String[] list; - String row; - Allocator allocator; -} - -fn usz! CsvRow.to_format(&self, Formatter* f) @dynamic -{ - return f.printf("%s", self.list); -} - -fn usz CsvRow.len(&self) @operator(len) -{ - return self.list.len; -} - -<* - @require col < self.list.len -*> -fn String CsvRow.get_col(&self, usz col) @operator([]) -{ - return self.list[col]; -} - -fn void CsvReader.init(&self, InStream stream, String separator = ",") -{ - self.stream = stream; - self.separator = separator; -} -<* - @param [&inout] allocator -*> -fn CsvRow! CsvReader.read_row(self, Allocator allocator) -{ - String row = io::readline(allocator, self.stream)!; - defer catch allocator::free(allocator, row); - String[] list = row.split(allocator, self.separator); - return { list, row, allocator }; -} - -fn CsvRow! CsvReader.tread_row(self) -{ - return self.read_row(tmem()) @inline; -} - -<* - @require self.allocator != null `Row already freed` -*> -fn void CsvRow.free(&self) -{ - allocator::free(self.allocator, self.list); - allocator::free(self.allocator, self.row); - self.allocator = null; -} - -fn void! CsvReader.skip_row(self) @maydiscard => @pool() -{ - (void)io::treadline(self.stream); -} - -macro void! CsvReader.@each_row(self, int rows = int.max; @body(String[] row)) @maydiscard -{ - InStream stream = self.stream; - String sep = self.separator; - while (rows--) - { - @stack_mem(512; Allocator mem) - { - String! s = io::readline(mem, stream); - if (catch err = s) - { - if (err == IoError.EOF) return; - return err?; - } - @body(s.split(mem, sep)); - }; - } -} \ No newline at end of file diff --git a/lib7/std/encoding/encoding.c3 b/lib7/std/encoding/encoding.c3 deleted file mode 100644 index b81b4e945..000000000 --- a/lib7/std/encoding/encoding.c3 +++ /dev/null @@ -1,7 +0,0 @@ -module std::encoding; - -fault DecodingFailure -{ - INVALID_CHARACTER, - INVALID_PADDING, -} \ No newline at end of file diff --git a/lib7/std/encoding/hex.c3 b/lib7/std/encoding/hex.c3 deleted file mode 100644 index 38ef23a81..000000000 --- a/lib7/std/encoding/hex.c3 +++ /dev/null @@ -1,108 +0,0 @@ -module std::encoding::hex; -import std::encoding @norecurse; - -// The implementation is based on https://www.rfc-editor.org/rfc/rfc4648 - -fn String encode_buffer(char[] code, char[] buffer) -{ - return (String)buffer[:encode_bytes(code, buffer)]; -} - -fn char[]! decode_buffer(char[] code, char[] buffer) -{ - return buffer[:decode_bytes(code, buffer)!]; -} - -fn String encode(Allocator allocator, char[] code) -{ - char[] data = allocator::alloc_array(allocator, char, encode_len(code.len)); - return (String)data[:encode_bytes(code, data)]; -} - -fn char[]! decode(Allocator allocator, char[] code) -{ - char[] data = allocator::alloc_array(allocator, char, decode_len(code.len)); - return data[:decode_bytes(code, data)!]; -} - -fn String tencode(char[] code) @inline => encode(tmem(), code); -fn char[]! tdecode(char[] code) @inline => decode(tmem(), code); - - -<* - Calculate the size of the encoded data. - @param n "Size of the input to be encoded." - @return "The size of the input once encoded." -*> -fn usz encode_len(usz n) => n * 2; - -<* - Encode the content of src into dst, which must be properly sized. - @param src "The input to be encoded." - @param dst "The encoded input." - @return "The encoded size." - @require dst.len >= encode_len(src.len) "Destination array is not large enough" -*> -fn usz encode_bytes(char[] src, char[] dst) -{ - usz j = 0; - foreach (v : src) - { - dst[j] = HEXALPHABET[v >> 4]; - dst[j + 1] = HEXALPHABET[v & 0x0f]; - j = j + 2; - } - return src.len * 2; -} - -<* - Calculate the size of the decoded data. - @param n "Size of the input to be decoded." - @return "The size of the input once decoded." -*> -macro usz decode_len(usz n) => n / 2; - -<* - Decodes src into bytes. Returns the actual number of bytes written to dst. - - Expects that src only contains hexadecimal characters and that src has even - length. - - @param src "The input to be decoded." - @param dst "The decoded input." - @require src.len % 2 == 0 "src is not of even length" - @require dst.len >= decode_len(src.len) "Destination array is not large enough" - @return! DecodingFailure.INVALID_CHARACTER -*> -fn usz! decode_bytes(char[] src, char[] dst) -{ - usz i; - for (usz j = 1; j < src.len; j += 2) - { - char a = HEXREVERSE[src[j - 1]]; - char b = HEXREVERSE[src[j]]; - if (a > 0x0f || b > 0x0f) return DecodingFailure.INVALID_CHARACTER?; - dst[i] = (a << 4) | b; - i++; - } - return i; -} - -const char[?] HEXALPHABET @private = "0123456789abcdef"; -const char[?] HEXREVERSE @private = -x`ffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffff - 00010203040506070809ffffffffffff - ff0a0b0c0d0e0fffffffffffffffffff - ffffffffffffffffffffffffffffffff - ff0a0b0c0d0e0fffffffffffffffffff - ffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffff - ffffffffffffffffffffffffffffffff`; diff --git a/lib7/std/encoding/json.c3 b/lib7/std/encoding/json.c3 deleted file mode 100644 index 648dc98fb..000000000 --- a/lib7/std/encoding/json.c3 +++ /dev/null @@ -1,392 +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::encoding::json; -import std::io; -import std::ascii; -import std::collections::object; - -fault JsonParsingError -{ - EOF, - UNEXPECTED_CHARACTER, - INVALID_ESCAPE_SEQUENCE, - DUPLICATE_MEMBERS, - INVALID_NUMBER, -} - -fn Object*! parse_string(Allocator allocator, String s) -{ - return parse(allocator, (ByteReader){}.init(s)); -} - -fn Object*! tparse_string(String s) -{ - return parse(tmem(), (ByteReader){}.init(s)); -} - -fn Object*! parse(Allocator allocator, InStream s) -{ - @stack_mem(512; Allocator smem) - { - JsonContext context = { .last_string = dstring::new_with_capacity(smem, 64), .stream = s, .allocator = allocator }; - @pool(allocator) - { - return parse_any(&context); - }; - }; -} - -fn Object*! tparse(InStream s) -{ - return parse(tmem(), s); -} - -// -- Implementation follows -- - -enum JsonTokenType @local -{ - NO_TOKEN, - LBRACE, - LBRACKET, - COMMA, - COLON, - RBRACE, - RBRACKET, - STRING, - NUMBER, - TRUE, - FALSE, - NULL, - EOF, -} - -struct JsonContext @local -{ - uint line; - InStream stream; - Allocator allocator; - JsonTokenType token; - DString last_string; - double last_number; - char current; - bitstruct : char { - bool skip_comments; - bool reached_end; - bool pushed_back; - } -} - - -fn Object*! parse_from_token(JsonContext* context, JsonTokenType token) @local -{ - switch (token) - { - case NO_TOKEN: unreachable(); - case LBRACE: return parse_map(context); - case LBRACKET: return parse_array(context); - case COMMA: - case RBRACE: - case RBRACKET: - case COLON: return JsonParsingError.UNEXPECTED_CHARACTER?; - case STRING: return object::new_string(context.last_string.str_view(), context.allocator); - case NUMBER: return object::new_float(context.last_number, context.allocator); - case TRUE: return object::new_bool(true); - case FALSE: return object::new_bool(false); - case NULL: return object::new_null(); - case EOF: return JsonParsingError.EOF?; - } -} -fn Object*! parse_any(JsonContext* context) @local -{ - return parse_from_token(context, advance(context)); -} - -fn JsonTokenType! lex_number(JsonContext *context, char c) @local -{ - @stack_mem(256; Allocator mem) - { - DString t = dstring::new_with_capacity(mem, 32); - bool negate = c == '-'; - if (negate) - { - t.append(c); - c = read_next(context)!; - } - while (c.is_digit()) - { - t.append(c); - c = read_next(context)!; - } - if (c == '.') - { - t.append(c); - while (c = read_next(context)!, c.is_digit()) - { - t.append(c); - } - } - if ((c | 32) == 'e') - { - t.append(c); - c = read_next(context)!; - switch (c) - { - case '-': - case '+': - t.append(c); - c = read_next(context)!; - } - if (!c.is_digit()) return JsonParsingError.INVALID_NUMBER?; - while (c.is_digit()) - { - t.append(c); - c = read_next(context)!; - } - } - pushback(context, c); - double! d = t.str_view().to_double() ?? JsonParsingError.INVALID_NUMBER?; - context.last_number = d!; - return NUMBER; - }; -} - -fn Object*! parse_map(JsonContext* context) @local -{ - Object* map = object::new_obj(context.allocator); - defer catch map.free(); - JsonTokenType token = advance(context)!; - - @stack_mem(256; Allocator mem) - { - DString temp_key = dstring::new_with_capacity(mem, 32); - while (token != JsonTokenType.RBRACE) - { - if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?; - DString string = context.last_string; - if (map.has_key(string.str_view())) return JsonParsingError.DUPLICATE_MEMBERS?; - // Copy the key to our temp holder, since our - // last_string may be used in parse_any - temp_key.clear(); - temp_key.append(string); - parse_expected(context, COLON)!; - Object* element = parse_any(context)!; - map.set(temp_key.str_view(), element); - token = advance(context)!; - if (token == JsonTokenType.COMMA) - { - token = advance(context)!; - continue; - } - if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER?; - } - return map; - }; -} - -fn Object*! parse_array(JsonContext* context) @local -{ - Object* list = object::new_obj(context.allocator); - defer catch list.free(); - JsonTokenType token = advance(context)!; - while (token != JsonTokenType.RBRACKET) - { - Object* element = parse_from_token(context, token)!; - list.push(element); - token = advance(context)!; - if (token == JsonTokenType.COMMA) - { - token = advance(context)!; - continue; - } - if (token != JsonTokenType.RBRACKET) return JsonParsingError.UNEXPECTED_CHARACTER?; - } - return list; -} - -fn void pushback(JsonContext* context, char c) @local -{ - if (!context.reached_end) - { - assert(!context.pushed_back); - context.pushed_back = true; - context.current = c; - } -} - -fn char! read_next(JsonContext* context) @local -{ - if (context.reached_end) return '\0'; - if (context.pushed_back) - { - context.pushed_back = false; - return context.current; - } - char! c = context.stream.read_byte(); - if (catch err = c) - { - case IoError.EOF: - context.reached_end = true; - return '\0'; - default: - return err?; - } - if (c == 0) - { - context.reached_end = true; - } - return c; -} - -fn JsonTokenType! advance(JsonContext* context) @local -{ - char c; - // Skip whitespace - while WS: (c = read_next(context)!) - { - switch (c) - { - case '\n': - context.line++; - nextcase; - case ' ': - case '\t': - case '\r': - case '\v': - continue; - case '/': - if (!context.skip_comments) break; - c = read_next(context)!; - if (c != '*') - { - pushback(context, c); - break WS; - } - while COMMENT: (true) - { - // Skip to */ - while (c = read_next(context)!) - { - if (c == '\n') context.line++; - if (c != '*') continue; - // Skip through all the '*' - while (c = read_next(context)!) - { - if (c == '\n') context.line++; - if (c != '*') break; - } - if (c == '/') break COMMENT; - } - } - continue; - default: - break WS; - } - } - switch (c) - { - case '\0': - return IoError.EOF?; - case '{': - return LBRACE; - case '}': - return RBRACE; - case '[': - return LBRACKET; - case ']': - return RBRACKET; - case ':': - return COLON; - case ',': - return COMMA; - case '"': - return lex_string(context); - case '-': - case '0'..'9': - return lex_number(context, c); - case 't': - match(context, "rue")!; - return TRUE; - case 'f': - match(context, "alse")!; - return FALSE; - case 'n': - match(context, "ull")!; - return NULL; - default: - return JsonParsingError.UNEXPECTED_CHARACTER?; - } -} - -fn void! match(JsonContext* context, String str) @local -{ - foreach (c : str) - { - char l = read_next(context)!; - if (l != c) return JsonParsingError.UNEXPECTED_CHARACTER?; - } -} - -fn void! parse_expected(JsonContext* context, JsonTokenType token) @local -{ - if (advance(context)! != token) return JsonParsingError.UNEXPECTED_CHARACTER?; -} - -fn JsonTokenType! lex_string(JsonContext* context) -{ - context.last_string.clear(); - while LOOP: (true) - { - char c = read_next(context)!; - switch (c) - { - case '\0': - return JsonParsingError.EOF?; - case 1..31: - return JsonParsingError.UNEXPECTED_CHARACTER?; - case '"': - break LOOP; - case '\\': - break; - default: - context.last_string.append(c); - continue; - } - c = read_next(context)!; - switch (c) - { - case '\0': - return JsonParsingError.EOF?; - case 1..31: - return JsonParsingError.UNEXPECTED_CHARACTER?; - case '"': - case '\\': - case '/': - break; - case 'b': - c = '\b'; - case 'f': - c = '\f'; - case 'n': - c = '\n'; - case 'r': - c = '\r'; - case 't': - c = '\t'; - case 'u': - uint val; - for (int i = 0; i < 4; i++) - { - c = read_next(context)!; - if (!c.is_xdigit()) return JsonParsingError.INVALID_ESCAPE_SEQUENCE?; - val = val << 4 + (c > '9' ? (c | 32) - 'a' + 10 : c - '0'); - } - context.last_string.append_char32(val); - continue; - default: - return JsonParsingError.INVALID_ESCAPE_SEQUENCE?; - } - context.last_string.append(c); - } - return STRING; -} diff --git a/lib7/std/experimental/FrameScheduler.c3 b/lib7/std/experimental/FrameScheduler.c3 deleted file mode 100644 index c8bb7b1bf..000000000 --- a/lib7/std/experimental/FrameScheduler.c3 +++ /dev/null @@ -1,94 +0,0 @@ -module std::experimental::scheduler{Event}; -import std::collections, std::thread, std::time; - -struct DelayedSchedulerEvent @local -{ - inline Event event; - Clock execution_time; -} - -fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other) @local -{ - switch - { - case self.execution_time < other.execution_time: return -1; - case self.execution_time > other.execution_time: return 1; - default: return 0; - } -} - -struct FrameScheduler -{ - PriorityQueue{DelayedSchedulerEvent} delayed_events; - List{Event} events; - List{Event} pending_events; - bool pending; - Mutex mtx; -} - -fn void FrameScheduler.init(&self) -{ - self.events.init(mem); - self.pending_events.init(mem); - self.delayed_events.init(mem); - (void)self.mtx.init(); - bool pending; -} - -macro void FrameScheduler.@destroy(&self; @destruct(Event e)) -{ - foreach (e : self.events) @destruct(e); - foreach (e : self.pending_events) @destruct(e); - foreach (e : self.delayed_events.heap) @destruct(e.event); - self.events.free(); - self.pending_events.free(); - self.delayed_events.free(); - (void)self.mtx.destroy(); -} - -fn void FrameScheduler.queue_delayed_event(&self, Event event, Duration delay) -{ - self.mtx.@in_lock() - { - self.delayed_events.push({ event, clock::now().add_duration(delay)}); - @atomic_store(self.pending, true); - }; -} - -fn bool FrameScheduler.has_delayed(&self) -{ - self.mtx.@in_lock() - { - return @ok(self.delayed_events.first()); - }; -} - -fn void FrameScheduler.queue_event(&self, Event event) -{ - self.mtx.@in_lock() - { - self.pending_events.push(event); - @atomic_store(self.pending, true); - }; -} -fn Event! FrameScheduler.pop_event(&self) -{ - while (true) - { - if (try event = self.events.pop()) return event; - if (!@atomic_load(self.pending)) return IteratorResult.NO_MORE_ELEMENT?; - self.mtx.@in_lock() - { - self.events.add_all(&self.pending_events); - self.pending_events.clear(); - Clock c = clock::now(); - while (try top = self.delayed_events.first()) - { - if (top.execution_time > c) break; - self.events.push(self.delayed_events.pop()!!); - } - @atomic_store(self.pending, self.delayed_events.len() > 0); - if (!self.events.len()) return IteratorResult.NO_MORE_ELEMENT?; - }; - } -} diff --git a/lib7/std/hash/adler32.c3 b/lib7/std/hash/adler32.c3 deleted file mode 100644 index f6b770cc7..000000000 --- a/lib7/std/hash/adler32.c3 +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2021 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::hash::adler32; - -const uint ADLER_CONST @private = 65521; - -struct Adler32 -{ - uint a; - uint b; -} - -fn void Adler32.init(&self) -{ - *self = { 1, 0 }; -} - -fn void Adler32.updatec(&self, char c) -{ - self.a = (self.a + c) % ADLER_CONST; - self.b = (self.b + self.a) % ADLER_CONST; -} - -fn void Adler32.update(&self, char[] data) -{ - uint a = self.a; - uint b = self.b; - foreach (char x : data) - { - a = (a + x) % ADLER_CONST; - b = (b + a) % ADLER_CONST; - } - *self = { a, b }; -} - -fn uint Adler32.final(&self) -{ - return (self.b << 16) | self.a; -} - -fn uint encode(char[] data) -{ - uint a = 1; - uint b = 0; - foreach (char x : data) - { - a = (a + x) % ADLER_CONST; - b = (b + a) % ADLER_CONST; - } - return (b << 16) | a; -} \ No newline at end of file diff --git a/lib7/std/hash/crc32.c3 b/lib7/std/hash/crc32.c3 deleted file mode 100644 index d34eff23e..000000000 --- a/lib7/std/hash/crc32.c3 +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2021 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::hash::crc32; - -struct Crc32 -{ - uint result; -} - -fn void Crc32.init(&self, uint seed = 0) -{ - self.result = ~seed; -} - -fn void Crc32.updatec(&self, char c) -{ - self.result = (self.result >> 8) ^ CRC32_TABLE[(self.result ^ c) & 0xFF]; -} - -fn void Crc32.update(&self, char[] data) -{ - uint result = self.result; - foreach (char x : data) - { - result = (result >> 8) ^ CRC32_TABLE[(result ^ x) & 0xFF]; - } - self.result = result; -} - -fn uint Crc32.final(&self) -{ - return ~self.result; -} - -fn uint encode(char[] data) -{ - uint result = ~(uint)(0); - foreach (char x : data) - { - result = (result >> 8) ^ CRC32_TABLE[(result ^ x) & 0xFF]; - } - return ~result; -} - -const uint[256] CRC32_TABLE @private = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, -}; \ No newline at end of file diff --git a/lib7/std/hash/crc64.c3 b/lib7/std/hash/crc64.c3 deleted file mode 100644 index 718fc4b4a..000000000 --- a/lib7/std/hash/crc64.c3 +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2021 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::hash::crc64; - -struct Crc64 -{ - ulong result; -} - -fn void Crc64.init(&self, uint seed = 0) -{ - self.result = seed; -} - -fn void Crc64.updatec(&self, char c) -{ - self.result = (self.result << 8) ^ CRC64_TABLE[(char)((self.result >> 56) ^ c)]; -} - -fn void Crc64.update(&self, char[] data) -{ - ulong result = self.result; - foreach (char x : data) - { - result = (result << 8) ^ CRC64_TABLE[(char)((result >> 56) ^ x)]; - } - self.result = result; -} - -fn ulong Crc64.final(&self) -{ - return self.result; -} - -fn ulong encode(char[] data) -{ - ulong result = (ulong)(0); - foreach (char x : data) - { - result = (result << 8) ^ CRC64_TABLE[(char)((result >> 56) ^ x)]; - } - return result; -} - -const ulong[256] CRC64_TABLE @private = { - 0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5, - 0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a, - 0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b, - 0xdb55aacf12c73561, 0x99a54b24bb2d03f2, 0x5eb4691841135847, 0x1c4488f3e8f96ed4, - 0x663d78ff90e185ef, 0x24cd9914390bb37c, 0xe3dcbb28c335e8c9, 0xa12c5ac36adfde5a, - 0x2f0e1eba9ea36930, 0x6dfeff5137495fa3, 0xaaefdd6dcd770416, 0xe81f3c86649d3285, - 0xf45bb4758c645c51, 0xb6ab559e258e6ac2, 0x71ba77a2dfb03177, 0x334a9649765a07e4, - 0xbd68d2308226b08e, 0xff9833db2bcc861d, 0x388911e7d1f2dda8, 0x7a79f00c7818eb3b, - 0xcc7af1ff21c30bde, 0x8e8a101488293d4d, 0x499b3228721766f8, 0x0b6bd3c3dbfd506b, - 0x854997ba2f81e701, 0xc7b97651866bd192, 0x00a8546d7c558a27, 0x4258b586d5bfbcb4, - 0x5e1c3d753d46d260, 0x1cecdc9e94ace4f3, 0xdbfdfea26e92bf46, 0x990d1f49c77889d5, - 0x172f5b3033043ebf, 0x55dfbadb9aee082c, 0x92ce98e760d05399, 0xd03e790cc93a650a, - 0xaa478900b1228e31, 0xe8b768eb18c8b8a2, 0x2fa64ad7e2f6e317, 0x6d56ab3c4b1cd584, - 0xe374ef45bf6062ee, 0xa1840eae168a547d, 0x66952c92ecb40fc8, 0x2465cd79455e395b, - 0x3821458aada7578f, 0x7ad1a461044d611c, 0xbdc0865dfe733aa9, 0xff3067b657990c3a, - 0x711223cfa3e5bb50, 0x33e2c2240a0f8dc3, 0xf4f3e018f031d676, 0xb60301f359dbe0e5, - 0xda050215ea6c212f, 0x98f5e3fe438617bc, 0x5fe4c1c2b9b84c09, 0x1d14202910527a9a, - 0x93366450e42ecdf0, 0xd1c685bb4dc4fb63, 0x16d7a787b7faa0d6, 0x5427466c1e109645, - 0x4863ce9ff6e9f891, 0x0a932f745f03ce02, 0xcd820d48a53d95b7, 0x8f72eca30cd7a324, - 0x0150a8daf8ab144e, 0x43a04931514122dd, 0x84b16b0dab7f7968, 0xc6418ae602954ffb, - 0xbc387aea7a8da4c0, 0xfec89b01d3679253, 0x39d9b93d2959c9e6, 0x7b2958d680b3ff75, - 0xf50b1caf74cf481f, 0xb7fbfd44dd257e8c, 0x70eadf78271b2539, 0x321a3e938ef113aa, - 0x2e5eb66066087d7e, 0x6cae578bcfe24bed, 0xabbf75b735dc1058, 0xe94f945c9c3626cb, - 0x676dd025684a91a1, 0x259d31cec1a0a732, 0xe28c13f23b9efc87, 0xa07cf2199274ca14, - 0x167ff3eacbaf2af1, 0x548f120162451c62, 0x939e303d987b47d7, 0xd16ed1d631917144, - 0x5f4c95afc5edc62e, 0x1dbc74446c07f0bd, 0xdaad56789639ab08, 0x985db7933fd39d9b, - 0x84193f60d72af34f, 0xc6e9de8b7ec0c5dc, 0x01f8fcb784fe9e69, 0x43081d5c2d14a8fa, - 0xcd2a5925d9681f90, 0x8fdab8ce70822903, 0x48cb9af28abc72b6, 0x0a3b7b1923564425, - 0x70428b155b4eaf1e, 0x32b26afef2a4998d, 0xf5a348c2089ac238, 0xb753a929a170f4ab, - 0x3971ed50550c43c1, 0x7b810cbbfce67552, 0xbc902e8706d82ee7, 0xfe60cf6caf321874, - 0xe224479f47cb76a0, 0xa0d4a674ee214033, 0x67c58448141f1b86, 0x253565a3bdf52d15, - 0xab1721da49899a7f, 0xe9e7c031e063acec, 0x2ef6e20d1a5df759, 0x6c0603e6b3b7c1ca, - 0xf6fae5c07d3274cd, 0xb40a042bd4d8425e, 0x731b26172ee619eb, 0x31ebc7fc870c2f78, - 0xbfc9838573709812, 0xfd39626eda9aae81, 0x3a28405220a4f534, 0x78d8a1b9894ec3a7, - 0x649c294a61b7ad73, 0x266cc8a1c85d9be0, 0xe17dea9d3263c055, 0xa38d0b769b89f6c6, - 0x2daf4f0f6ff541ac, 0x6f5faee4c61f773f, 0xa84e8cd83c212c8a, 0xeabe6d3395cb1a19, - 0x90c79d3fedd3f122, 0xd2377cd44439c7b1, 0x15265ee8be079c04, 0x57d6bf0317edaa97, - 0xd9f4fb7ae3911dfd, 0x9b041a914a7b2b6e, 0x5c1538adb04570db, 0x1ee5d94619af4648, - 0x02a151b5f156289c, 0x4051b05e58bc1e0f, 0x87409262a28245ba, 0xc5b073890b687329, - 0x4b9237f0ff14c443, 0x0962d61b56fef2d0, 0xce73f427acc0a965, 0x8c8315cc052a9ff6, - 0x3a80143f5cf17f13, 0x7870f5d4f51b4980, 0xbf61d7e80f251235, 0xfd913603a6cf24a6, - 0x73b3727a52b393cc, 0x31439391fb59a55f, 0xf652b1ad0167feea, 0xb4a25046a88dc879, - 0xa8e6d8b54074a6ad, 0xea16395ee99e903e, 0x2d071b6213a0cb8b, 0x6ff7fa89ba4afd18, - 0xe1d5bef04e364a72, 0xa3255f1be7dc7ce1, 0x64347d271de22754, 0x26c49cccb40811c7, - 0x5cbd6cc0cc10fafc, 0x1e4d8d2b65facc6f, 0xd95caf179fc497da, 0x9bac4efc362ea149, - 0x158e0a85c2521623, 0x577eeb6e6bb820b0, 0x906fc95291867b05, 0xd29f28b9386c4d96, - 0xcedba04ad0952342, 0x8c2b41a1797f15d1, 0x4b3a639d83414e64, 0x09ca82762aab78f7, - 0x87e8c60fded7cf9d, 0xc51827e4773df90e, 0x020905d88d03a2bb, 0x40f9e43324e99428, - 0x2cffe7d5975e55e2, 0x6e0f063e3eb46371, 0xa91e2402c48a38c4, 0xebeec5e96d600e57, - 0x65cc8190991cb93d, 0x273c607b30f68fae, 0xe02d4247cac8d41b, 0xa2dda3ac6322e288, - 0xbe992b5f8bdb8c5c, 0xfc69cab42231bacf, 0x3b78e888d80fe17a, 0x7988096371e5d7e9, - 0xf7aa4d1a85996083, 0xb55aacf12c735610, 0x724b8ecdd64d0da5, 0x30bb6f267fa73b36, - 0x4ac29f2a07bfd00d, 0x08327ec1ae55e69e, 0xcf235cfd546bbd2b, 0x8dd3bd16fd818bb8, - 0x03f1f96f09fd3cd2, 0x41011884a0170a41, 0x86103ab85a2951f4, 0xc4e0db53f3c36767, - 0xd8a453a01b3a09b3, 0x9a54b24bb2d03f20, 0x5d45907748ee6495, 0x1fb5719ce1045206, - 0x919735e51578e56c, 0xd367d40ebc92d3ff, 0x1476f63246ac884a, 0x568617d9ef46bed9, - 0xe085162ab69d5e3c, 0xa275f7c11f7768af, 0x6564d5fde549331a, 0x279434164ca30589, - 0xa9b6706fb8dfb2e3, 0xeb46918411358470, 0x2c57b3b8eb0bdfc5, 0x6ea7525342e1e956, - 0x72e3daa0aa188782, 0x30133b4b03f2b111, 0xf7021977f9cceaa4, 0xb5f2f89c5026dc37, - 0x3bd0bce5a45a6b5d, 0x79205d0e0db05dce, 0xbe317f32f78e067b, 0xfcc19ed95e6430e8, - 0x86b86ed5267cdbd3, 0xc4488f3e8f96ed40, 0x0359ad0275a8b6f5, 0x41a94ce9dc428066, - 0xcf8b0890283e370c, 0x8d7be97b81d4019f, 0x4a6acb477bea5a2a, 0x089a2aacd2006cb9, - 0x14dea25f3af9026d, 0x562e43b4931334fe, 0x913f6188692d6f4b, 0xd3cf8063c0c759d8, - 0x5dedc41a34bbeeb2, 0x1f1d25f19d51d821, 0xd80c07cd676f8394, 0x9afce626ce85b507, -}; diff --git a/lib7/std/hash/fnv32a.c3 b/lib7/std/hash/fnv32a.c3 deleted file mode 100644 index 24e7a1bf7..000000000 --- a/lib7/std/hash/fnv32a.c3 +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2021 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::hash::fnv32a; - -distinct Fnv32a = uint; - -const FNV32A_START @private = 0x811c9dc5; -const FNV32A_MUL @private = 0x01000193; - -macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV32A_MUL; - -fn void Fnv32a.init(&self) -{ - *self = FNV32A_START; -} - -fn void Fnv32a.update(&self, char[] data) -{ - Fnv32a h = *self; - foreach (char x : data) - { - update(&h, x); - } - *self = h; -} - -macro void Fnv32a.update_char(&self, char c) -{ - update(self, c); -} - -fn uint encode(char[] data) -{ - uint h = FNV32A_START; - foreach (char x : data) - { - update(&h, x); - } - return h; -} diff --git a/lib7/std/hash/fnv64a.c3 b/lib7/std/hash/fnv64a.c3 deleted file mode 100644 index 9b4f36378..000000000 --- a/lib7/std/hash/fnv64a.c3 +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2021 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::hash::fnv64a; - -distinct Fnv64a = ulong; - -const FNV64A_START @private = 0xcbf29ce484222325; -const FNV64A_MUL @private = 0x00000100000001b3; - -macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV64A_MUL; - -fn void Fnv64a.init(&self) -{ - *self = FNV64A_START; -} - -fn void Fnv64a.update(&self, char[] data) -{ - Fnv64a h = *self; - foreach (char x : data) - { - update(&h, x); - } - *self = h; -} - -macro void Fnv64a.update_char(&self, char c) -{ - update(self, c); -} - -fn ulong encode(char[] data) -{ - ulong h = FNV64A_START; - foreach (char x : data) - { - update(&h, x); - } - return h; -} diff --git a/lib7/std/hash/hmac.c3 b/lib7/std/hash/hmac.c3 deleted file mode 100644 index 2a511410d..000000000 --- a/lib7/std/hash/hmac.c3 +++ /dev/null @@ -1,107 +0,0 @@ -module std::hash::hmac{HashAlg, HASH_BYTES, BLOCK_BYTES}; -import std::crypto; - -struct Hmac -{ - HashAlg a, b; -} - -fn char[HASH_BYTES] hash(char[] key, char[] message) -{ - Hmac hmac @noinit; - hmac.init(key); - hmac.update(message); - return hmac.final(); -} - -<* - @require output.len > 0 "Output must be greater than zero" - @require output.len < int.max / HASH_BYTES "Output is too large" -*> -fn void pbkdf2(char[] pw, char[] salt, uint iterations, char[] output) -{ - usz l = output.len / HASH_BYTES; - usz r = output.len % HASH_BYTES; - - Hmac hmac; - hmac.init(pw); - - char[] dst_curr = output; - for (usz i = 1; i <= l; i++) - { - @derive(&hmac, salt, iterations, i, dst_curr[:HASH_BYTES]); - dst_curr = dst_curr[HASH_BYTES..]; - } - - if (r > 0) - { - char[HASH_BYTES] tmp; - @derive(&hmac, salt, iterations, l + 1, &tmp); - dst_curr[..] = tmp[:dst_curr.len]; - mem::zero_volatile(&tmp); - } -} - -fn void Hmac.init(&self, char[] key) -{ - char[BLOCK_BYTES] buffer; - if (key.len > BLOCK_BYTES) - { - self.a.init(); - self.a.update(key); - buffer[:HASH_BYTES] = self.a.final()[..]; - } - else - { - buffer[:key.len] = key[..]; - } - - foreach (&b : buffer) *b ^= IPAD; - - self.a.init(); - self.a.update(&buffer); - - foreach (&b : buffer) *b ^= IPAD ^ OPAD; - - self.b.init(); - self.b.update(&buffer); - - mem::zero_volatile(&buffer); -} - -fn void Hmac.update(&self, char[] data) -{ - self.a.update(data); -} - -fn char[HASH_BYTES] Hmac.final(&self) -{ - self.b.update(&&self.a.final()); - return self.b.final(); -} - -const IPAD @private = 0x36; -const OPAD @private = 0x5C; - -macro @derive(Hmac *hmac_start, char[] salt, uint iterations, usz index, char[] out) -{ - assert(out.len == HASH_BYTES); - char[HASH_BYTES] tmp @noinit; - defer mem::zero_volatile(&tmp); - Hmac hmac = *hmac_start; - hmac.update(salt); - UIntBE be = { (uint)index }; - hmac.update(&&bitcast(be, char[4])); - tmp = hmac.final(); - out[..] = tmp; - for (int it = 1; it < iterations; it++) - { - hmac = *hmac_start; - hmac.update(&tmp); - tmp = hmac.final(); - foreach (i, v : tmp) - { - out[i] ^= v; - } - } -} \ No newline at end of file diff --git a/lib7/std/hash/md5.c3 b/lib7/std/hash/md5.c3 deleted file mode 100644 index df6321798..000000000 --- a/lib7/std/hash/md5.c3 +++ /dev/null @@ -1,225 +0,0 @@ -module std::hash::md5; -import std::hash::hmac; -import std::bits; - -const BLOCK_BYTES = 64; -const HASH_BYTES = 16; - -struct Md5 -{ - uint lo, hi; - uint a, b, c, d; - char[64] buffer; - uint[16] block; -} - -def HmacMd5 = Hmac{Md5, HASH_BYTES, BLOCK_BYTES}; -def hmac = hmac::hash{Md5, HASH_BYTES, BLOCK_BYTES}; -def pbkdf2 = hmac::pbkdf2{Md5, HASH_BYTES, BLOCK_BYTES}; - -fn char[HASH_BYTES] hash(char[] data) -{ - Md5 md5; - md5.init(); - md5.update(data); - return md5.final(); -} - -fn void Md5.init(&self) -{ - self.a = 0x67452301; - self.b = 0xefcdab89; - self.c = 0x98badcfe; - self.d = 0x10325476; - - self.lo = 0; - self.hi = 0; -} - - -fn void Md5.update(&ctx, char[] data) -{ - uint saved_lo = ctx.lo; - if ((ctx.lo = (saved_lo + data.len) & 0x1fffffff) < saved_lo) ctx.hi++; - ctx.hi += data.len >> 29; - - usz used = (usz)saved_lo & 0x3f; - - if (used) - { - usz available = 64 - used; - - if (data.len < available) - { - ctx.buffer[used:data.len] = data[..]; - return; - } - ctx.buffer[used:available] = data[:available]; - data = data[available..]; - body(ctx, &ctx.buffer, 64); - } - - if (data.len >= 64) - { - data = body(ctx, data, data.len & ~(usz)0x3f)[:data.len & 0x3f]; - } - ctx.buffer[:data.len] = data[..]; -} - -fn char[HASH_BYTES] Md5.final(&ctx) -{ - usz used = (usz)ctx.lo & 0x3f; - ctx.buffer[used++] = 0x80; - - usz available = 64 - used; - - if (available < 8) - { - ctx.buffer[used:available] = 0; - body(ctx, &ctx.buffer, 64); - used = 0; - available = 64; - } - ctx.buffer[used:available - 8] = 0; - - ctx.lo <<= 3; - ctx.buffer[56:4] = bitcast(ctx.lo, char[4])[..]; - ctx.buffer[60:4] = bitcast(ctx.hi, char[4])[..]; - - body(ctx, &ctx.buffer, 64); - - char[16] res @noinit; - res[0:4] = bitcast(ctx.a, char[4]); - res[4:4] = bitcast(ctx.b, char[4]); - res[8:4] = bitcast(ctx.c, char[4]); - res[12:4] = bitcast(ctx.d, char[4]); - *ctx = {}; - return res; -} - -module std::hash::md5 @private; - -// Implementation -macro @f(x, y, z) => z ^ (x & (y ^ z)); -macro @g(x, y, z) => y ^ (z & (x ^ y)); -macro @h(x, y, z) => (x ^ y) ^ z; -macro @h2(x, y, z) => x ^ (y ^ z); -macro @i(x, y, z) => y ^ (x | ~z); - -macro @step(#f, a, b, c, d, ptr, n, t, s) -{ - *a += #f(b, c, d) + @unaligned_load(*(uint *)&ptr[n * 4], 2) + t; - *a = (*a << s) | ((*a & 0xffffffff) >> (32 - s)); - *a += b; -} - - -fn char* body(Md5* ctx, void* data, usz size) -{ - char* ptr; - uint a, b, c, d; - uint saved_a, saved_b, saved_c, saved_d; - ptr = data; - a = ctx.a; - b = ctx.b; - c = ctx.c; - d = ctx.d; - - do - { - saved_a = a; - saved_b = b; - saved_c = c; - saved_d = d; - -/* Round 1 */ - @step(@f, &a, b, c, d, ptr, 0, 0xd76aa478, 7) ; - @step(@f, &d, a, b, c, ptr, 1, 0xe8c7b756, 12) ; - @step(@f, &c, d, a, b, ptr, 2, 0x242070db, 17) ; - @step(@f, &b, c, d, a, ptr, 3, 0xc1bdceee, 22) ; - @step(@f, &a, b, c, d, ptr, 4, 0xf57c0faf, 7) ; - @step(@f, &d, a, b, c, ptr, 5, 0x4787c62a, 12) ; - @step(@f, &c, d, a, b, ptr, 6, 0xa8304613, 17) ; - @step(@f, &b, c, d, a, ptr, 7, 0xfd469501, 22) ; - @step(@f, &a, b, c, d, ptr, 8, 0x698098d8, 7) ; - @step(@f, &d, a, b, c, ptr, 9, 0x8b44f7af, 12) ; - @step(@f, &c, d, a, b, ptr, 10, 0xffff5bb1, 17); - @step(@f, &b, c, d, a, ptr, 11, 0x895cd7be, 22); - @step(@f, &a, b, c, d, ptr, 12, 0x6b901122, 7) ; - @step(@f, &d, a, b, c, ptr, 13, 0xfd987193, 12); - @step(@f, &c, d, a, b, ptr, 14, 0xa679438e, 17); - @step(@f, &b, c, d, a, ptr, 15, 0x49b40821, 22); - -/* Round 2 */ - @step(@g, &a, b, c, d, ptr, 1, 0xf61e2562, 5) ; - @step(@g, &d, a, b, c, ptr, 6, 0xc040b340, 9) ; - @step(@g, &c, d, a, b, ptr, 11, 0x265e5a51, 14); - @step(@g, &b, c, d, a, ptr, 0, 0xe9b6c7aa, 20) ; - @step(@g, &a, b, c, d, ptr, 5, 0xd62f105d, 5) ; - @step(@g, &d, a, b, c, ptr, 10, 0x02441453, 9) ; - @step(@g, &c, d, a, b, ptr, 15, 0xd8a1e681, 14); - @step(@g, &b, c, d, a, ptr, 4, 0xe7d3fbc8, 20) ; - @step(@g, &a, b, c, d, ptr, 9, 0x21e1cde6, 5) ; - @step(@g, &d, a, b, c, ptr, 14, 0xc33707d6, 9) ; - @step(@g, &c, d, a, b, ptr, 3, 0xf4d50d87, 14) ; - @step(@g, &b, c, d, a, ptr, 8, 0x455a14ed, 20) ; - @step(@g, &a, b, c, d, ptr, 13, 0xa9e3e905, 5) ; - @step(@g, &d, a, b, c, ptr, 2, 0xfcefa3f8, 9) ; - @step(@g, &c, d, a, b, ptr, 7, 0x676f02d9, 14) ; - @step(@g, &b, c, d, a, ptr, 12, 0x8d2a4c8a, 20); - -/* Round 3 */ - @step(@h, &a, b, c, d, ptr, 5, 0xfffa3942, 4); - @step(@h2, &d, a, b, c, ptr, 8, 0x8771f681, 11); - @step(@h, &c, d, a, b, ptr, 11, 0x6d9d6122, 16); - @step(@h2, &b, c, d, a, ptr, 14, 0xfde5380c, 23); - @step(@h, &a, b, c, d, ptr, 1, 0xa4beea44, 4); - @step(@h2, &d, a, b, c, ptr, 4, 0x4bdecfa9, 11); - @step(@h, &c, d, a, b, ptr, 7, 0xf6bb4b60, 16); - @step(@h2, &b, c, d, a, ptr, 10, 0xbebfbc70, 23); - @step(@h, &a, b, c, d, ptr, 13, 0x289b7ec6, 4) ; - @step(@h2, &d, a, b, c, ptr, 0, 0xeaa127fa, 11) ; - @step(@h, &c, d, a, b, ptr, 3, 0xd4ef3085, 16) ; - @step(@h2, &b, c, d, a, ptr, 6, 0x04881d05, 23) ; - @step(@h, &a, b, c, d, ptr, 9, 0xd9d4d039, 4) ; - @step(@h2, &d, a, b, c, ptr, 12, 0xe6db99e5, 11) ; - @step(@h, &c, d, a, b, ptr, 15, 0x1fa27cf8, 16) ; - @step(@h2, &b, c, d, a, ptr, 2, 0xc4ac5665, 23) ; - -/* Round 4 */ - @step(@i, &a, b, c, d, ptr, 0, 0xf4292244, 6) ; - @step(@i, &d, a, b, c, ptr, 7, 0x432aff97, 10) ; - @step(@i, &c, d, a, b, ptr, 14, 0xab9423a7, 15) ; - @step(@i, &b, c, d, a, ptr, 5, 0xfc93a039, 21) ; - @step(@i, &a, b, c, d, ptr, 12, 0x655b59c3, 6) ; - @step(@i, &d, a, b, c, ptr, 3, 0x8f0ccc92, 10) ; - @step(@i, &c, d, a, b, ptr, 10, 0xffeff47d, 15) ; - @step(@i, &b, c, d, a, ptr, 1, 0x85845dd1, 21) ; - @step(@i, &a, b, c, d, ptr, 8, 0x6fa87e4f, 6) ; - @step(@i, &d, a, b, c, ptr, 15, 0xfe2ce6e0, 10) ; - @step(@i, &c, d, a, b, ptr, 6, 0xa3014314, 15) ; - @step(@i, &b, c, d, a, ptr, 13, 0x4e0811a1, 21) ; - @step(@i, &a, b, c, d, ptr, 4, 0xf7537e82, 6) ; - @step(@i, &d, a, b, c, ptr, 11, 0xbd3af235, 10) ; - @step(@i, &c, d, a, b, ptr, 2, 0x2ad7d2bb, 15) ; - @step(@i, &b, c, d, a, ptr, 9, 0xeb86d391, 21) ; - - a += saved_a; - b += saved_b; - c += saved_c; - d += saved_d; - - ptr += 64; - - } while (size -= 64); - - ctx.a = a; - ctx.b = b; - ctx.c = c; - ctx.d = d; - - return ptr; -} - - - diff --git a/lib7/std/hash/sha1.c3 b/lib7/std/hash/sha1.c3 deleted file mode 100644 index 4dad804f1..000000000 --- a/lib7/std/hash/sha1.c3 +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright (c) 2021 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. -// -// Implementation was off Steve Reid's SHA-1 C implementation - -module std::hash::sha1; -import std::hash::hmac; -import std::bits; - -const BLOCK_BYTES = 64; -const HASH_BYTES = 20; - -struct Sha1 -{ - uint[5] state; - uint[2] count; - char[BLOCK_BYTES] buffer; -} - -def HmacSha1 = Hmac{Sha1, HASH_BYTES, BLOCK_BYTES}; -def hmac = hmac::hash{Sha1, HASH_BYTES, BLOCK_BYTES}; -def pbkdf2 = hmac::pbkdf2{Sha1, HASH_BYTES, BLOCK_BYTES}; - -fn char[HASH_BYTES] hash(char[] data) -{ - Sha1 sha1 @noinit; - sha1.init(); - sha1.update(data); - return sha1.final(); -} - -fn void Sha1.init(&self) -{ - // SHA1 initialization constants - *self = { - .state = { - 0x67452301, - 0xEFCDAB89, - 0x98BADCFE, - 0x10325476, - 0xC3D2E1F0 - } - }; -} - -<* - @param [in] data - @require data.len <= uint.max -*> -fn void Sha1.update(&self, char[] data) -{ - uint j = self.count[0]; - uint len = data.len; - if ((self.count[0] += len << 3) < j) self.count[1]++; - self.count[1] += len >> 29; - j = (j >> 3) & 63; - uint i; - if (j + len > 63) - { - i = 64 - j; - self.buffer[j..] = data[:i]; - sha1_transform(&self.state, &self.buffer); - for (; i + 63 < len; i += 64) - { - sha1_transform(&self.state, &data[i]); - } - j = 0; - } - self.buffer[j:len - i] = data[i..]; -} - - -fn char[HASH_BYTES] Sha1.final(&self) -{ - char[8] finalcount; - for (uint i = 0; i < 8; i++) - { - finalcount[i] = (char)((self.count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 0xFF); - } - self.update((char[]){ 0o200 }); - while ((self.count[0] & 504) != 448) - { - self.update((char[]){ 0 }); - } - - self.update(&finalcount); - char[HASH_BYTES] digest; - for (uint i = 0; i < HASH_BYTES; i++) - { - digest[i] = (char)((self.state[i >> 2] >> ((3 - (i & 3)) * 8)) & 0xFF); - } - - // Clear mem - *self = {}; - finalcount = {}; - return digest; -} - -union Long16 @local -{ - char[BLOCK_BYTES] c; - uint[16] l; -} - -macro blk(Long16* block, i) @local -{ - return (block.l[i & 15] = (block.l[(i + 13) & 15] ^ block.l[(i + 8) & 15] - ^ block.l[(i + 2) & 15] ^ block.l[i & 15]).rotl(1)); -} - -macro blk0(Long16* block, i) @local -{ - $if env::BIG_ENDIAN: - return block.l[i]; - $else - return block.l[i] = (block.l[i].rotl(24) & 0xFF00FF00) - | (block.l[i].rotl(8) & 0x00FF00FF); - $endif -} - -macro r0(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local -{ - var w = *wref; - *z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + v.rotl(5); - *wref = w.rotl(30); -} - -macro r1(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local -{ - var w = *wref; - *z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + v.rotl(5); - *wref = w.rotl(30); -} - -macro r2(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local -{ - var w = *wref; - *z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + v.rotl(5); - *wref = w.rotl(30); -} - -macro r3(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local -{ - var w = *wref; - *z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + v.rotl(5); - *wref = w.rotl(30); -} - -macro r4(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local -{ - var w = *wref; - *z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + v.rotl(5); - *wref = w.rotl(30); -} - -<* - @param [&inout] state - @param [&in] buffer -*> -fn void sha1_transform(uint[5]* state, char* buffer) @local -{ - Long16 block; - block.c[..] = buffer[:64]; - uint a = (*state)[0]; - uint b = (*state)[1]; - uint c = (*state)[2]; - uint d = (*state)[3]; - uint e = (*state)[4]; - r0(&block, a, &b, c, d, &e, 0); - r0(&block, e, &a, b, c, &d, 1); - r0(&block, d, &e, a, b, &c, 2); - r0(&block, c, &d, e, a, &b, 3); - r0(&block, b, &c, d, e, &a, 4); - r0(&block, a, &b, c, d, &e, 5); - r0(&block, e, &a, b, c, &d, 6); - r0(&block, d, &e, a, b, &c, 7); - r0(&block, c, &d, e, a, &b, 8); - r0(&block, b, &c, d, e, &a, 9); - r0(&block, a, &b, c, d, &e, 10); - r0(&block, e, &a, b, c, &d, 11); - r0(&block, d, &e, a, b, &c, 12); - r0(&block, c, &d, e, a, &b, 13); - r0(&block, b, &c, d, e, &a, 14); - r0(&block, a, &b, c, d, &e, 15); - r1(&block, e, &a, b, c, &d, 16); - r1(&block, d, &e, a, b, &c, 17); - r1(&block, c, &d, e, a, &b, 18); - r1(&block, b, &c, d, e, &a, 19); - r2(&block, a, &b, c, d, &e, 20); - r2(&block, e, &a, b, c, &d, 21); - r2(&block, d, &e, a, b, &c, 22); - r2(&block, c, &d, e, a, &b, 23); - r2(&block, b, &c, d, e, &a, 24); - r2(&block, a, &b, c, d, &e, 25); - r2(&block, e, &a, b, c, &d, 26); - r2(&block, d, &e, a, b, &c, 27); - r2(&block, c, &d, e, a, &b, 28); - r2(&block, b, &c, d, e, &a, 29); - r2(&block, a, &b, c, d, &e, 30); - r2(&block, e, &a, b, c, &d, 31); - r2(&block, d, &e, a, b, &c, 32); - r2(&block, c, &d, e, a, &b, 33); - r2(&block, b, &c, d, e, &a, 34); - r2(&block, a, &b, c, d, &e, 35); - r2(&block, e, &a, b, c, &d, 36); - r2(&block, d, &e, a, b, &c, 37); - r2(&block, c, &d, e, a, &b, 38); - r2(&block, b, &c, d, e, &a, 39); - r3(&block, a, &b, c, d, &e, 40); - r3(&block, e, &a, b, c, &d, 41); - r3(&block, d, &e, a, b, &c, 42); - r3(&block, c, &d, e, a, &b, 43); - r3(&block, b, &c, d, e, &a, 44); - r3(&block, a, &b, c, d, &e, 45); - r3(&block, e, &a, b, c, &d, 46); - r3(&block, d, &e, a, b, &c, 47); - r3(&block, c, &d, e, a, &b, 48); - r3(&block, b, &c, d, e, &a, 49); - r3(&block, a, &b, c, d, &e, 50); - r3(&block, e, &a, b, c, &d, 51); - r3(&block, d, &e, a, b, &c, 52); - r3(&block, c, &d, e, a, &b, 53); - r3(&block, b, &c, d, e, &a, 54); - r3(&block, a, &b, c, d, &e, 55); - r3(&block, e, &a, b, c, &d, 56); - r3(&block, d, &e, a, b, &c, 57); - r3(&block, c, &d, e, a, &b, 58); - r3(&block, b, &c, d, e, &a, 59); - r4(&block, a, &b, c, d, &e, 60); - r4(&block, e, &a, b, c, &d, 61); - r4(&block, d, &e, a, b, &c, 62); - r4(&block, c, &d, e, a, &b, 63); - r4(&block, b, &c, d, e, &a, 64); - r4(&block, a, &b, c, d, &e, 65); - r4(&block, e, &a, b, c, &d, 66); - r4(&block, d, &e, a, b, &c, 67); - r4(&block, c, &d, e, a, &b, 68); - r4(&block, b, &c, d, e, &a, 69); - r4(&block, a, &b, c, d, &e, 70); - r4(&block, e, &a, b, c, &d, 71); - r4(&block, d, &e, a, b, &c, 72); - r4(&block, c, &d, e, a, &b, 73); - r4(&block, b, &c, d, e, &a, 74); - r4(&block, a, &b, c, d, &e, 75); - r4(&block, e, &a, b, c, &d, 76); - r4(&block, d, &e, a, b, &c, 77); - r4(&block, c, &d, e, a, &b, 78); - r4(&block, b, &c, d, e, &a, 79); - (*state)[0] += a; - (*state)[1] += b; - (*state)[2] += c; - (*state)[3] += d; - (*state)[4] += e; - a = b = c = d = e = 0; - block = {}; -} \ No newline at end of file diff --git a/lib7/std/hash/sha256.c3 b/lib7/std/hash/sha256.c3 deleted file mode 100644 index a5e86ff3b..000000000 --- a/lib7/std/hash/sha256.c3 +++ /dev/null @@ -1,176 +0,0 @@ -module std::hash::sha256; - -import std::hash::hmac; - -const BLOCK_SIZE = 64; -const HASH_SIZE = 32; - -const uint[64] K @local = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 -}; - -// Right rotate function -macro uint @rotr(uint x, uint n) @local => (((x) >> (n)) | ((x) << (32 - (n)))); - -// SHA-256 functions -macro uint @ch(uint x, uint y, uint z) @local => (x & y) ^ (~x & z); -macro uint @maj(uint x, uint y, uint z) @local => (x & y) ^ (x & z) ^ (y & z); -macro uint @_sigma0(uint x) @local => @rotr(x, 2) ^ @rotr(x, 13) ^ @rotr(x, 22); -macro uint @_sigma1(uint x) @local => @rotr(x, 6) ^ @rotr(x, 11) ^ @rotr(x, 25); -macro uint @sigma0(uint x) @local => @rotr(x, 7) ^ @rotr(x, 18) ^ (x >> 3); -macro uint @sigma1(uint x) @local => @rotr(x, 17) ^ @rotr(x, 19) ^ (x >> 10); - -struct Sha256 -{ - uint[8] state; - ulong bitcount; - char[BLOCK_SIZE] buffer; -} - -def HmacSha256 = Hmac{Sha256, HASH_SIZE, BLOCK_SIZE}; -def hmac = hmac::hash{Sha256, HASH_SIZE, BLOCK_SIZE}; -def pbkdf2 = hmac::pbkdf2{Sha256, HASH_SIZE, BLOCK_SIZE}; - -fn char[HASH_SIZE] hash(char[] data) -{ - Sha256 sha256 @noinit; - sha256.init(); - sha256.update(data); - return sha256.final(); -} - -fn void Sha256.init(&self) -{ - // Sha256 initialization constants - *self = { - .state = { - 0x6A09E667, - 0xBB67AE85, - 0x3C6EF372, - 0xA54FF53A, - 0x510E527F, - 0x9B05688C, - 0x1F83D9AB, - 0x5BE0CD19 - } - }; -} - -<* - @param [in] data - @require data.len <= uint.max -*> -fn void Sha256.update(&self, char[] data) { - uint i = 0; - uint len = data.len; - uint buffer_pos = (uint)(self.bitcount / 8) % BLOCK_SIZE; - self.bitcount += (ulong)(len * 8); - - while (len--) { - self.buffer[buffer_pos++] = data[i++]; - if (buffer_pos == BLOCK_SIZE) { - sha256_transform(&self.state, &self.buffer); - buffer_pos = 0; // Reset buffer position - } - } -} - -fn char[HASH_SIZE] Sha256.final(&self) { - char[HASH_SIZE] hash; - ulong i = (self.bitcount / 8) % BLOCK_SIZE; - - // Append 0x80 to the buffer - self.buffer[i++] = 0x80; - - // Pad the buffer with zeros - if (i > BLOCK_SIZE - 8) { - while (i < BLOCK_SIZE) { - self.buffer[i++] = 0x00; - } - sha256_transform(&self.state, &self.buffer); - i = 0; // Reset buffer index after transformation - } - - while (i < BLOCK_SIZE - 8) { - self.buffer[i++] = 0x00; - } - - // Append the bitcount in big-endian format - for (int j = 0; j < 8; ++j) { - self.buffer[BLOCK_SIZE - 8 + j] = (char)((self.bitcount >> (56 - j * 8)) & 0xFF); - } - - sha256_transform(&self.state, &self.buffer); - - // Convert state to the final hash - for (i = 0; i < 8; ++i) { - hash[i * 4] = (char)((self.state[i] >> 24) & 0xFF); - hash[i * 4 + 1] = (char)((self.state[i] >> 16) & 0xFF); - hash[i * 4 + 2] = (char)((self.state[i] >> 8) & 0xFF); - hash[i * 4 + 3] = (char)(self.state[i] & 0xFF); - } - - return hash; -} - -<* - @param [&inout] state - @param [&in] buffer -*> -fn void sha256_transform(uint* state, char* buffer) @local { - uint a, b, c, d, e, f, g, h, t1, t2; - uint[64] m; - int i; - - // Prepare the message schedule - for (i = 0; i < 16; ++i) { - m[i] = ((uint)buffer[i * 4] << 24) | ((uint)buffer[i * 4 + 1] << 16) | - ((uint)buffer[i * 4 + 2] << 8) | ((uint)buffer[i * 4 + 3]); // Ensure values are cast to uint for correct shifts - } - for (i = 16; i < 64; ++i) { - m[i] = @sigma1(m[i - 2]) + m[i - 7] + @sigma0(m[i - 15]) + m[i - 16]; - } - - // Initialize working variables - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - f = state[5]; - g = state[6]; - h = state[7]; - - // Perform the main SHA-256 compression function - for (i = 0; i < 64; ++i) { - t1 = h + @_sigma1(e) + @ch(e, f, g) + K[i] + m[i]; - t2 = @_sigma0(a) + @maj(a, b, c); - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - } - - // Update the state - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - state[5] += f; - state[6] += g; - state[7] += h; - a = b = c = d = e = f = g = h = t1 = t2 = i = 0; - m[:64] = buffer[:64] = 0; -} \ No newline at end of file diff --git a/lib7/std/io/bits.c3 b/lib7/std/io/bits.c3 deleted file mode 100644 index 653869979..000000000 --- a/lib7/std/io/bits.c3 +++ /dev/null @@ -1,94 +0,0 @@ -module std::io; - -struct BitReader -{ - InStream reader; - uint bits; - uint len; -} - -fn void BitReader.init(&self, InStream byte_reader) -{ - *self = { .reader = byte_reader }; -} - -fn void BitReader.clear(&self) @inline -{ - self.len = 0; -} - -<* - @require nbits <= 8 - @require self.len + nbits <= uint.sizeof * 8 -*> -fn char! BitReader.read_bits(&self, uint nbits) -{ - uint bits = self.bits; - if (self.len < nbits) - { - // New bits are pushed right. - char c = self.reader.read_byte()!; - bits <<= 8; - bits |= c; - self.bits = bits; - self.len += 8; - } - self.len -= nbits; - uint mask = (1 << nbits) - 1; - return (char)((bits >> self.len) & mask); -} - -struct BitWriter -{ - OutStream writer; - uint bits; - uint len; -} - -// c3 doesn't allow to shift more than bit width of a variable, -// so use closest byte boundary of 24 instead of 32 -const int WRITER_BITS = 24; - -fn void BitWriter.init(&self, OutStream byte_writer) -{ - *self = { .writer = byte_writer }; -} - -fn void! BitWriter.flush(&self) -{ - if (self.len == 0) return; - - int padding = ($sizeof(self.bits) * 8 - self.len); - uint bits = self.bits << padding; - uint n = (self.len + 7) / 8; - char[4] buffer; - bitorder::write(bits, &buffer, UIntBE); - io::write_all(self.writer, buffer[:n])!; - self.len = 0; -} - -<* - @require nbits <= 32 -*> -fn void! BitWriter.write_bits(&self, uint bits, uint nbits) -{ - if (nbits == 0) return; - while (self.len + nbits > WRITER_BITS) - { - uint to_push = WRITER_BITS - self.len; - uint bits_to_push = (bits >> (nbits - to_push)) & ((1 << to_push) - 1); - - self.bits <<= to_push; - self.bits |= bits_to_push; - self.len += to_push; - nbits -= to_push; - - self.flush()!; - } - - if (nbits == 0) return; - - self.bits <<= nbits; - self.bits |= bits & ((1 << nbits) - 1); - self.len += nbits; -} \ No newline at end of file diff --git a/lib7/std/io/file.c3 b/lib7/std/io/file.c3 deleted file mode 100644 index 37361b3ea..000000000 --- a/lib7/std/io/file.c3 +++ /dev/null @@ -1,230 +0,0 @@ -module std::io; -import libc; - -struct File (InStream, OutStream) -{ - CFile file; -} - -module std::io::file; -import libc, std::io::path, std::io::os; - -fn File! open(String filename, String mode) -{ - return from_handle(os::native_fopen(filename, mode)); -} - -fn File! open_path(Path path, String mode) -{ - return from_handle(os::native_fopen(path.str_view(), mode)); -} - -fn bool exists(String file) => @pool() -{ - return os::native_file_or_dir_exists(file); -} - -fn File from_handle(CFile file) -{ - return { .file = file }; -} - -fn bool is_file(String path) -{ - return os::native_is_file(path); -} - -fn bool is_dir(String path) -{ - return os::native_is_dir(path); -} - -fn usz! get_size(String path) -{ - return os::native_file_size(path); -} - -fn void! delete(String filename) -{ - return os::native_remove(filename) @inline; -} - - -<* - @require self.file != null -*> -fn void! File.reopen(&self, String filename, String mode) -{ - self.file = os::native_freopen(self.file, filename, mode)!; -} - -<* - @require self.file != null -*> -fn usz! File.seek(&self, isz offset, Seek seek_mode = Seek.SET) @dynamic -{ - os::native_fseek(self.file, offset, seek_mode)!; - return os::native_ftell(self.file); -} - - -/* -Implement later -<* - @require self.file == null -*> -fn void! File.memopen(File* file, char[] data, String mode) -{ - @pool() - { - file.file = libc::memopen(data.ptr, data.len, mode.zstr_tcopy(), file.file); - // TODO errors - }; -} -*/ - - -<* - @require self.file != null -*> -fn void! File.write_byte(&self, char c) @dynamic -{ - return os::native_fputc(c, self.file); -} - -<* - @param [&inout] self -*> -fn void! File.close(&self) @inline @dynamic -{ - if (self.file && libc::fclose(self.file)) - { - switch (libc::errno()) - { - case errno::ECONNRESET: - case errno::EBADF: return IoError.FILE_NOT_VALID?; - case errno::EINTR: return IoError.INTERRUPTED?; - case errno::EDQUOT: - case errno::EFAULT: - case errno::EAGAIN: - case errno::EFBIG: - case errno::ENETDOWN: - case errno::ENETUNREACH: - case errno::ENOSPC: - case errno::EIO: return IoError.INCOMPLETE_WRITE?; - default: return IoError.UNKNOWN_ERROR?; - } - } - self.file = null; -} - -<* - @require self.file != null -*> -fn bool File.eof(&self) @inline -{ - return libc::feof(self.file) != 0; -} - -<* - @param [in] buffer -*> -fn usz! File.read(&self, char[] buffer) @dynamic -{ - return os::native_fread(self.file, buffer); -} - -<* - @param [out] buffer - @require self.file != null `File must be initialized` -*> -fn usz! File.write(&self, char[] buffer) @dynamic -{ - return os::native_fwrite(self.file, buffer); -} - -fn Fd File.fd(self) @if(env::LIBC) -{ - return libc::fileno(self.file); -} - -fn bool File.isatty(self) @if(env::LIBC) -{ - return libc::isatty(self.fd()) > 0; -} - -fn char! File.read_byte(&self) @dynamic -{ - int c = libc::fgetc(self.file); - if (c == -1) return IoError.EOF?; - return (char)c; -} - -<* - Load up to buffer.len characters. Returns IoError.OVERFLOW if the file is longer - than the buffer. - - @param filename "The path to the file to read" - @param [in] buffer "The buffer to read to" -*> -fn char[]! load_buffer(String filename, char[] buffer) -{ - File file = open(filename, "rb")!; - defer (void)file.close(); - usz len = file.seek(0, END)!; - if (len > buffer.len) return IoError.OVERFLOW?; - file.seek(0, SET)!; - usz read = 0; - while (read < len) - { - read += file.read(buffer[read:len - read])!; - } - return buffer[:len]; - -} - -fn char[]! load(Allocator allocator, String filename) => load_new(filename, allocator); - -fn char[]! load_new(String filename, Allocator allocator = allocator::heap()) -{ - File file = open(filename, "rb")!; - defer (void)file.close(); - usz len = file.seek(0, END)!; - file.seek(0, SET)!; - char* data = allocator::malloc_try(allocator, len)!; - defer catch allocator::free(allocator, data); - usz read = 0; - while (read < len) - { - read += file.read(data[read:len - read])!; - } - return data[:len]; -} - -fn char[]! load_path_new(Path path, Allocator allocator = allocator::heap()) => load_new(path.str_view(), allocator); - -fn char[]! load_temp(String filename) -{ - return load_new(filename, allocator::temp()); -} - -fn char[]! load_path_temp(Path path) => load_temp(path.str_view()); - -fn void! save(String filename, char[] data) -{ - File file = open(filename, "wb")!; - defer (void)file.close(); - while (data.len) - { - usz written = file.write(data)!; - data = data[written..]; - } -} - -<* - @require self.file != null `File must be initialized` -*> -fn void! File.flush(&self) @dynamic -{ - libc::fflush(self.file); -} diff --git a/lib7/std/io/formatter.c3 b/lib7/std/io/formatter.c3 deleted file mode 100644 index 9ce3f29ae..000000000 --- a/lib7/std/io/formatter.c3 +++ /dev/null @@ -1,566 +0,0 @@ -module std::io; -import std::collections::map; -import libc; - -const int PRINTF_NTOA_BUFFER_SIZE = 256; - -interface Printable -{ - fn String to_constant_string() @optional; - fn usz! to_format(Formatter* formatter) @optional; -} - -fault PrintFault -{ - BUFFER_EXCEEDED, - INTERNAL_BUFFER_EXCEEDED, - INVALID_FORMAT, - NOT_ENOUGH_ARGUMENTS, - INVALID_ARGUMENT, -} - -def OutputFn = fn void!(void* buffer, char c); -def FloatType = double; - - -macro bool is_struct_with_default_print($Type) -{ - return $Type.kindof == STRUCT - &&& !$defined($Type.to_format) - &&& !$defined($Type.to_constant_string); -} - -<* - Introspect a struct and print it to a formatter - - @require @typekind(value) == STRUCT `This macro is only valid on macros` -*> -macro usz! struct_to_format(value, Formatter* f, bool $force_dump) -{ - var $Type = $typeof(value); - usz total = f.print("{ ")!; - $foreach ($i, $member : $Type.membersof) - $if $i > 0: - total += f.print(", ")!; - $endif - $if $member.nameof != "": - total += f.printf("%s: ", $member.nameof)!; - $endif - $if ($force_dump &&& $member.typeid.kindof == STRUCT) ||| - is_struct_with_default_print($typefrom($member.typeid)): - total += struct_to_format($member.get(value), f, $force_dump)!; - $else - total += f.printf("%s", $member.get(value))!; - $endif - $endforeach - return total + f.print(" }"); -} - -fn usz! ReflectedParam.to_format(&self, Formatter* f) @dynamic -{ - return f.printf("[Parameter '%s']", self.name); -} - -fn usz! Formatter.printf(&self, String format, args...) -{ - return self.vprintf(format, args) @inline; -} - -struct Formatter -{ - void *data; - OutputFn out_fn; - struct - { - PrintFlags flags; - uint width; - uint prec; - usz idx; - anyfault first_fault; - } -} - -bitstruct PrintFlags : uint -{ - bool zeropad; - bool left; - bool plus; - bool space; - bool hash; - bool uppercase; - bool precision; -} - -fn void Formatter.init(&self, OutputFn out_fn, void* data = null) -{ - *self = { .data = data, .out_fn = out_fn}; -} - -fn usz! Formatter.out(&self, char c) @private -{ - if (catch err = self.out_fn(self.data, c)) - { - if (self.first_fault) return self.first_fault?; - self.first_fault = err; - return err?; - } - return 1; -} - -fn usz! Formatter.print_with_function(&self, Printable arg) -{ - if (&arg.to_format) - { - PrintFlags old = self.flags; - uint old_width = self.width; - uint old_prec = self.prec; - defer - { - self.flags = old; - self.width = old_width; - self.prec = old_prec; - } - if (!arg) return self.out_substr("(null)"); - return arg.to_format(self); - } - if (&arg.to_constant_string) - { - PrintFlags old = self.flags; - uint old_width = self.width; - uint old_prec = self.prec; - defer - { - self.flags = old; - self.width = old_width; - self.prec = old_prec; - } - if (!arg) return self.out_substr("(null)"); - return self.out_substr(arg.to_constant_string()); - } - return SearchResult.MISSING?; -} - -fn usz! Formatter.out_unknown(&self, String category, any arg) @private -{ - return self.out_substr("[") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr("]"); -} -fn usz! Formatter.out_str(&self, any arg) @private -{ - switch (arg.type.kindof) - { - case TYPEID: - return self.out_substr("typeid"); - case VOID: - return self.out_substr("void"); - case ANYFAULT: - case FAULT: - return self.out_substr((*(anyfault*)arg.ptr).nameof); - case INTERFACE: - case ANY: - return self.out_str(*(any*)arg); - case OPTIONAL: - unreachable(); - case SIGNED_INT: - case UNSIGNED_INT: - PrintFlags flags = self.flags; - uint width = self.width; - defer - { - self.flags = flags; - self.width = width; - } - self.flags = {}; - self.width = 0; - return self.ntoa_any(arg, 10) ?? self.out_substr(""); - case FLOAT: - PrintFlags flags = self.flags; - uint width = self.width; - defer - { - self.flags = flags; - self.width = width; - } - self.flags = {}; - self.width = 0; - return self.ftoa(float_from_any(arg)) ?? self.out_substr("ERR"); - case BOOL: - return self.out_substr(*(bool*)arg.ptr ? "true" : "false"); - default: - } - usz! n = self.print_with_function((Printable)arg); - if (try n) return n; - if (@catch(n) != SearchResult.MISSING) n!; - switch (arg.type.kindof) - { - case ENUM: - usz i = types::any_to_enum_ordinal(arg, usz)!!; - assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i); - return self.out_substr(arg.type.names[i]); - case STRUCT: - return self.out_unknown("struct", arg); - case UNION: - return self.out_unknown("union", arg); - case BITSTRUCT: - return self.out_unknown("bitstruct", arg); - case FUNC: - return self.out_unknown("function", arg); - case DISTINCT: - if (arg.type == String.typeid) - { - return self.out_substr(*(String*)arg); - } - if (arg.type == ZString.typeid) - { - return self.out_substr(*(ZString*)arg ? ((ZString*)arg).str_view() : "(null)"); - } - if (arg.type == DString.typeid) - { - return self.out_substr(*(DString*)arg ? ((DString*)arg).str_view() : "(null)"); - } - return self.out_str(arg.as_inner()); - case POINTER: - typeid inner = arg.type.inner; - void** pointer = arg.ptr; - if (arg.type.inner != void.typeid) - { - any deref = any_make(*pointer, inner); - n = self.print_with_function((Printable)deref); - if (try n) return n; - if (@catch(n) != SearchResult.MISSING) n!; - } - PrintFlags flags = self.flags; - uint width = self.width; - defer - { - self.flags = flags; - self.width = width; - } - self.width = 0; - return self.out_substr("0x")! + self.ntoa_any(arg, 16); - case ARRAY: - // this is SomeType[?] so grab the "SomeType" - PrintFlags flags = self.flags; - uint width = self.width; - defer - { - self.flags = flags; - self.width = width; - } - self.flags = {}; - self.width = 0; - typeid inner = arg.type.inner; - usz size = inner.sizeof; - usz alen = arg.type.len; - // Pretend this is a String - void* ptr = (void*)arg.ptr; - usz len = self.out('[')!; - for (usz i = 0; i < alen; i++) - { - if (i != 0) len += self.out_substr(", ")!; - len += self.out_str(any_make(ptr, inner))!; - ptr += size; - } - len += self.out(']')!; - return len; - case VECTOR: - PrintFlags flags = self.flags; - uint width = self.width; - defer - { - self.flags = flags; - self.width = width; - } - self.flags = {}; - self.width = 0; - // this is SomeType[?] so grab the "SomeType" - typeid inner = arg.type.inner; - usz size = inner.sizeof; - usz vlen = arg.type.len; - // Pretend this is a String - void* ptr = (void*)arg.ptr; - usz len = self.out_substr("[<")!; - for (usz i = 0; i < vlen; i++) - { - if (i != 0) len += self.out_substr(", ")!; - len += self.out_str(any_make(ptr, inner))!; - ptr += size; - } - len += self.out_substr(">]")!; - return len; - case SLICE: - // this is SomeType[] so grab the "SomeType" - typeid inner = arg.type.inner; - if (inner == void.typeid) inner = char.typeid; - PrintFlags flags = self.flags; - uint width = self.width; - defer - { - self.flags = flags; - self.width = width; - } - self.flags = {}; - self.width = 0; - usz size = inner.sizeof; - // Pretend this is a String - String* temp = (void*)arg.ptr; - void* ptr = (void*)temp.ptr; - usz slen = temp.len; - usz len = self.out('[')!; - for (usz i = 0; i < slen; i++) - { - if (i != 0) len += self.out_substr(", ")!; - len += self.out_str(any_make(ptr, inner))!; - ptr += size; - } - len += self.out(']')!; - return len; - case ANY: - case INTERFACE: - unreachable("Already handled"); - default: - } - return self.out_substr("Invalid type"); -} - - - - -fn void! out_null_fn(void* data @unused, char c @unused) @private -{ -} - -macro usz! @report_fault(Formatter* f, $fault) -{ - (void)f.out_substr($fault); - return PrintFault.INVALID_FORMAT?; -} - -macro usz! @wrap_bad(Formatter* f, #action) -{ - usz! len = #action; - if (catch err = len) - { - case PrintFault.BUFFER_EXCEEDED: - case PrintFault.INTERNAL_BUFFER_EXCEEDED: - return f.first_err(err)?; - default: - err = f.first_err(PrintFault.INVALID_ARGUMENT); - f.out_substr("")!; - return err?; - } - return len; -} - -fn usz! Formatter.vprintf(&self, String format, any[] anys) -{ - self.first_fault = {}; - if (!self.out_fn) - { - // use null output function - self.out_fn = &out_null_fn; - } - usz total_len; - usz format_len = format.len; - usz variant_index = 0; - for (usz i = 0; i < format_len; i++) - { - // format specifier? %[flags][width][.precision][length] - char c = format[i]; - if (c != '%') - { - // no - total_len += self.out(c)!; - continue; - } - i++; - if (i >= format_len) return @report_fault(self, "%ERR"); - c = format[i]; - if (c == '%') - { - total_len += self.out(c)!; - continue; - } - // evaluate flags - self.flags = {}; - while FLAG_EVAL: (true) - { - switch (c) - { - case '0': self.flags.zeropad = true; - case '-': self.flags.left = true; - case '+': self.flags.plus = true; - case ' ': self.flags.space = true; - case '#': self.flags.hash = true; - default: break FLAG_EVAL; - } - if (++i >= format_len) return @report_fault(self, "%ERR"); - c = format[i]; - } - // evaluate width field - int! w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i); - if (catch w) return @report_fault(self, "%ERR"); - c = format[i]; - if (w < 0) - { - self.flags.left = true; - w = -w; - } - self.width = w; - // evaluate precision field - self.prec = 0; - if (c == '.') - { - self.flags.precision = true; - if (++i >= format_len) return @report_fault(self, ""); - int! prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i); - if (catch prec) return @report_fault(self, ""); - self.prec = prec < 0 ? 0 : prec; - c = format[i]; - } - - // evaluate specifier - uint base = 0; - if (variant_index >= anys.len) - { - self.first_err(PrintFault.NOT_ENOUGH_ARGUMENTS); - total_len += self.out_substr("")!; - continue; - } - any current = anys[variant_index++]; - switch (c) - { - case 'd': - base = 10; - self.flags.hash = false; - case 'X' : - self.flags.uppercase = true; - nextcase; - case 'x' : - base = 16; - case 'O': - self.flags.uppercase = true; - nextcase; - case 'o' : - base = 8; - case 'B': - self.flags.uppercase = true; - nextcase; - case 'b' : - base = 2; - case 'A': - self.flags.uppercase = true; - nextcase; - case 'a': - total_len += @wrap_bad(self, self.atoa(float_from_any(current)))!; - continue; - case 'F' : - self.flags.uppercase = true; - nextcase; - case 'f': - total_len += @wrap_bad(self, self.ftoa(float_from_any(current)))!; - continue; - case 'E': - self.flags.uppercase = true; - nextcase; - case 'e': - total_len += @wrap_bad(self, self.etoa(float_from_any(current)))!; - continue; - case 'G': - self.flags.uppercase = true; - nextcase; - case 'g': - total_len += @wrap_bad(self, self.gtoa(float_from_any(current)))!; - continue; - case 'c': - total_len += self.out_char(current)!; - continue; - case 'H': - self.flags.uppercase = true; - nextcase; - case 'h': - char[] out @noinit; - switch (current) - { - case char[]: - out = *current; - case ichar[]: - out = *(char[]*)current; - default: - if (current.type.kindof == ARRAY && (current.type.inner == char.typeid || current.type.inner == ichar.typeid)) - { - out = ((char*)current.ptr)[:current.type.sizeof]; - break; - } - total_len += self.out_substr("")!; - continue; - } - if (self.flags.left) - { - usz len = print_hex_chars(self, out, self.flags.uppercase)!; - total_len += len; - total_len += self.pad(' ', self.width, len)!; - continue; - } - if (self.width) - { - total_len += self.pad(' ', self.width, out.len * 2)!; - } - total_len += print_hex_chars(self, out, self.flags.uppercase)!; - continue; - case 's': - if (self.flags.left) - { - usz len = self.out_str(current)!; - total_len += len; - total_len += self.pad(' ', self.width, len)!; - continue; - } - if (self.width) - { - OutputFn out_fn = self.out_fn; - self.out_fn = (OutputFn)&out_null_fn; - usz len = self.out_str(current)!; - self.out_fn = out_fn; - total_len += self.pad(' ', self.width, len)!; - } - total_len += self.out_str(current)!; - continue; - case 'p': - self.flags.zeropad = true; - self.flags.hash = true; - base = 16; - default: - self.first_err(PrintFault.INVALID_FORMAT); - total_len += self.out_substr("")!; - continue; - } - if (base != 10) - { - self.flags.plus = false; - self.flags.space = false; - } - // ignore '0' flag when precision is given - if (self.flags.precision) self.flags.zeropad = false; - - bool is_neg; - total_len += @wrap_bad(self, self.ntoa(int_from_any(current, &is_neg), is_neg, base))!; - } - // termination - // out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); - - // return written chars without terminating \0 - if (self.first_fault) return self.first_fault?; - return total_len; -} - - -fn usz! Formatter.print(&self, String str) -{ - if (!self.out_fn) - { - // use null output function - self.out_fn = &out_null_fn; - } - foreach (c : str) self.out(c)!; - return self.idx; -} diff --git a/lib7/std/io/formatter_private.c3 b/lib7/std/io/formatter_private.c3 deleted file mode 100644 index 503519371..000000000 --- a/lib7/std/io/formatter_private.c3 +++ /dev/null @@ -1,689 +0,0 @@ -module std::io; -import std::math; - -const char[16] XDIGITS_H = "0123456789ABCDEF"; -const char[16] XDIGITS_L = "0123456789abcdef"; - -fault FormattingFault -{ - BAD_FORMAT -} - -fn usz! print_hex_chars(Formatter* f, char[] out, bool uppercase) @inline -{ - char past_10 = (uppercase ? 'A' : 'a') - 10; - usz len = 0; - foreach (c : out) - { - char digit = c >> 4; - f.out(digit + (digit < 10 ? '0' : past_10))!; - len++; - digit = c & 0xf; - f.out(digit + (digit < 10 ? '0' : past_10))!; - len++; - } - return len; -} - -macro Formatter.first_err(&self, anyfault f) -{ - if (self.first_fault) return self.first_fault; - self.first_fault = f; - return f; -} - -fn usz! Formatter.adjust(&self, usz len) @local -{ - if (!self.flags.left) return 0; - return self.pad(' ', self.width, len); -} - -fn uint128! int_from_any(any arg, bool *is_neg) @private -{ - switch (arg.type.kindof) - { - case TypeKind.POINTER: - *is_neg = false; - return (uint128)(uptr)*(void**)arg.ptr; - case TypeKind.DISTINCT: - case TypeKind.ENUM: - return int_from_any(arg.as_inner(), is_neg); - default: - break; - } - *is_neg = false; - switch (arg) - { - case bool: - return (uint128)*arg; - case ichar: - int val = *arg; - return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val; - case short: - int val = *arg; - return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val; - case int: - int val = *arg; - return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val; - case long: - long val = *arg; - return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val; - case int128: - int128 val = *arg; - return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val; - case char: - return *arg; - case ushort: - return *arg; - case uint: - return *arg; - case ulong: - return *arg; - case uint128: - return *arg; - case float: - float f = *arg; - return (uint128)((*is_neg = f < 0) ? -f : f); - case double: - double d = *arg; - return (uint128)((*is_neg = d < 0) ? -d : d); - default: - return FormattingFault.BAD_FORMAT?; - } -} - -fn FloatType! float_from_any(any arg) @private -{ - $if env::F128_SUPPORT: - if (arg.type == float128.typeid) return (FloatType)*((float128*)arg.ptr); - $endif - if (arg.type.kindof == TypeKind.DISTINCT) - { - return float_from_any(arg.as_inner()); - } - switch (arg) - { - case bool: - return (FloatType)*arg; - case ichar: - return *arg; - case short: - return *arg; - case int: - return *arg; - case long: - return *arg; - case int128: - return *arg; - case char: - return *arg; - case ushort: - return *arg; - case uint: - return *arg; - case ulong: - return *arg; - case uint128: - return *arg; - case float: - return (FloatType)*arg; - case double: - return (FloatType)*arg; - default: - return FormattingFault.BAD_FORMAT?; - } -} - - -<* - Read a simple integer value, typically for formatting. - - @param [inout] len_ptr "the length remaining." - @param [in] buf "the buf to read from." - @param maxlen "the maximum len that can be read." - @return "The result of the atoi." -*> -fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline @private -{ - uint i = 0; - usz len = *len_ptr; - while (len < maxlen) - { - char c = buf[len]; - if (!c.is_digit()) break; - i = i * 10 + c - '0'; - len++; - } - *len_ptr = len; - return i; -} - -fn usz! Formatter.out_substr(&self, String str) @private -{ - usz l = conv::utf8_codepoints(str); - uint prec = self.prec; - if (self.flags.precision && l < prec) l = prec; - usz index = 0; - usz chars = str.len; - char* ptr = str.ptr; - while (index < chars) - { - char c = ptr[index]; - // Break if we have precision set and we ran out... - if (c & 0xC0 != 0x80 && self.flags.precision && !prec--) break; - self.out(c)!; - index++; - } - return index; -} - -fn usz! Formatter.pad(&self, char c, isz width, isz len) @inline -{ - isz delta = width - len; - for (isz i = 0; i < delta; i++) self.out(c)!; - return max(0, delta); -} - -fn char* fmt_u(uint128 x, char* s) -{ - for (; x > ulong.max; x /= 10) *--s = '0' + (char)(x % 10); - for (ulong y = (ulong)x; y; y /= 10) *--s = '0' + (char)(y % 10); - return s; -} - -fn usz! Formatter.out_chars(&self, char[] s) -{ - foreach (c : s) self.out(c)!; - return s.len; -} - -enum FloatFormatting -{ - FLOAT, - EXPONENTIAL, - ADAPTIVE, - HEX -} - -fn usz! Formatter.etoa(&self, double y) => self.floatformat(EXPONENTIAL, y); -fn usz! Formatter.ftoa(&self, double y) => self.floatformat(FLOAT, y); -fn usz! Formatter.gtoa(&self, double y) => self.floatformat(ADAPTIVE, y); -fn usz! Formatter.atoa(&self, double y) => self.floatformat(HEX, y); - -fn usz! Formatter.floatformat(&self, FloatFormatting formatting, double y) @private -{ - // This code is heavily based on musl's printf code - const BUF_SIZE = (math::DOUBLE_MANT_DIG + 28) / 29 + 1 - + (math::DOUBLE_MAX_EXP + math::DOUBLE_MANT_DIG + 28 + 8) / 9; - uint[BUF_SIZE] big; - bool is_neg = false; - if (math::signbit(y)) - { - is_neg = true; - y = -y; - } - isz pl = is_neg || self.flags.plus ? 1 : 0; - // Print inf/nan - if (!math::is_finite(y)) - { - usz len; - // Add padding - if (!self.flags.left) len += self.pad(' ', self.width, 3 + pl)!; - String s = self.flags.uppercase ? "INF" : "inf"; - if (math::is_nan(y)) s = self.flags.uppercase ? "NAN" : "nan"; - if (pl) len += self.out(is_neg ? '-' : '+')!; - len += self.out_chars(s)!; - if (self.flags.left) len += self.pad(' ', self.width, 3 + pl)!; - return len; - } - // Rescale - int e2; - - y = math::frexp(y, &e2) * 2; - if (y) e2--; - char[12] ebuf0; - char* ebuf = 12 + (char*)&ebuf0; - char[9 + math::DOUBLE_MANT_DIG / 4] buf_array; - char* buf = &buf_array; - isz p = self.flags.precision ? self.prec : -1; - if (formatting == HEX) - { - double round = 8.0; - // 0x / 0X - pl += 2; - if (p > 0 && p < math::DOUBLE_MANT_DIG / 4 - 1) - { - int re = math::DOUBLE_MANT_DIG / 4 - 1 - (int)p; - round *= 1 << (math::DOUBLE_MANT_DIG % 4); - while (re--) round *= 16; - if (is_neg) - { - y = -y; - y -= round; - y += round; - y = -y; - } - else - { - y += round; - y -= round; - } - } - // Reverse print - char* estr = fmt_u(e2 < 0 ? (int128)-e2 : (int128)e2, ebuf); - if (estr == ebuf) *--estr = '0'; - *--estr = (e2 < 0 ? '-' : '+'); - *--estr = self.flags.uppercase ? 'P' : 'p'; - char* s = buf; - char* xdigits = self.flags.uppercase ? &XDIGITS_H : &XDIGITS_L; - do - { - int x = (int)y; - *s++ = xdigits[x]; - y = 16 * (y - x); - if (s - buf == 1 && (y || p > 0 || self.flags.hash)) *s++ = '.'; - } while (y); - isz outlen = s - buf; - isz explen = ebuf - estr; - if (p > int.max - 2 - explen - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; - usz len; - usz l = p && outlen - 2 < p - ? p + 2 + explen - : outlen + explen; - if (!self.flags.left && !self.flags.zeropad) len += self.pad(' ', self.width, pl + l)!; - if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!; - len += self.out_chars(self.flags.uppercase ? "0X" : "0x")!; - if (self.flags.zeropad) len += self.pad('0', self.width, pl + l)!; - len += self.out_chars(buf[:outlen])!; - len += self.pad('0', l - outlen - explen, 0)!; - len += self.out_chars(estr[:explen])!; - if (self.flags.left) len += self.pad(' ', self.width, pl + l)!; - return len; - } - if (p < 0) p = 6; - if (y) - { - y *= 0x1p28; - e2 -= 28; - } - - uint* a, z, r; - if (e2 < 0) - { - a = r = z = &big; - } - else - { - a = r = z = (uint*)&big + big.len - math::DOUBLE_MANT_DIG - 1; - } - - do - { - uint v = z++[0] = (uint)y; - y = 1000000000 * (y - v); - } while (y); - - while (e2 > 0) - { - uint carry = 0; - int sh = math::min(29, e2); - for (uint* d = z - 1; d >= a; d--) - { - ulong x = (ulong)*d << sh + carry; - *d = (uint)(x % 1000000000); - carry = (uint)(x / 1000000000); - } - if (carry) *--a = carry; - while (z > a && !z[-1]) z--; - e2 -= sh; - } - - while (e2 < 0) - { - uint carry = 0; - uint* b; - int sh = math::min(9, -e2); - int need = (int)(1 + (p + math::DOUBLE_MANT_DIG / 3u + 8) / 9); - for (uint* d = a; d < z; d++) - { - // CHECK THIS - uint rm = *d & ((1 << sh) - 1); - *d = (*d >> sh) + carry; - carry = (1000000000 >> sh) * rm; - } - if (!a[0]) a++; - if (carry) z++[0] = carry; - // Avoid (slow!) computation past requested precision - b = formatting == FLOAT ? r : a; - if (z - b > need) z = b + need; - e2 += sh; - } - - int e; - if (a < z) - { - for (int i = 10, e = (int)(9 * (r - a)); *a >= i; i *= 10, e++); - } - - // Perform rounding: j is precision after the radix (possibly neg) - int j = (int)(p - (isz)(formatting == FLOAT ? 0 : e - (int)(formatting == ADAPTIVE && p))); - if (j < 9 * (z - r - 1)) - { - uint x; - // We avoid C's broken division of negative numbers - uint* d = r + 1 + ((j + 9 * math::DOUBLE_MAX_EXP) / 9 - math::DOUBLE_MAX_EXP); - j += 9 * math::DOUBLE_MAX_EXP; - j %= 9; - int i; - for (i = 10, j++; j < 9; i *= 10, j++); - x = *d % i; - // Are there any significant digits past j? - if (x || (d + 1) != z) - { - double round = 2 / math::DOUBLE_EPSILON; - double small; - if (((*d / i) & 1) || (i == 1000000000 && d > a && (d[-1] & 1))) - { - round += 2; - } - switch - { - case x < i / 2: - small = 0x0.8p0; - case x == i / 2 && d + 1 == z: - small = 0x1.0p0; - default: - small = 0x1.8p0; - } - if (pl && is_neg) - { - round *= -1; - small *= -1; - } - *d -= x; - // Decide whether to round by probing round+small - if (round + small != round) - { - *d = *d + i; - while (*d > 999999999) - { - *d-- = 0; - if (d < a) *--a = 0; - (*d)++; - } - for (i = 10, e = (int)(9 * (r - a)); *a >= i; i *= 10, e++); - } - } - if (z > d + 1) z = d + 1; - } - for (; z>a && !z[-1]; z--); - - if (formatting == ADAPTIVE) - { - if (!p) p++; - if (p > e && e >= -4) - { - formatting = FLOAT; - p -= (isz)e + 1; - } - else - { - formatting = EXPONENTIAL; - p--; - } - if (!self.flags.hash) - { - // Count trailing zeros in last place - if (z > a && z[-1]) - { - for (int i = 10, j = 0; z[-1] % i == 0; i *= 10, j++); - } - else - { - j = 9; - } - if (formatting == FLOAT) - { - p = math::min(p, math::max((isz)0, 9 * (z - r - 1) - j)); - } - else - { - p = math::min(p, math::max((isz)0, 9 * (z - r - 1) + e - j)); - - } - } - } - if (p > int.max - 1 - (isz)(p || self.flags.hash)) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; - int l = (int)(1 + p + (isz)(p || self.flags.hash)); - char* estr @noinit; - if (formatting == FLOAT) - { - if (e > int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; - if (e > 0) l += e; - } - else - { - estr = fmt_u((uint128)(e < 0 ? -e : e), ebuf); - while (ebuf - estr < 2) (--estr)[0] = '0'; - *--estr = (e < 0 ? '-' : '+'); - *--estr = self.flags.uppercase ? 'E' : 'e'; - if (ebuf - estr > (isz)int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; - l += (int)(ebuf - estr); - } - if (l > int.max - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; - usz len; - if (!self.flags.left && !self.flags.zeropad) len += self.pad(' ', self.width, pl + l)!; - if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!; - if (self.flags.zeropad) len += self.pad('0', self.width, pl + l)!; - if (formatting == FLOAT) - { - if (a > r) a = r; - uint* d = a; - for (; d <= r; d++) - { - char* s = fmt_u(*d, buf + 9); - switch - { - case d != a: - while (s > buf) (--s)[0] = '0'; - case s == buf + 9: - *--s = '0'; - } - len += self.out_chars(s[:buf + 9 - s])!; - } - if (p || self.flags.hash) len += self.out('.')!; - for (; d < z && p > 0; d++, p -= 9) - { - char* s = fmt_u(*d, buf + 9); - while (s > buf) *--s = '0'; - len += self.out_chars(s[:math::min((isz)9, p)])!; - } - len += self.pad('0', p + 9, 9)!; - } - else - { - if (z <= a) z = a + 1; - for (uint* d = a; d < z && p >= 0; d++) - { - char* s = fmt_u(*d, buf + 9); - if (s == buf + 9) (--s)[0] = '0'; - if (d != a) - { - while (s > buf) (--s)[0] = '0'; - } - else - { - len += self.out(s++[0])!; - if (p > 0 || self.flags.hash) len += self.out('.')!; - } - len += self.out_chars(s[:math::min(buf + 9 - s, p)])!; - p -= buf + 9 - s; - } - len += self.pad('0', p + 18, 18)!; - len += self.out_chars(estr[:ebuf - estr])!; - } - - if (self.flags.left) len += self.pad(' ', self.width, pl + l)!; - - return len; -} - -fn usz! Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private -{ - char[PRINTF_NTOA_BUFFER_SIZE] buf @noinit; - usz len; - - // no hash for 0 values - if (!value) self.flags.hash = false; - - // write if precision != 0 or value is != 0 - if (!self.flags.precision || value) - { - char past_10 = (self.flags.uppercase ? 'A' : 'a') - 10; - do - { - if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; - char digit = (char)(value % base); - buf[len++] = digit + (digit < 10 ? '0' : past_10); - value /= base; - } - while (value); - } - return self.ntoa_format((String)buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base); -} - -fn usz! Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint base) @private -{ - // pad leading zeros - if (!self.flags.left) - { - if (self.width && self.flags.zeropad && (negative || self.flags.plus || self.flags.space)) self.width--; - while (len < self.prec) - { - if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; - buf[len++] = '0'; - } - while (self.flags.zeropad && len < self.width) - { - if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; - buf[len++] = '0'; - } - } - - // handle hash - if (self.flags.hash && base != 10) - { - if (!self.flags.precision && len && len == self.prec && len == self.width) - { - len--; - if (len) len--; - } - if (base != 10) - { - if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; - switch (base) - { - case 16: - buf[len++] = self.flags.uppercase ? 'X' : 'x'; - case 8: - buf[len++] = self.flags.uppercase ? 'O' : 'o'; - case 2: - buf[len++] = self.flags.uppercase ? 'B' : 'b'; - default: - unreachable(); - } - buf[len++] = '0'; - } - } - - switch (true) - { - case negative: - if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; - buf[len++] = '-'; - case self.flags.plus: - if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; - buf[len++] = '+'; - case self.flags.space: - if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?; - buf[len++] = ' '; - } - if (len) self.out_reverse(buf[:len])!; - return len; -} - - -fn usz! Formatter.ntoa_any(&self, any arg, uint base) @private -{ - bool is_neg; - return self.ntoa(int_from_any(arg, &is_neg)!!, is_neg, base) @inline; -} - -fn usz! Formatter.out_char(&self, any arg) @private -{ - if (!arg.type.kindof.is_int()) - { - return self.out_substr(""); - } - usz len = 1; - uint l = 1; - // pre padding - len += self.adjust(l)!; - // char output - Char32 c = types::any_to_int(arg, uint) ?? 0xFFFD; - switch (true) - { - case c < 0x7f: - self.out((char)c)!; - case c < 0x7ff: - self.out((char)(0xC0 | c >> 6))!; - self.out((char)(0x80 | (c & 0x3F)))!; - case c < 0xffff: - self.out((char)(0xE0 | c >> 12))!; - self.out((char)(0x80 | (c >> 6 & 0x3F)))!; - self.out((char)(0x80 | (c & 0x3F)))!; - default: - self.out((char)(0xF0 | c >> 18))!; - self.out((char)(0x80 | (c >> 12 & 0x3F)))!; - self.out((char)(0x80 | (c >> 6 & 0x3F)))!; - self.out((char)(0x80 | (c & 0x3F)))!; - } - len += self.adjust(l)!; - return len; -} - - -fn usz! Formatter.out_reverse(&self, char[] buf) @private -{ - usz n; - usz buffer_start_idx = self.idx; - usz len = buf.len; - // pad spaces up to given width - if (!self.flags.zeropad && !self.flags.left) - { - n += self.pad(' ', self.width, len)!; - } - // reverse string - while (len) n += self.out(buf[--len])!; - - // append pad spaces up to given width - n += self.adjust(n)!; - return n; -} - - -fn int! printf_parse_format_field( - any* args_ptr, usz args_len, usz* args_index_ptr, - char* format_ptr, usz format_len, usz* index_ptr) @inline @private -{ - char c = format_ptr[*index_ptr]; - if (c.is_digit()) return simple_atoi(format_ptr, format_len, index_ptr); - if (c != '*') return 0; - usz len = ++(*index_ptr); - if (len >= format_len) return FormattingFault.BAD_FORMAT?; - if (*args_index_ptr >= args_len) return FormattingFault.BAD_FORMAT?; - any val = args_ptr[(*args_index_ptr)++]; - if (!val.type.kindof.is_int()) return FormattingFault.BAD_FORMAT?; - uint! intval = types::any_to_int(val, int); - return intval ?? FormattingFault.BAD_FORMAT?; -} diff --git a/lib7/std/io/io.c3 b/lib7/std/io/io.c3 deleted file mode 100644 index 40cde0846..000000000 --- a/lib7/std/io/io.c3 +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright (c) 2021-2022 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::io; -import libc; - -enum Seek -{ - SET, - CURSOR, - END -} - -fault IoError -{ - ALREADY_EXISTS, - BUSY, - CANNOT_READ_DIR, - DIR_NOT_EMPTY, - EOF, - FILE_CANNOT_DELETE, - FILE_IS_DIR, - FILE_IS_PIPE, - FILE_NOT_DIR, - FILE_NOT_FOUND, - FILE_NOT_VALID, - GENERAL_ERROR, - ILLEGAL_ARGUMENT, - INCOMPLETE_WRITE, - INTERRUPTED, - INVALID_POSITION, - INVALID_PUSHBACK, - NAME_TOO_LONG, - NOT_SEEKABLE, - NO_PERMISSION, - OUT_OF_SPACE, - OVERFLOW, - READ_ONLY, - SYMLINK_FAILED, - TOO_MANY_DESCRIPTORS, - UNEXPECTED_EOF, - UNKNOWN_ERROR, - UNSUPPORTED_OPERATION, - WOULD_BLOCK, -} - - -<* - Read from a stream (default is stdin) to the next "\n" - or to the end of the stream, whatever comes first. - "\r" will be filtered from the String. - - @param stream `The stream to read from.` - @require @is_instream(stream) `The stream must implement InStream.` - @param [inout] allocator `the allocator to use.` - @return `The string containing the data read.` -*> -macro String! readline(Allocator allocator, stream = io::stdin()) -{ - bool $is_stream = @typeis(stream, InStream); - $if $is_stream: - $typeof(&stream.read_byte) func = &stream.read_byte; - char val = func((void*)stream)!; - $else - char val = stream.read_byte()!; - $endif - - if (val == '\n') return ""; - @pool(allocator) - { - DString str = dstring::temp_with_capacity(256); - if (val != '\r') str.append(val); - while (1) - { - $if $is_stream: - char! c = func((void*)stream); - $else - char! c = stream.read_byte(); - $endif - if (catch err = c) - { - if (err == IoError.EOF) break; - return err?; - } - if (c == '\r') continue; - if (c == '\n') break; - str.append_char(c); - } - return str.copy_str(allocator); - }; -} - -<* - Reads a string, see `readline`, except the it is allocated - on the temporary allocator and does not need to be freed. - - @param stream `The stream to read from.` - @require @is_instream(stream) `The stream must implement InStream.` - @return `The temporary string containing the data read.` -*> -macro String! treadline(stream = io::stdin()) -{ - return readline(tmem(), stream) @inline; -} - -<* - Print a value to a stream. - - @param out `the stream to print to` - @param x `the value to print` - @require @is_outstream(out) `The output must implement OutStream.` - @return `the number of bytes printed.` -*> -macro usz! fprint(out, x) -{ - var $Type = $typeof(x); - $switch ($Type) - $case String: return out.write(x); - $case ZString: return out.write(x.str_view()); - $case DString: return out.write(x.str_view()); - $default: - $if $assignable(x, String): - return out.write((String)x); - $else - $if is_struct_with_default_print($Type): - Formatter formatter; - formatter.init(&out_putstream_fn, &&(OutStream)out); - return struct_to_format(x, &formatter, false); - $else - return fprintf(out, "%s", x); - $endif - $endif - $endswitch -} - -<* - Prints using a 'printf'-style formatting string. - See `printf` for details on formatting. - - @param [inout] out `The OutStream to print to` - @param [in] format `The printf-style format string` - @return `the number of characters printed` -*> -fn usz! fprintf(OutStream out, String format, args...) -{ - Formatter formatter; - formatter.init(&out_putstream_fn, &out); - return formatter.vprintf(format, args); -} - -<* - Prints using a 'printf'-style formatting string, - appending '\n' at the end. See `printf`. - - @param [inout] out `The OutStream to print to` - @param [in] format `The printf-style format string` - @return `the number of characters printed` -*> -fn usz! fprintfn(OutStream out, String format, args...) @maydiscard -{ - Formatter formatter; - formatter.init(&out_putstream_fn, &out); - usz len = formatter.vprintf(format, args)!; - out.write_byte('\n')!; - if (&out.flush) out.flush()!; - return len + 1; -} - -<* - @require @is_outstream(out) "The output must implement OutStream" -*> -macro usz! fprintn(out, x = "") -{ - usz len = fprint(out, x)!; - out.write_byte('\n')!; - $switch - $case @typeid(out) == OutStream.typeid: - if (&out.flush) out.flush()!; - $case $defined(out.flush): - out.flush()!; - $endswitch - return len + 1; -} - -<* - Print any value to stdout. -*> -macro void print(x) -{ - (void)fprint(io::stdout(), x); -} - -<* - Print any value to stdout, appending an '\n’ after. - - @param x "The value to print" -*> -macro void printn(x = "") -{ - (void)fprintn(io::stdout(), x); -} - -<* - Print any value to stderr. -*> -macro void eprint(x) -{ - (void)fprint(io::stderr(), x); -} - -<* - Print any value to stderr, appending an '\n’ after. - - @param x "The value to print" -*> -macro void eprintn(x) -{ - (void)fprintn(io::stderr(), x); -} - - -fn void! out_putstream_fn(void* data, char c) @private -{ - OutStream* stream = data; - return (*stream).write_byte(c); -} - -fn void! out_putchar_fn(void* data @unused, char c) @private -{ - $if env::TESTING: - // HACK: this is used for the purpose of unit test output hijacking - File* stdout = io::stdout(); - assert(stdout.file); - libc::fputc(c, stdout.file); - $else - libc::putchar(c); - $endif -} - -<* - Prints using a 'printf'-style formatting string. - To print integer numbers, use "%d" or "%x"/"%X, - the latter gives the hexadecimal representation. - - All types can be printed using "%s" which gives - the default representation of the value. - - To create a custom output for a type, implement - the Printable interface. - - @param [in] format `The printf-style format string` - @return `the number of characters printed` -*> -fn usz! printf(String format, args...) @maydiscard -{ - Formatter formatter; - formatter.init(&out_putchar_fn); - return formatter.vprintf(format, args); -} - -<* - Prints using a 'printf'-style formatting string, - appending '\n' at the end. See `printf`. - - @param [in] format `The printf-style format string` - @return `the number of characters printed` -*> -fn usz! printfn(String format, args...) @maydiscard -{ - Formatter formatter; - formatter.init(&out_putchar_fn); - usz! len = formatter.vprintf(format, args); - out_putchar_fn(null, '\n')!; - io::stdout().flush()!; - return len + 1; -} - -<* - Prints using a 'printf'-style formatting string - to stderr. - - @param [in] format `The printf-style format string` - @return `the number of characters printed` -*> -fn usz! eprintf(String format, args...) @maydiscard -{ - Formatter formatter; - OutStream stream = stderr(); - formatter.init(&out_putstream_fn, &stream); - return formatter.vprintf(format, args); -} - - -<* - Prints using a 'printf'-style formatting string, - to stderr appending '\n' at the end. See `printf`. - - @param [in] format `The printf-style format string` - @return `the number of characters printed` -*> -fn usz! eprintfn(String format, args...) @maydiscard -{ - Formatter formatter; - OutStream stream = stderr(); - formatter.init(&out_putstream_fn, &stream); - usz! len = formatter.vprintf(format, args); - stderr().write_byte('\n')!; - stderr().flush()!; - return len + 1; -} - -<* - Prints using a 'printf'-style formatting string, - to a string buffer. See `printf`. - - @param [inout] buffer `The buffer to print to` - @param [in] format `The printf-style format string` - @return `a slice formed from the "buffer" with the resulting length.` -*> -fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard -{ - Formatter formatter; - BufferData data = { .buffer = buffer }; - formatter.init(&out_buffer_fn, &data); - usz size = formatter.vprintf(format, args)!; - return buffer[:data.written]; -} - -// Used to print to a buffer. -fn void! out_buffer_fn(void *data, char c) @private -{ - BufferData *buffer_data = data; - if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED?; - buffer_data.buffer[buffer_data.written++] = c; -} - -// Used for buffer printing -struct BufferData @private -{ - char[] buffer; - usz written; -} - -// Only available with LIBC -module std::io @if (env::LIBC); -import libc; - -<* - Libc `putchar`, prints a single character to stdout. -*> -fn void putchar(char c) @inline -{ - libc::putchar(c); -} - -<* - Get standard out. - - @return `stdout as a File` -*> -fn File* stdout() -{ - static File file; - if (!file.file) file = file::from_handle(libc::stdout()); - return &file; -} - -<* - Get standard err. - - @return `stderr as a File` -*> -fn File* stderr() -{ - static File file; - if (!file.file) file = file::from_handle(libc::stderr()); - return &file; -} - -<* - Get standard in. - - @return `stdin as a File` -*> -fn File* stdin() -{ - static File file; - if (!file.file) file = file::from_handle(libc::stdin()); - return &file; -} - -module std::io @if(!env::LIBC); - -File stdin_file; -File stdout_file; -File stderr_file; - -fn void putchar(char c) @inline -{ - (void)stdout_file.write_byte(c); -} - -fn File* stdout() -{ - return &stdout_file; -} - -fn File* stderr() -{ - return &stderr_file; -} - -fn File* stdin() -{ - return &stdin_file; -} - diff --git a/lib7/std/io/os/chdir.c3 b/lib7/std/io/os/chdir.c3 deleted file mode 100644 index c2a57ac52..000000000 --- a/lib7/std/io/os/chdir.c3 +++ /dev/null @@ -1,30 +0,0 @@ -module std::io::os; -import std::io::path, libc, std::os; - -macro void! native_chdir(Path path) -{ - $switch - $case env::POSIX: - if (posix::chdir(path.as_zstr())) - { - switch (libc::errno()) - { - case errno::EACCES: return IoError.NO_PERMISSION?; - case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?; - case errno::ENOTDIR: return IoError.FILE_NOT_DIR?; - case errno::ENOENT: return IoError.FILE_NOT_FOUND?; - case errno::ELOOP: return IoError.SYMLINK_FAILED?; - default: return IoError.GENERAL_ERROR?; - } - } - $case env::WIN32: - @pool() - { - // TODO improve with better error handling. - if (win32::setCurrentDirectoryW(path.str_view().to_utf16_tcopy()!!)) return; - }; - return IoError.GENERAL_ERROR?; - $default: - return IoError.UNSUPPORTED_OPERATION?; - $endswitch -} diff --git a/lib7/std/io/os/file_libc.c3 b/lib7/std/io/os/file_libc.c3 deleted file mode 100644 index 8aaa8d94f..000000000 --- a/lib7/std/io/os/file_libc.c3 +++ /dev/null @@ -1,125 +0,0 @@ -module std::io::os @if(env::LIBC); -import libc; - -<* - @require mode.len > 0 - @require filename.len > 0 -*> -fn void*! native_fopen(String filename, String mode) @inline => @pool() -{ - $if env::WIN32: - void* file = libc::_wfopen(filename.to_wstring_tcopy(), mode.to_wstring_tcopy())!; - $else - void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy()); - $endif - return file ?: file_open_errno()?; -} - -fn void! native_remove(String filename) => @pool() -{ - $if env::WIN32: - CInt result = libc::_wremove(filename.to_wstring_tcopy())!; - $else - CInt result = libc::remove(filename.zstr_tcopy()); - $endif - if (result) - { - switch (libc::errno()) - { - case errno::ENOENT: - return IoError.FILE_NOT_FOUND?; - case errno::EACCES: - default: - return IoError.FILE_CANNOT_DELETE?; - } - } -} - -<* - @require mode.len > 0 - @require filename.len > 0 -*> -fn void*! native_freopen(void* file, String filename, String mode) @inline => @pool() -{ - $if env::WIN32: - file = libc::_wfreopen(filename.to_wstring_tcopy(), mode.to_wstring_tcopy(), file)!; - $else - file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file); - $endif - return file ?: file_open_errno()?; -} - -fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline -{ - if (libc::fseek(file, (SeekIndex)offset, seek_mode.ordinal)) return file_seek_errno()?; -} - - -fn usz! native_ftell(CFile file) @inline -{ - long index = libc::ftell(file); - return index >= 0 ? (usz)index : file_seek_errno()?; -} - -fn usz! native_fwrite(CFile file, char[] buffer) @inline -{ - return libc::fwrite(buffer.ptr, 1, buffer.len, file); -} - -fn void! native_fputc(CInt c, CFile stream) @inline -{ - if (libc::fputc(c, stream) == libc::EOF) return IoError.EOF?; -} - -fn usz! native_fread(CFile file, char[] buffer) @inline -{ - return libc::fread(buffer.ptr, 1, buffer.len, file); -} - -macro anyfault file_open_errno() @local -{ - switch (libc::errno()) - { - case errno::EACCES: return IoError.NO_PERMISSION; - case errno::EDQUOT: return IoError.OUT_OF_SPACE; - case errno::EBADF: return IoError.FILE_NOT_VALID; - case errno::EEXIST: return IoError.ALREADY_EXISTS; - case errno::EINTR: return IoError.INTERRUPTED; - case errno::EFAULT: return IoError.GENERAL_ERROR; - case errno::EISDIR: return IoError.FILE_IS_DIR; - case errno::ELOOP: return IoError.SYMLINK_FAILED; - case errno::EMFILE: return IoError.TOO_MANY_DESCRIPTORS; - case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG; - case errno::ENFILE: return IoError.OUT_OF_SPACE; - case errno::ENOTDIR: return IoError.FILE_NOT_DIR; - case errno::ENOENT: return IoError.FILE_NOT_FOUND; - case errno::ENOSPC: return IoError.OUT_OF_SPACE; - case errno::ENXIO: return IoError.FILE_NOT_FOUND; - case errno::EOVERFLOW: return IoError.OVERFLOW; - case errno::EROFS: return IoError.READ_ONLY; - case errno::EOPNOTSUPP: return IoError.UNSUPPORTED_OPERATION; - case errno::EIO: return IoError.INCOMPLETE_WRITE; - case errno::EWOULDBLOCK: return IoError.WOULD_BLOCK; - default: return IoError.UNKNOWN_ERROR; - } -} - -macro anyfault file_seek_errno() @local -{ - switch (libc::errno()) - { - case errno::ESPIPE: return IoError.FILE_IS_PIPE; - case errno::EPIPE: return IoError.FILE_IS_PIPE; - case errno::EOVERFLOW: return IoError.OVERFLOW; - case errno::ENXIO: return IoError.FILE_NOT_FOUND; - case errno::ENOSPC: return IoError.OUT_OF_SPACE; - case errno::EIO: return IoError.INCOMPLETE_WRITE; - case errno::EINVAL: return IoError.INVALID_POSITION; - case errno::EINTR: return IoError.INTERRUPTED; - case errno::EFBIG: return IoError.OUT_OF_SPACE; - case errno::EBADF: return IoError.FILE_NOT_VALID; - case errno::EAGAIN: return IoError.WOULD_BLOCK; - default: return IoError.UNKNOWN_ERROR; - } -} - diff --git a/lib7/std/io/os/file_nolibc.c3 b/lib7/std/io/os/file_nolibc.c3 deleted file mode 100644 index 7c23afa03..000000000 --- a/lib7/std/io/os/file_nolibc.c3 +++ /dev/null @@ -1,83 +0,0 @@ -module std::io::os @if(env::NO_LIBC); -import libc; - -def FopenFn = fn void*!(String, String); -def FreopenFn = fn void*!(void*, String, String); -def FcloseFn = fn void!(void*); -def FseekFn = fn void!(void*, isz, Seek); -def FtellFn = fn usz!(void*); -def FwriteFn = fn usz!(void*, char[] buffer); -def FreadFn = fn usz!(void*, char[] buffer); -def RemoveFn = fn void!(String); -def FputcFn = fn void!(int, void*); - -FopenFn native_fopen_fn @weak @if(!$defined(native_fopen_fn)); -FcloseFn native_fclose_fn @weak @if(!$defined(native_fclose_fn)); -FreopenFn native_freopen_fn @weak @if(!$defined(native_freopen_fn)); -FseekFn native_fseek_fn @weak @if(!$defined(native_fseek_fn)); -FtellFn native_ftell_fn @weak @if(!$defined(native_ftell_fn)); -FwriteFn native_fwrite_fn @weak @if(!$defined(native_fwrite_fn)); -FreadFn native_fread_fn @weak @if(!$defined(native_fread_fn)); -RemoveFn native_remove_fn @weak @if(!$defined(native_remove_fn)); -FputcFn native_fputc_fn @weak @if(!$defined(native_fputc_fn)); - -<* - @require mode.len > 0 - @require filename.len > 0 -*> -fn void*! native_fopen(String filename, String mode) @inline -{ - if (native_fopen_fn) return native_fopen_fn(filename, mode); - return IoError.UNSUPPORTED_OPERATION?; -} - -<* - Delete a file. - - @require filename.len > 0 -*> -fn void! native_remove(String filename) @inline -{ - if (native_remove_fn) return native_remove_fn(filename); - return IoError.UNSUPPORTED_OPERATION?; -} - -<* - @require mode.len > 0 - @require filename.len > 0 -*> -fn void*! native_freopen(void* file, String filename, String mode) @inline -{ - if (native_freopen_fn) return native_freopen_fn(file, filename, mode); - return IoError.UNSUPPORTED_OPERATION?; -} - -fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline -{ - if (native_fseek_fn) return native_fseek_fn(file, offset, seek_mode); - return IoError.UNSUPPORTED_OPERATION?; -} - -fn usz! native_ftell(CFile file) @inline -{ - if (native_ftell_fn) return native_ftell_fn(file); - return IoError.UNSUPPORTED_OPERATION?; -} - -fn usz! native_fwrite(CFile file, char[] buffer) @inline -{ - if (native_fwrite_fn) return native_fwrite_fn(file, buffer); - return IoError.UNSUPPORTED_OPERATION?; -} - -fn usz! native_fread(CFile file, char[] buffer) @inline -{ - if (native_fread_fn) return native_fread_fn(file, buffer); - return IoError.UNSUPPORTED_OPERATION?; -} - -fn void! native_fputc(CInt c, CFile stream) @inline -{ - if (native_fputc_fn) return native_fputc_fn(c, stream); - return IoError.UNSUPPORTED_OPERATION?; -} diff --git a/lib7/std/io/os/fileinfo.c3 b/lib7/std/io/os/fileinfo.c3 deleted file mode 100644 index ab6d3a8e8..000000000 --- a/lib7/std/io/os/fileinfo.c3 +++ /dev/null @@ -1,116 +0,0 @@ -module std::io::os; -import libc, std::os, std::io; - -fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || env::BSD_FAMILY) => @pool() -{ - $if env::DARWIN || env::LINUX || env::BSD_FAMILY: - int res = libc::stat(path.zstr_tcopy(), stat); - $else - unreachable("Stat unimplemented"); - int res = 0; - $endif - if (res != 0) - { - switch (libc::errno()) - { - case errno::EBADF: - return IoError.FILE_NOT_VALID?; - case errno::EFAULT: - unreachable("Invalid stat"); - case errno::EIO: - return IoError.GENERAL_ERROR?; - case errno::EACCES: - return IoError.NO_PERMISSION?; - case errno::ELOOP: - return IoError.NO_PERMISSION?; - case errno::ENAMETOOLONG: - return IoError.NAME_TOO_LONG?; - case errno::ENOENT: - return IoError.FILE_NOT_FOUND?; - case errno::ENOTDIR: - return IoError.FILE_NOT_DIR?; - case errno::EOVERFLOW: - return IoError.GENERAL_ERROR?; - default: - return IoError.UNKNOWN_ERROR?; - } - } -} - -fn usz! native_file_size(String path) @if(env::WIN32) => @pool() -{ - Win32_FILE_ATTRIBUTE_DATA data; - win32::getFileAttributesExW(path.to_wstring_tcopy()!, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data); - Win32_LARGE_INTEGER size; - size.lowPart = data.nFileSizeLow; - size.highPart = data.nFileSizeHigh; - return (usz)size.quadPart; -} - -fn usz! native_file_size(String path) @if(!env::WIN32 && !env::DARWIN) -{ - File f = file::open(path, "r")!; - defer (void)f.close(); - return f.seek(0, Seek.END)!; -} - -fn usz! native_file_size(String path) @if(env::DARWIN) -{ - Stat stat; - native_stat(&stat, path)!; - return stat.st_size; -} - -fn bool native_file_or_dir_exists(String path) -{ - $switch - $case env::DARWIN: - $case env::FREEBSD: - $case env::NETBSD: - $case env::OPENBSD: - $case env::LINUX: - Stat stat; - return @ok(native_stat(&stat, path)); - $case env::WIN32: - @pool() - { - return (bool)win32::pathFileExistsW(path.to_utf16_tcopy()) ?? false; - }; - $case env::POSIX: - @pool() - { - return posix::access(path.zstr_tcopy(), 0 /* F_OK */) != -1; - }; - $default: - unreachable("Not supported"); - $endswitch -} - -fn bool native_is_file(String path) -{ - $switch - $case env::DARWIN: - $case env::FREEBSD: - $case env::NETBSD: - $case env::OPENBSD: - $case env::LINUX: - Stat stat; - return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFREG); - $default: - File! f = file::open(path, "r"); - defer (void)f.close(); - return @ok(f); - $endswitch -} - -fn bool native_is_dir(String path) -{ - $if env::DARWIN || env::LINUX || env::BSD_FAMILY: - Stat stat; - return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFDIR); - $else - return native_file_or_dir_exists(path) && !native_is_file(path); - $endif -} - - diff --git a/lib7/std/io/os/getcwd.c3 b/lib7/std/io/os/getcwd.c3 deleted file mode 100644 index 69f649612..000000000 --- a/lib7/std/io/os/getcwd.c3 +++ /dev/null @@ -1,41 +0,0 @@ -module std::io::os; -import libc, std::os; - -macro String! getcwd(Allocator allocator = allocator::heap()) -{ - $switch - $case env::WIN32: - const DEFAULT_BUFFER = 256; - Char16[DEFAULT_BUFFER] buffer; - WString res = win32::_wgetcwd(&buffer, DEFAULT_BUFFER); - bool free = false; - defer if (free) libc::free(res); - if (!res) - { - if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?; - res = win32::_wgetcwd(null, 0); - free = true; - } - Char16[] str16 = res[:win32::wcslen(res)]; - return string::new_from_utf16(allocator, str16); - - $case env::POSIX: - const usz DEFAULT_BUFFER = 256; - char[DEFAULT_BUFFER] buffer; - ZString res = posix::getcwd(&buffer, DEFAULT_BUFFER); - bool free = false; - if (!res) - { - // Improve error - if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?; - res = posix::getcwd(null, 0); - free = true; - } - defer if (free) libc::free((void*)res); - return res.copy(allocator); - - $default: - return IoError.UNSUPPORTED_OPERATION?; - $endswitch -} - diff --git a/lib7/std/io/os/ls.c3 b/lib7/std/io/os/ls.c3 deleted file mode 100644 index 4bc446801..000000000 --- a/lib7/std/io/os/ls.c3 +++ /dev/null @@ -1,51 +0,0 @@ -module std::io::os @if(env::POSIX); -import std::io, std::os; - -fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator) -{ - PathList list; - 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)?; - Posix_dirent* entry; - while ((entry = posix::readdir(directory))) - { - String name = ((ZString)&entry.name).str_view(); - if (!name || name == "." || name == "..") continue; - if (entry.d_type == posix::DT_LNK && no_symlinks) continue; - if (entry.d_type == posix::DT_DIR && no_dirs) continue; - Path path = path::new(allocator, name)!!; - list.push(path); - } - return list; -} - -module std::io::os @if(env::WIN32); -import std::time, std::os, std::io; - -fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator) -{ - PathList list; - list.init(allocator); - - @pool(allocator) - { - WString result = dir.str_view().tconcat(`\*`).to_wstring_tcopy()!!; - Win32_WIN32_FIND_DATAW find_data; - Win32_HANDLE find = win32::findFirstFileW(result, &find_data); - if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?; - defer win32::findClose(find); - do - { - if (no_dirs && (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY)) continue; - @pool(allocator) - { - String filename = string::temp_from_wstring((WString)&find_data.cFileName)!; - if (filename == ".." || filename == ".") continue; - list.push(path::new(allocator, filename)!); - }; - } while (win32::findNextFileW(find, &find_data)); - return list; - }; -} diff --git a/lib7/std/io/os/mkdir.c3 b/lib7/std/io/os/mkdir.c3 deleted file mode 100644 index 385c00338..000000000 --- a/lib7/std/io/os/mkdir.c3 +++ /dev/null @@ -1,50 +0,0 @@ -module std::io::os; -import libc; -import std::io::path; -import std::os::win32; -import std::os::posix; - - -macro bool! native_mkdir(Path path, MkdirPermissions permissions) -{ - $switch - $case env::POSIX: - if (!posix::mkdir(path.as_zstr(), permissions == NORMAL ? 0o777 : 0o700)) return true; - switch (libc::errno()) - { - case errno::EACCES: - case errno::EPERM: - case errno::EROFS: - case errno::EFAULT: return IoError.NO_PERMISSION?; - case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?; - case errno::EDQUOT: - case errno::ENOSPC: return IoError.OUT_OF_SPACE?; - case errno::EISDIR: - case errno::EEXIST: return false; - case errno::ELOOP: return IoError.SYMLINK_FAILED?; - case errno::ENOTDIR: return IoError.FILE_NOT_FOUND?; - default: return IoError.GENERAL_ERROR?; - } - $case env::WIN32: - @pool() - { - // TODO security attributes - if (win32::createDirectoryW(path.str_view().to_utf16_tcopy()!!, null)) return true; - switch (win32::getLastError()) - { - case win32::ERROR_ACCESS_DENIED: - return IoError.NO_PERMISSION?; - case win32::ERROR_DISK_FULL: - return IoError.OUT_OF_SPACE?; - case win32::ERROR_ALREADY_EXISTS: - return false; - case win32::ERROR_PATH_NOT_FOUND: - return IoError.FILE_NOT_FOUND?; - default: - return IoError.GENERAL_ERROR?; - } - }; - $default: - return IoError.UNSUPPORTED_OPERATION?; - $endswitch -} \ No newline at end of file diff --git a/lib7/std/io/os/rmdir.c3 b/lib7/std/io/os/rmdir.c3 deleted file mode 100644 index 970b1d319..000000000 --- a/lib7/std/io/os/rmdir.c3 +++ /dev/null @@ -1,48 +0,0 @@ -module std::io::os; -import libc; -import std::io::path; -import std::os::win32; -import std::os::posix; - -macro bool! native_rmdir(Path path) -{ - $switch - $case env::POSIX: - if (!posix::rmdir(path.as_zstr())) return true; - switch (libc::errno()) - { - case errno::EBUSY: return IoError.BUSY?; - case errno::EACCES: - case errno::EPERM: - case errno::EROFS: - case errno::EFAULT: return IoError.NO_PERMISSION?; - case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?; - case errno::ENOTDIR: - case errno::ENOENT: return false; - case errno::ENOTEMPTY: return IoError.DIR_NOT_EMPTY?; - case errno::ELOOP: return IoError.SYMLINK_FAILED?; - default: return IoError.GENERAL_ERROR?; - } - $case env::WIN32: - @pool() - { - if (win32::removeDirectoryW(path.str_view().to_utf16_tcopy()!!)) return true; - switch (win32::getLastError()) - { - case win32::ERROR_ACCESS_DENIED: - return IoError.NO_PERMISSION?; - case win32::ERROR_CURRENT_DIRECTORY: - return IoError.BUSY?; - case win32::ERROR_DIR_NOT_EMPTY: - return IoError.DIR_NOT_EMPTY?; - case win32::ERROR_DIRECTORY: - case win32::ERROR_PATH_NOT_FOUND: - return false; - default: - return IoError.GENERAL_ERROR?; - } - }; - $default: - return IoError.UNSUPPORTED_OPERATION?; - $endswitch -} diff --git a/lib7/std/io/os/rmtree.c3 b/lib7/std/io/os/rmtree.c3 deleted file mode 100644 index 866e89e34..000000000 --- a/lib7/std/io/os/rmtree.c3 +++ /dev/null @@ -1,64 +0,0 @@ -module std::io::os @if(env::POSIX); -import std::io, std::os, libc; - -<* - @require dir.str_view().len > 0 -*> -fn void! native_rmtree(Path dir) -{ - DIRPtr directory = posix::opendir(dir.as_zstr()); - defer if (directory) posix::closedir(directory); - if (!directory) return path::is_dir(dir) ? IoError.CANNOT_READ_DIR? : IoError.FILE_NOT_DIR?; - Posix_dirent* entry; - while ((entry = posix::readdir(directory))) - { - @pool() - { - String name = ((ZString)&entry.name).str_view(); - if (!name || name == "." || name == "..") continue; - Path new_path = dir.tappend(name)!; - if (entry.d_type == posix::DT_DIR) - { - native_rmtree(new_path)!; - continue; - } - if (libc::remove(new_path.as_zstr())) - { - // TODO improve - return IoError.GENERAL_ERROR?; - } - }; - } - os::native_rmdir(dir)!; -} - -module std::io::os @if(env::WIN32); -import std::io, std::time, std::os; - -fn void! native_rmtree(Path path) -{ - Win32_WIN32_FIND_DATAW find_data; - String s = path.str_view().tconcat("\\*"); - Win32_HANDLE find = win32::findFirstFileW(s.to_utf16_tcopy(), &find_data)!; - - if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?; - defer win32::findClose(find); - do - { - @pool() - { - String filename = string::new_from_wstring(tmem(), (WString)&find_data.cFileName)!; - if (filename == "." || filename == "..") continue; - Path file_path = path.tappend(filename)!; - if (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY) - { - native_rmtree(file_path)!; - } - else - { - win32::deleteFileW(file_path.str_view().to_wstring_tcopy()!!); - } - }; - } while (win32::findNextFileW(find, &find_data) != 0); - os::native_rmdir(path)!; -} diff --git a/lib7/std/io/os/temp_directory.c3 b/lib7/std/io/os/temp_directory.c3 deleted file mode 100644 index 75c50449d..000000000 --- a/lib7/std/io/os/temp_directory.c3 +++ /dev/null @@ -1,29 +0,0 @@ -module std::io::os @if(env::LIBC); -import std::io::path, std::os; - -fn Path! native_temp_directory(Allocator allocator) @if(!env::WIN32) -{ - foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" }) - { - String tmpdir = env::tget_var(env) ?? ""; - if (tmpdir) return path::new(allocator, tmpdir); - } - return path::new(allocator, "/tmp"); -} - -fn Path! native_temp_directory(Allocator allocator) @if(env::WIN32) => @pool(allocator) -{ - Win32_DWORD len = win32::getTempPathW(0, null); - if (!len) return IoError.GENERAL_ERROR?; - Char16[] buff = mem::temp_alloc_array(Char16, len + (usz)1); - if (!win32::getTempPathW(len, buff)) return IoError.GENERAL_ERROR?; - return path::new(allocator, string::temp_from_utf16(buff[:len])); -} - -module std::io::os @if(env::NO_LIBC); -import std::io::path; - -macro Path! native_temp_directory(Allocator allocator = allocator::heap()) -{ - return IoError.UNSUPPORTED_OPERATION?; -} diff --git a/lib7/std/io/path.c3 b/lib7/std/io/path.c3 deleted file mode 100644 index 2420b5161..000000000 --- a/lib7/std/io/path.c3 +++ /dev/null @@ -1,687 +0,0 @@ -module std::io::path; -import std::collections::list, std::io::os; -import std::os::win32; - -const PathEnv DEFAULT_ENV = env::WIN32 ? PathEnv.WIN32 : PathEnv.POSIX; -const char PREFERRED_SEPARATOR_WIN32 = '\\'; -const char PREFERRED_SEPARATOR_POSIX = '/'; -const char PREFERRED_SEPARATOR = env::WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX; - -def PathList = List { Path }; - -fault PathResult -{ - INVALID_PATH, - NO_PARENT, -} - -def Path = PathImp; - -struct PathImp (Printable) -{ - String path_string; - PathEnv env; - Allocator allocator; -} - -enum PathEnv -{ - WIN32, - POSIX -} - -fn Path! cwd(Allocator allocator) -{ - @pool(allocator) - { - return new(allocator, os::getcwd(tmem())); - }; -} - -fn bool is_dir(Path path) => os::native_is_dir(path.str_view()); -fn bool is_file(Path path) => os::native_is_file(path.str_view()); -fn usz! file_size(Path path) => os::native_file_size(path.str_view()); -fn bool exists(Path path) => os::native_file_or_dir_exists(path.str_view()); -fn Path! tcwd() => cwd(tmem()) @inline; - -<* - @require @is_pathlike(path) : "Expected a Path or String to chdir" -*> -macro void! chdir(path) -{ - $if @typeis(path, String): - @pool() - { - return os::native_chdir(temp(path)); - }; - $else - return os::native_chdir(path) @inline; - $endif -} - -fn Path! temp_directory(Allocator allocator) => os::native_temp_directory(allocator); - -fn void! delete(Path path) => os::native_remove(path.str_view()) @inline; - -macro bool @is_pathlike(#path) => @typeis(#path, String) || @typeis(#path, Path); - -macro bool is_separator(char c, PathEnv path_env = DEFAULT_ENV) -{ - return c == '/' || (c == '\\' && path_env == PathEnv.WIN32); -} - -macro bool is_posix_separator(char c) => c == '/'; -macro bool is_win32_separator(char c) => c == '/' || c == '\\'; - -fn PathList! ls(Allocator allocator, Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "") -{ - $if $defined(os::native_ls): - return os::native_ls(dir, no_dirs, no_symlinks, mask, allocator); - $else - return IoError.UNSUPPORTED_OPERATION?; - $endif -} - -enum MkdirPermissions -{ - NORMAL, - USER_ONLY, - USER_AND_ADMIN -} - -<* - Create a directory on a given path, optionally recursive. - - @param path `The path to create` - @require @is_pathlike(path) : "Expected a Path or String to chdir" - @param recursive `If directories in between should be created if they're missing, defaults to false` - @param permissions `The permissions to set on the directory` -*> -macro bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL) -{ - $if @typeis(path, String): - @pool() { return _mkdir(temp(path), recursive, permissions); }; - $else - return _mkdir(path, recursive, permissions); - $endif -} - - -<* - Tries to delete directory, which must be empty. - - @param path `The path to delete` - @require @is_pathlike(path) : "Expected a Path or String to chdir" - @return `true if there was a directory to delete, false otherwise` - @return! PathResult.INVALID_PATH `if the path was invalid` -*> -macro bool! rmdir(path) -{ - $if @typeis(path, String): - @pool() { return _rmdir(temp(path)); }; - $else - return _mkdir(path); - $endif -} - -<* - Like [rmdir] but deletes a directory even if it contains items. -*> -fn void! rmtree(Path path) -{ - if (!path.path_string.len) return PathResult.INVALID_PATH?; - $if $defined(os::native_rmtree): - return os::native_rmtree(path); - $else - return IoError.UNSUPPORTED_OPERATION?; - $endif -} - -<* - Creates a new path. - - @return! PathResult.INVALID_PATH `if the path was invalid` -*> -fn Path! new(Allocator allocator, String path, PathEnv path_env = DEFAULT_ENV) -{ - return { normalize(path.copy(allocator), path_env), path_env, allocator }; -} - -<* - Creates a new path using the temp allocator. - - @return! PathResult.INVALID_PATH `if the path was invalid` -*> -fn Path! temp(String path, PathEnv path_env = DEFAULT_ENV) -{ - return new(tmem(), path, path_env); -} - -fn Path! from_win32_wstring(Allocator allocator, WString path) => @pool(allocator) -{ - return path::new(allocator, string::temp_from_wstring(path)!); -} - -fn Path! for_windows(Allocator allocator, String path) -{ - return new(allocator, path, WIN32); -} - -fn Path! for_posix(Allocator allocator, String path) -{ - return new(allocator, path, POSIX); -} - -fn bool Path.equals(self, Path p2) -{ - return self.env == p2.env && self.path_string == p2.path_string; -} - -<* - Append the string to the current path. - - @param [in] filename -*> -fn Path! Path.append(self, Allocator allocator, String filename) -{ - if (!self.path_string.len) return new(allocator, filename, self.env)!; - assert(!is_separator(self.path_string[^1], self.env)); - - @pool(allocator) - { - DString dstr = dstring::temp_with_capacity(self.path_string.len + 1 + filename.len); - dstr.append(self.path_string); - dstr.append(PREFERRED_SEPARATOR); - dstr.append(filename); - return new(allocator, dstr.str_view(), self.env); - }; -} - -fn Path! Path.tappend(self, String filename) => self.append(tmem(), filename); - -fn usz! start_of_base_name(String str, PathEnv path_env) @local -{ - if (!str.len) return 0; - usz! start_slash = str.rindex_of_char('/'); - if (path_env != PathEnv.WIN32) return start_slash + 1 ?? 0; - if (try index = str.rindex_of_char('\\')) - { - if (try start_slash && start_slash > index) return start_slash + 1; - // c:\ style path, we're done! - if (str[0] != '\\') return index + 1; - // Handle \\server\foo - // Find the \ before "foo" - usz last_index = 2 + str[2..].index_of_char('\\')!; - // If they don't match, we're done - if (last_index > index) return PathResult.INVALID_PATH?; - if (last_index != index) return index + 1; - // Otherwise just default to the volume length. - } - return volume_name_len(str, path_env)!!; -} - - -fn bool! String.is_absolute_path(self) => @pool() -{ - return temp(self).is_absolute(); -} - -fn bool! Path.is_absolute(self) -{ - String path_str = self.str_view(); - if (!path_str.len) return false; - usz path_start = volume_name_len(path_str, self.env)!; - if (path_start > 0 && path_str[0] == '\\') return true; - return path_start < path_str.len && is_separator(path_str[path_start], self.env); -} - - -fn Path! String.to_absolute_path(self, Allocator allocator) => @pool(allocator) -{ - return temp(self).absolute(allocator); -} - -<* - @require self.env == DEFAULT_ENV : "This method is only available on native paths" -*> -fn Path! Path.absolute(self, Allocator allocator) -{ - String path_str = self.str_view(); - if (!path_str.len) return PathResult.INVALID_PATH?; - if (self.is_absolute()!) return new(allocator, path_str, self.env); - if (path_str == ".") - { - @pool(allocator) - { - String cwd = os::getcwd(tmem())!; - return new(allocator, cwd, self.env); - }; - } - $if DEFAULT_ENV == WIN32: - @pool(allocator) - { - const usz BUFFER_LEN = 4096; - WString buffer = (WString)mem::temp_alloc_array(Char16, BUFFER_LEN); - buffer = win32::_wfullpath(buffer, path_str.to_wstring_tcopy()!, BUFFER_LEN); - if (!buffer) return PathResult.INVALID_PATH?; - return { string::new_from_wstring(allocator, buffer), WIN32, allocator }; - }; - $else - String cwd = os::getcwd(tmem())!; - return (Path){ cwd, self.env, tmem() }.append(allocator, path_str)!; - $endif -} - -fn String! String.file_basename(self, Allocator allocator) => @pool(allocator) -{ - return temp(self).basename().copy(allocator); -} - -fn String! String.file_tbasename(self) => self.file_basename(tmem()); - -fn String Path.basename(self) -{ - usz basename_start = start_of_base_name(self.path_string, self.env)!!; - String path_str = self.path_string; - if (basename_start == path_str.len) return ""; - return path_str[basename_start..]; -} - -fn String! String.path_tdirname(self) => self.path_dirname(tmem()); - -fn String! String.path_dirname(self, Allocator allocator) => @pool(allocator) -{ - return temp(self).dirname().copy(allocator); -} - -fn String Path.dirname(self) -{ - String path_str = self.path_string; - usz basename_start = start_of_base_name(path_str, self.env)!!; - if (basename_start == 0) return "."; - usz start = volume_name_len(path_str, self.env)!!; - if (basename_start <= start + 1) - { - if (self.env == WIN32 && basename_start > start && path_str[0..1] == `\\`) - { - return path_str[:basename_start - 1]; - } - return path_str[:basename_start]; - } - return path_str[:basename_start - 1]; -} - - -<* - Test if the path has the given extension, so given the path /foo/bar.c3 - this would be true matching the extension "c3" - - @param [in] extension `The extension name (not including the leading '.')` - @require extension.len > 0 : `The extension cannot be empty` - @return `true if the extension matches` -*> -fn bool Path.has_extension(self, String extension) -{ - String basename = self.basename(); - if (basename.len <= extension.len) return false; - if (basename[^extension.len + 1] != '.') return false; - return basename[^extension.len..] == extension; -} - -fn String! Path.extension(self) -{ - String basename = self.basename(); - usz index = basename.rindex_of(".")!; - // Plain ".foo" does not have an - if (index == 0) return SearchResult.MISSING?; - if (index == basename.len) return ""; - return basename[index + 1..]; -} - -fn String Path.volume_name(self) -{ - usz len = volume_name_len(self.str_view(), self.env)!!; - if (!len) return ""; - return self.path_string[:len]; -} - -fn Path! String.to_path(self, Allocator allocator) -{ - return new(allocator, self); -} - -fn Path! String.to_tpath(self) -{ - return new(tmem(), self); -} - -fn usz! volume_name_len(String path, PathEnv path_env) @local -{ - usz len = path.len; - if (len < 2 || path_env != PathEnv.WIN32) return 0; - switch (path[0]) - { - case '\\': - // "\\" paths.. must be longer than 2 - if (len == 2) return 0; - int count = 1; - while (count < len && path[count] == '\\') count++; - // Not 2 => folded paths - if (count != 2) return 0; - // Check that we have a name followed by '\' - isz base_found = 0; - for (usz i = 2; i < len; i++) - { - char c = path[i]; - if (is_win32_separator(c)) - { - if (base_found) return i; - base_found = i; - continue; - } - if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH?; - } - if (base_found > 0 && base_found + 1 < len) return len; - return PathResult.INVALID_PATH?; - case 'A'..'Z': - case 'a'..'z': - return path[1] == ':' ? 2 : 0; - default: - return 0; - } -} - -<* - Get the path of the parent. This does not allocate, but returns a slice - of the path itself. - - @return `The parent of the path as a non-allocated path` - @return! PathResult.NO_PARENT `if this path does not have a parent` -*> -fn Path! Path.parent(self) -{ - if (self.path_string.len == 1 && is_separator(self.path_string[0], self.env)) return PathResult.NO_PARENT?; - foreach_r(i, c : self.path_string) - { - if (is_separator(c, self.env)) - { - return { self.path_string[:i], self.env, null }; - } - } - return PathResult.NO_PARENT?; -} - -fn String! normalize(String path_str, PathEnv path_env = DEFAULT_ENV) -{ - if (!path_str.len) return path_str; - usz path_start = volume_name_len(path_str, path_env)!; - if (path_start > 0 && path_env == PathEnv.WIN32) - { - for (usz i = 0; i < path_start; i++) if (path_str[i] == '/') path_str[i] = '\\'; - } - usz path_len = path_str.len; - if (path_start == path_len) return path_str; - char path_separator = path_env == PathEnv.WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX; - usz len = path_start; - bool has_root = is_separator(path_str[path_start], path_env); - if (has_root) - { - path_str[len++] = path_separator; - path_start++; - } - // It is safe to write it as true, since we already dealt with /foo. - // This allows us to avoid checking whether it is the start of the path. - bool previous_was_separator = true; - - for (usz i = path_start; i < path_len; i++) - { - char c = path_str[i]; - // Fold foo///bar into foo/bar - if (is_separator(c, path_env)) - { - // Fold // - if (previous_was_separator) continue; - - // New /, so mark and rewrite - path_str.ptr[len++] = path_separator; - previous_was_separator = true; - continue; - } - - // The rest are names of the path elements, so check that the - // characters are valid. - if (is_reserved_path_char(c, path_env)) return PathResult.INVALID_PATH?; - - // If we have '.' after a separator - if (c == '.' && previous_was_separator) - { - // Get the number of dots until next separator, expecting 1 or 2 - bool is_last = i == path_len - 1; - int dots = 1; - if (!is_last) - { - char next = path_str[i + 1]; - switch - { - case next == '.': - dots = 2; - is_last = i == path_len - 2; - if (!is_last && !is_separator(path_str[i + 2], path_env)) - { - dots = 0; - } - case !is_separator(next, path_env): - dots = 0; - } - } - switch (dots) - { - case 1: - // /./abc -> skip to /./abc - // ^ ^ - i++; - continue; - case 2: - // This is an error: /a/../.. - if (len == path_start && has_root) return PathResult.INVALID_PATH?; - - // If this .. at the start, or after ../? If so, we just copy .. - if (len == path_start || - (len - path_start >= 3 && path_str[len - 1] == path_separator - && path_str[len - 3] == '.' && path_str[len - 3] == '.' && - (len - 3 == 0 || path_str[len - 4] == path_separator))) - { - if (i != len) - { - path_str[len] = '.'; - path_str[len + 1] = '.'; - } - len += 2; - if (len < path_len) path_str[len++] = path_separator; - i += 2; - continue; - } - // Step back, now looking at '/' abc/def/. -> abc/def/ - len--; - // Step back until finding a separator or the start. - while (len > path_start && !is_separator(path_str[len - 1], path_env)) - { - len--; - } - // Reading, we go from /../abc to /../abc - // ^ ^ - i += 2; - continue; - default: - break; - - } - } - if (i != len) path_str[len] = c; - previous_was_separator = false; - len++; - } - if (len > path_start + 1 && is_separator(path_str[len - 1], path_env)) len--; - if (path_str.len > len) path_str.ptr[len] = 0; - // Empty path after normalization -> "." - if (!len) - { - path_str[0] = '.'; - return path_str[:1]; - } - return path_str[:len]; -} - -fn ZString Path.as_zstr(self) => (ZString)self.path_string.ptr; - -fn String Path.root_directory(self) -{ - String path_str = self.str_view(); - usz len = path_str.len; - if (!len) return ""; - if (path_str == ".") return "."; - if (self.env == PathEnv.WIN32) - { - usz root_len = volume_name_len(path_str, self.env)!!; - if (root_len == len || !is_win32_separator(path_str[root_len])) return ""; - return path_str[root_len..root_len]; - } - if (!is_posix_separator(path_str[0])) return ""; - for (usz i = 1; i < len; i++) - { - if (is_posix_separator(path_str[i])) - { - return path_str[:i]; - } - } - return path_str; -} - -def PathWalker = fn bool! (Path, bool is_dir, void*); - -<* - Walk the path recursively. PathWalker is run on every file and - directory found. Return true to abort the walk. - @require self.env == DEFAULT_ENV : "This method is only available on native paths" -*> -fn bool! Path.walk(self, PathWalker w, void* data) -{ - const PATH_MAX = 512; - @stack_mem(PATH_MAX; Allocator allocator) - { - Path abs = self.absolute(allocator)!; - PathList files = ls(allocator, abs)!; - foreach (f : files) - { - if (f.str_view() == "." || f.str_view() == "..") continue; - f = abs.append(allocator, f.str_view())!; - bool is_directory = is_dir(f); - if (w(f, is_directory, data)!) return true; - if (is_directory && f.walk(w, data)!) return true; - } - }; - return false; -} - -def TraverseCallback = fn bool! (Path, bool is_dir, any data); - -<* - Walk the path recursively. TraverseCallback is run for every file and - directory found. Return true to abort the walk. - @require path.env == DEFAULT_ENV : "This method is only available on native paths" -*> -fn bool! traverse(Path path, TraverseCallback callback, any data) -{ - const PATH_MAX = 512; - @stack_mem(PATH_MAX; Allocator allocator) - { - Path abs = path.absolute(allocator)!; - PathList files = ls(allocator, abs)!; - foreach (f : files) - { - if (f.str_view() == "." || f.str_view() == "..") continue; - @stack_mem(128; Allocator smem) - { - f = abs.append(smem, f.str_view())!; - bool is_directory = is_dir(f); - if (callback(f, is_directory, data)!) return true; - if (is_directory && traverse(f, callback, data)!) return true; - }; - } - }; - return false; -} - -fn String Path.str_view(self) @inline -{ - return self.path_string; -} - - -fn bool Path.has_suffix(self, String str) -{ - return self.str_view().ends_with(str); -} - -<* - @require self.allocator != null : "This Path should never be freed" -*> -fn void Path.free(self) -{ - allocator::free(self.allocator, self.path_string.ptr); -} - -fn usz! Path.to_format(&self, Formatter* formatter) @dynamic -{ - return formatter.print(self.str_view()); -} - - -const bool[256] RESERVED_PATH_CHAR_POSIX = { - [0] = true, - ['/'] = true, -}; -const bool[256] RESERVED_PATH_CHAR_WIN32 = { - [0..31] = true, - ['>'] = true, - ['<'] = true, - [':'] = true, - ['\"'] = true, - ['/'] = true, - ['\\'] = true, - ['|'] = true, - ['?'] = true, - ['*'] = true, -}; - -macro bool is_reserved_win32_path_char(char c) -{ - return RESERVED_PATH_CHAR_WIN32[c]; -} - -macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_ENV) -{ - return path_env == PathEnv.WIN32 - ? RESERVED_PATH_CHAR_WIN32[c] - : RESERVED_PATH_CHAR_POSIX[c]; -} -fn bool! _mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL) @private -{ - if (!path.path_string.len) return PathResult.INVALID_PATH?; - if (is_dir(path)) return false; - if (exists(path)) return IoError.FILE_NOT_DIR?; - - if (recursive) - { - if (try parent = path.parent()) mkdir(parent, true, permissions)!; - } - if (!is_dir(path.parent()) ?? false) return IoError.CANNOT_READ_DIR?; - - return os::native_mkdir(path, permissions); -} - -fn bool! _rmdir(Path path) @private -{ - if (!path.path_string.len) return PathResult.INVALID_PATH?; - return os::native_rmdir(path); -} diff --git a/lib7/std/io/stream.c3 b/lib7/std/io/stream.c3 deleted file mode 100644 index 0f083229d..000000000 --- a/lib7/std/io/stream.c3 +++ /dev/null @@ -1,469 +0,0 @@ -module std::io; -import std::math; - -interface InStream -{ - fn void! close() @optional; - fn usz! seek(isz offset, Seek seek) @optional; - fn usz len() @optional; - fn usz! available() @optional; - fn usz! read(char[] buffer); - fn char! read_byte(); - fn usz! write_to(OutStream out) @optional; - fn void! pushback_byte() @optional; -} - - -interface OutStream -{ - fn void! destroy() @optional; - fn void! close() @optional; - fn void! flush() @optional; - fn usz! write(char[] bytes); - fn void! write_byte(char c); - fn usz! read_to(InStream in) @optional; -} - -fn usz! available(InStream s) -{ - if (&s.available) return s.available(); - if (&s.seek) - { - usz curr = s.seek(0, Seek.CURSOR)!; - usz len = s.seek(0, Seek.END)!; - s.seek(curr, Seek.SET)!; - return len - curr; - } - return 0; -} - -macro bool @is_instream(#expr) -{ - return $assignable(#expr, InStream); -} - -macro bool @is_outstream(#expr) -{ - return $assignable(#expr, OutStream); -} - -<* - @param [&out] ref - @require @is_instream(stream) -*> -macro usz! read_any(stream, any ref) -{ - return read_all(stream, ((char*)ref)[:ref.type.sizeof]); -} - -<* - @param [&in] ref "the object to write." - @require @is_outstream(stream) - @ensure return == ref.type.sizeof -*> -macro usz! write_any(stream, any ref) -{ - return write_all(stream, ((char*)ref)[:ref.type.sizeof]); -} - -<* - @require @is_instream(stream) -*> -macro usz! read_all(stream, char[] buffer) -{ - if (buffer.len == 0) return 0; - usz n = stream.read(buffer)!; - if (n != buffer.len) return IoError.UNEXPECTED_EOF?; - return n; -} - -<* - @require @is_instream(stream) -*> -macro char[]! read_fully(Allocator allocator, stream) -{ - usz len = available(stream)!; - char* data = allocator::malloc_try(allocator, len)!; - defer catch allocator::free(allocator, data); - usz read = 0; - while (read < len) - { - read += stream.read(data[read:len - read])!; - } - return data[:len]; -} - -<* - @require @is_outstream(stream) -*> -macro usz! write_all(stream, char[] buffer) -{ - if (buffer.len == 0) return 0; - usz n = stream.write(buffer)!; - if (n != buffer.len) return IoError.INCOMPLETE_WRITE?; - return n; -} - -macro usz! @read_using_read_byte(&s, char[] buffer) @deprecated -{ - return read_using_read_byte(*s, buffer); -} - -macro usz! read_using_read_byte(s, char[] buffer) -{ - usz len = 0; - foreach (&cptr : buffer) - { - char! c = s.read_byte(); - if (catch err = c) - { - case IoError.EOF: return len; - default: return err?; - } - *cptr = c; - len++; - } - return len; -} - -macro void! write_byte_using_write(s, char c) -{ - char[1] buff = { c }; - s.write(&buff)!; -} - -macro void! @write_byte_using_write(&s, char c) @deprecated -{ - return write_byte_using_write(*s, c); -} - -macro char! @read_byte_using_read(&s) @deprecated -{ - return read_byte_using_read(*s); -} - -macro char! read_byte_using_read(s) -{ - char[1] buffer; - usz read = s.read(&buffer)!; - if (read != 1) return IoError.EOF?; - return buffer[0]; -} - -def ReadByteFn = fn char!(); - - -macro usz! write_using_write_byte(s, char[] bytes) -{ - foreach (c : bytes) s.write_byte(self, c)!; - return bytes.len; -} - -macro usz! @write_using_write_byte(&s, char[] bytes) @deprecated -{ - return write_using_write_byte(*s, bytes); -} - -macro void! pushback_using_seek(s) -{ - s.seek(-1, CURSOR)!; -} - -macro void! @pushback_using_seek(&s) @deprecated -{ - s.seek(-1, CURSOR)!; -} - -fn usz! copy_to(InStream in, OutStream dst, char[] buffer = {}) -{ - if (buffer.len) return copy_through_buffer(in, dst, buffer); - if (&in.write_to) return in.write_to(dst); - if (&dst.read_to) return dst.read_to(in); - $switch (env::MEMORY_ENV) - $case NORMAL: - return copy_through_buffer(in, dst, &&(char[4096]){}); - $case SMALL: - return copy_through_buffer(in, dst, &&(char[1024]){}); - $case TINY: - $case NONE: - return copy_through_buffer(in, dst, &&(char[256]){}); - $endswitch -} - -macro usz! copy_through_buffer(InStream in, OutStream dst, char[] buffer) @local -{ - usz total_copied; - while (true) - { - usz! len = in.read(buffer); - if (catch err = len) - { - case IoError.EOF: return total_copied; - default: return err?; - } - if (!len) return total_copied; - usz written = dst.write(buffer[:len])!; - total_copied += len; - if (written != len) return IoError.INCOMPLETE_WRITE?; - } -} - -const char[?] MAX_VARS @private = { [2] = 3, [4] = 5, [8] = 10 }; - -<* - @require @is_instream(stream) - @require @typekind(x_ptr) == POINTER && $typeof(x_ptr).inner.kindof.is_int() -*> -macro usz! read_varint(stream, x_ptr) -{ - var $Type = $typefrom($typeof(x_ptr).inner); - const MAX = MAX_VARS[$Type.sizeof]; - $Type x; - uint shift; - usz n; - for (usz i = 0; i < MAX; i++) - { - char! c = stream.read_byte(); - if (catch err = c) - { - case IoError.EOF: - return IoError.UNEXPECTED_EOF?; - default: - return err?; - } - n++; - if (c & 0x80 == 0) - { - if (i + 1 == MAX && c > 1) break; - x |= c << shift; - $if $Type.kindof == SIGNED_INT: - x = x & 1 == 0 ? x >> 1 : ~(x >> 1); - $endif - *x_ptr = x; - return n; - } - x |= (c & 0x7F) << shift; - shift += 7; - } - return MathError.OVERFLOW?; -} -<* - @require @is_outstream(stream) - @require @typekind(x).is_int() -*> -macro usz! write_varint(stream, x) -{ - var $Type = $typeof(x); - const MAX = MAX_VARS[$Type.sizeof]; - char[MAX] buffer @noinit; - usz i; - while (x >= 0x80) - { - buffer[i] = (char)(x | 0x80); - x >>= 7; - i++; - } - buffer[i] = (char)x; - return write_all(stream, buffer[:i + 1]); -} - -<* - @require @is_instream(stream) -*> -macro ushort! read_be_ushort(stream) -{ - char hi_byte = stream.read_byte()!; - char lo_byte = stream.read_byte()!; - return (ushort)(hi_byte << 8 | lo_byte); -} - -<* - @require @is_instream(stream) -*> -macro short! read_be_short(stream) -{ - return read_be_ushort(stream); -} - -<* - @require @is_outstream(stream) -*> -macro void! write_be_short(stream, ushort s) -{ - stream.write_byte((char)(s >> 8))!; - stream.write_byte((char)s)!; -} - -<* - @require @is_instream(stream) -*> -macro uint! read_be_uint(stream) -{ - uint val = stream.read_byte()! << 24; - val += stream.read_byte()! << 16; - val += stream.read_byte()! << 8; - return val + stream.read_byte()!; -} - -<* - @require @is_instream(stream) -*> -macro int! read_be_int(stream) -{ - return read_be_uint(stream); -} - -<* - @require @is_outstream(stream) -*> -macro void! write_be_int(stream, uint s) -{ - stream.write_byte((char)(s >> 24))!; - stream.write_byte((char)(s >> 16))!; - stream.write_byte((char)(s >> 8))!; - stream.write_byte((char)s)!; -} - -<* - @require @is_instream(stream) -*> -macro ulong! read_be_ulong(stream) -{ - ulong val = (ulong)stream.read_byte()! << 56; - val += (ulong)stream.read_byte()! << 48; - val += (ulong)stream.read_byte()! << 40; - val += (ulong)stream.read_byte()! << 32; - val += (ulong)stream.read_byte()! << 24; - val += (ulong)stream.read_byte()! << 16; - val += (ulong)stream.read_byte()! << 8; - return val + stream.read_byte()!; -} - -<* - @require @is_instream(stream) -*> -macro long! read_be_long(stream) -{ - return read_be_ulong(stream); -} - -<* - @require @is_outstream(stream) -*> -macro void! write_be_long(stream, ulong s) -{ - stream.write_byte((char)(s >> 56))!; - stream.write_byte((char)(s >> 48))!; - stream.write_byte((char)(s >> 40))!; - stream.write_byte((char)(s >> 32))!; - stream.write_byte((char)(s >> 24))!; - stream.write_byte((char)(s >> 16))!; - stream.write_byte((char)(s >> 8))!; - stream.write_byte((char)s)!; -} - -<* - @require @is_instream(stream) -*> -macro uint128! read_be_uint128(stream) -{ - uint128 val = (uint128)stream.read_byte()! << 120; - val += (uint128)stream.read_byte()! << 112; - val += (uint128)stream.read_byte()! << 104; - val += (uint128)stream.read_byte()! << 96; - val += (uint128)stream.read_byte()! << 88; - val += (uint128)stream.read_byte()! << 80; - val += (uint128)stream.read_byte()! << 72; - val += (uint128)stream.read_byte()! << 64; - val += (uint128)stream.read_byte()! << 56; - val += (uint128)stream.read_byte()! << 48; - val += (uint128)stream.read_byte()! << 40; - val += (uint128)stream.read_byte()! << 32; - val += (uint128)stream.read_byte()! << 24; - val += (uint128)stream.read_byte()! << 16; - val += (uint128)stream.read_byte()! << 8; - return val + stream.read_byte()!; -} - -<* - @require @is_instream(stream) -*> -macro int128! read_be_int128(stream) -{ - return read_be_uint128(stream); -} - -<* - @require @is_outstream(stream) -*> -macro void! write_be_int128(stream, uint128 s) -{ - stream.write_byte((char)(s >> 120))!; - stream.write_byte((char)(s >> 112))!; - stream.write_byte((char)(s >> 104))!; - stream.write_byte((char)(s >> 96))!; - stream.write_byte((char)(s >> 88))!; - stream.write_byte((char)(s >> 80))!; - stream.write_byte((char)(s >> 72))!; - stream.write_byte((char)(s >> 64))!; - stream.write_byte((char)(s >> 56))!; - stream.write_byte((char)(s >> 48))!; - stream.write_byte((char)(s >> 40))!; - stream.write_byte((char)(s >> 32))!; - stream.write_byte((char)(s >> 24))!; - stream.write_byte((char)(s >> 16))!; - stream.write_byte((char)(s >> 8))!; - stream.write_byte((char)s)!; -} - -<* - @require @is_outstream(stream) - @require data.len < 256 "Data exceeded 255" -*> -macro usz! write_tiny_bytearray(stream, char[] data) -{ - stream.write_byte((char)data.len)!; - return stream.write(data) + 1; -} - -<* - @require @is_instream(stream) -*> -macro char[]! read_tiny_bytearray(stream, Allocator allocator) -{ - int len = stream.read_byte()!; - if (!len) return {}; - char[] data = allocator::alloc_array(allocator, char, len); - io::read_all(stream, data)!; - return data; -} - -<* - @require @is_outstream(stream) - @require data.len < 0x1000 "Data exceeded 65535" -*> -macro usz! write_short_bytearray(stream, char[] data) -{ - io::write_be_short(stream, (ushort)data.len)!; - return stream.write(data) + 2; -} - -<* - @require @is_instream(stream) -*> -macro char[]! read_short_bytearray(stream, Allocator allocator) -{ - int len = io::read_be_ushort(stream)!; - if (!len) return {}; - char[] data = allocator::alloc_array(allocator, char, len); - io::read_all(stream, data)!; - return data; -} - -<* - Wrap bytes for reading using io functions. -*> -fn ByteReader wrap_bytes(char[] bytes) -{ - return { bytes, 0 }; -} \ No newline at end of file diff --git a/lib7/std/io/stream/buffer.c3 b/lib7/std/io/stream/buffer.c3 deleted file mode 100644 index c158e2b5e..000000000 --- a/lib7/std/io/stream/buffer.c3 +++ /dev/null @@ -1,136 +0,0 @@ -module std::io; - -struct ReadBuffer (InStream) -{ - InStream wrapped_stream; - char[] bytes; - usz read_idx; - usz write_idx; -} - -<* - Buffer reads from a stream. - @param [inout] self - @require bytes.len > 0 - @require self.bytes.len == 0 "Init may not run on already initialized data" -*> -fn ReadBuffer* ReadBuffer.init(&self, InStream wrapped_stream, char[] bytes) -{ - *self = { .wrapped_stream = wrapped_stream, .bytes = bytes }; - return self; -} -fn String ReadBuffer.str_view(&self) @inline -{ - return (String)self.bytes[self.read_idx:self.write_idx - self.read_idx]; -} - -fn void! ReadBuffer.close(&self) @dynamic -{ - if (&self.wrapped_stream.close) self.wrapped_stream.close()!; -} - -fn usz! ReadBuffer.read(&self, char[] bytes) @dynamic -{ - if (self.read_idx == self.write_idx) - { - if (self.read_idx == 0 && bytes.len >= self.bytes.len) - { - // Read directly into the input buffer. - return self.wrapped_stream.read(bytes)!; - } - self.refill()!; - } - usz n = min(self.write_idx - self.read_idx, bytes.len); - bytes[:n] = self.bytes[self.read_idx:n]; - self.read_idx += n; - return n; -} - -fn char! ReadBuffer.read_byte(&self) @dynamic -{ - if (self.read_idx == self.write_idx) self.refill()!; - if (self.read_idx == self.write_idx) return IoError.EOF?; - char c = self.bytes[self.read_idx]; - self.read_idx++; - return c; -} - -fn void! ReadBuffer.refill(&self) @local @inline -{ - self.read_idx = 0; - self.write_idx = self.wrapped_stream.read(self.bytes)!; -} - -struct WriteBuffer (OutStream) -{ - OutStream wrapped_stream; - char[] bytes; - usz index; -} - -<* - Buffer writes to a stream. Call `flush` when done writing to the buffer. - @param [inout] self - @require bytes.len > 0 "Non-empty buffer required" - @require self.bytes.len == 0 "Init may not run on already initialized data" -*> -fn WriteBuffer* WriteBuffer.init(&self, OutStream wrapped_stream, char[] bytes) -{ - *self = { .wrapped_stream = wrapped_stream, .bytes = bytes }; - return self; -} - -fn String WriteBuffer.str_view(&self) @inline -{ - return (String)self.bytes[:self.index]; -} - -fn void! WriteBuffer.close(&self) @dynamic -{ - if (&self.wrapped_stream.close) return self.wrapped_stream.close(); -} - -fn void! WriteBuffer.flush(&self) @dynamic -{ - self.write_pending()!; - if (&self.wrapped_stream.flush) self.wrapped_stream.flush()!; -} - -fn usz! WriteBuffer.write(&self, char[] bytes) @dynamic -{ - usz n = self.bytes.len - self.index; - if (bytes.len < n) - { - // Enough room in the buffer. - self.bytes[self.index:bytes.len] = bytes[..]; - self.index += bytes.len; - return bytes.len; - } - self.write_pending()!; - if (bytes.len >= self.bytes.len) - { - // Write directly to the stream. - return self.wrapped_stream.write(bytes); - } - // Buffer the data. - self.bytes[:bytes.len] = bytes[..]; - self.index = bytes.len; - return bytes.len; -} - -fn void! WriteBuffer.write_byte(&self, char c) @dynamic -{ - usz n = self.bytes.len - self.index; - if (n == 0) - { - self.write_pending()!; - } - self.bytes[self.index] = c; - self.index += 1; -} - -fn void! WriteBuffer.write_pending(&self) @local -{ - self.index -= self.wrapped_stream.write(self.bytes[:self.index])!; - if (self.index != 0) return IoError.INCOMPLETE_WRITE?; -} diff --git a/lib7/std/io/stream/bytebuffer.c3 b/lib7/std/io/stream/bytebuffer.c3 deleted file mode 100644 index c1964fa30..000000000 --- a/lib7/std/io/stream/bytebuffer.c3 +++ /dev/null @@ -1,148 +0,0 @@ -module std::io; -import std::math; - -struct ByteBuffer (InStream, OutStream) -{ - Allocator allocator; - usz max_read; - char[] bytes; - usz read_idx; - usz write_idx; - bool has_last; -} - -<* - ByteBuffer provides a streamable read/write buffer. - max_read defines how many bytes might be kept before its internal buffer is shrinked. - @require self.bytes.len == 0 "Buffer already initialized." -*> -fn ByteBuffer* ByteBuffer.init(&self, Allocator allocator, usz max_read, usz initial_capacity = 16) -{ - *self = { .allocator = allocator, .max_read = max_read }; - initial_capacity = max(initial_capacity, 16); - self.grow(initial_capacity); - return self; -} - -fn ByteBuffer* ByteBuffer.tinit(&self, usz max_read, usz initial_capacity = 16) -{ - return self.init(tmem(), max_read, initial_capacity); -} - -<* - @require buf.len > 0 - @require self.bytes.len == 0 "Buffer already initialized." -*> -fn ByteBuffer* ByteBuffer.init_with_buffer(&self, char[] buf) -{ - *self = { .max_read = buf.len, .bytes = buf }; - return self; -} - -fn void ByteBuffer.free(&self) -{ - if (self.allocator) allocator::free(self.allocator, self.bytes); - *self = {}; -} - -fn usz! ByteBuffer.write(&self, char[] bytes) @dynamic -{ - usz cap = self.bytes.len - self.write_idx; - if (cap < bytes.len) self.grow(bytes.len); - self.bytes[self.write_idx:bytes.len] = bytes[..]; - self.write_idx += bytes.len; - return bytes.len; -} - -fn void! ByteBuffer.write_byte(&self, char c) @dynamic -{ - usz cap = self.bytes.len - self.write_idx; - if (cap == 0) self.grow(1); - self.bytes[self.write_idx] = c; - self.write_idx++; -} - -fn usz! ByteBuffer.read(&self, char[] bytes) @dynamic -{ - usz readable = self.write_idx - self.read_idx; - if (readable == 0) - { - self.has_last = false; - return IoError.EOF?; - } - usz n = min(readable, bytes.len); - bytes[:n] = self.bytes[self.read_idx:n]; - self.read_idx += n; - self.has_last = n > 0; - self.shrink(); - return n; -} - -fn char! ByteBuffer.read_byte(&self) @dynamic -{ - usz readable = self.write_idx - self.read_idx; - if (readable == 0) - { - self.has_last = false; - return IoError.EOF?; - } - char c = self.bytes[self.read_idx]; - self.read_idx++; - self.has_last = true; - self.shrink(); - return c; -} - -<* - Only the last byte of a successful read can be pushed back. -*> -fn void! ByteBuffer.pushback_byte(&self) @dynamic -{ - if (!self.has_last) return IoError.EOF?; - assert(self.read_idx > 0); - self.read_idx--; - self.has_last = false; -} - -fn usz! ByteBuffer.seek(&self, isz offset, Seek seek) @dynamic -{ - switch (seek) - { - case SET: - if (offset < 0 || offset > self.write_idx) return IoError.INVALID_POSITION?; - self.read_idx = offset; - return offset; - case CURSOR: - if ((offset < 0 && self.read_idx < -offset) || - (offset > 0 && self.read_idx + offset > self.write_idx)) return IoError.INVALID_POSITION?; - self.read_idx += offset; - case END: - if (offset < 0 || offset > self.write_idx) return IoError.INVALID_POSITION?; - self.read_idx = self.write_idx - offset; - } - return self.read_idx; -} - -fn usz! ByteBuffer.available(&self) @inline @dynamic -{ - return self.write_idx - self.read_idx; -} - -fn void ByteBuffer.grow(&self, usz n) -{ - n = math::next_power_of_2(n); - char* p = allocator::realloc(self.allocator, self.bytes, n); - self.bytes = p[:n]; -} - -macro ByteBuffer.shrink(&self) -{ - if (self.read_idx >= self.max_read) - { - // Drop the read data besides the last byte (for pushback_byte). - usz readable = self.write_idx - self.read_idx; - self.bytes[:1 + readable] = self.bytes[self.read_idx - 1:1 + readable]; - self.write_idx = 1 + readable; - self.read_idx = 1; - } -} \ No newline at end of file diff --git a/lib7/std/io/stream/bytereader.c3 b/lib7/std/io/stream/bytereader.c3 deleted file mode 100644 index ea2ae3c79..000000000 --- a/lib7/std/io/stream/bytereader.c3 +++ /dev/null @@ -1,68 +0,0 @@ -module std::io; - -struct ByteReader (InStream) -{ - char[] bytes; - usz index; -} - -fn usz ByteReader.len(&self) @dynamic -{ - return self.bytes.len; -} - -fn ByteReader* ByteReader.init(&self, char[] bytes) -{ - *self = { .bytes = bytes }; - return self; -} - -fn usz! ByteReader.read(&self, char[] bytes) @dynamic -{ - if (self.index >= self.bytes.len) return IoError.EOF?; - usz len = min(self.bytes.len - self.index, bytes.len); - if (len == 0) return 0; - mem::copy(bytes.ptr, &self.bytes[self.index], len); - self.index += len; - return len; -} - -fn char! ByteReader.read_byte(&self) @dynamic -{ - if (self.index >= self.bytes.len) return IoError.EOF?; - return self.bytes[self.index++]; -} - -fn void! ByteReader.pushback_byte(&self) @dynamic -{ - if (!self.index) return IoError.INVALID_PUSHBACK?; - self.index--; -} - -fn usz! ByteReader.seek(&self, isz offset, Seek seek) @dynamic -{ - isz new_index; - switch (seek) - { - case SET: new_index = offset; - case CURSOR: new_index = self.index + offset; - case END: new_index = self.bytes.len + offset; - } - if (new_index < 0) return IoError.INVALID_POSITION?; - self.index = new_index; - return new_index; -} - -fn usz! ByteReader.write_to(&self, OutStream writer) @dynamic -{ - if (self.index >= self.bytes.len) return 0; - usz written = writer.write(self.bytes[self.index..])!; - self.index += written; - assert(self.index <= self.bytes.len); - return written; -} - -fn usz! ByteReader.available(&self) @inline @dynamic -{ - return max(0, self.bytes.len - self.index); -} \ No newline at end of file diff --git a/lib7/std/io/stream/bytewriter.c3 b/lib7/std/io/stream/bytewriter.c3 deleted file mode 100644 index 377aaab7e..000000000 --- a/lib7/std/io/stream/bytewriter.c3 +++ /dev/null @@ -1,113 +0,0 @@ -module std::io; -import std::math; - -struct ByteWriter (OutStream) -{ - char[] bytes; - usz index; - Allocator allocator; -} - -<* - @param [&inout] self - @param [&inout] allocator - @require self.bytes.len == 0 "Init may not run on already initialized data" - @ensure (bool)allocator, self.index == 0 -*> -fn ByteWriter* ByteWriter.init(&self, Allocator allocator) -{ - *self = { .bytes = {}, .allocator = allocator }; - return self; -} - -<* - @param [&inout] self - @require self.bytes.len == 0 "Init may not run on already initialized data" - @ensure self.index == 0 -*> -fn ByteWriter* ByteWriter.tinit(&self) -{ - return self.init(tmem()) @inline; -} - -fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data) -{ - *self = { .bytes = data, .allocator = null }; - return self; -} - -fn void! ByteWriter.destroy(&self) @dynamic -{ - if (!self.allocator) return; - if (void* ptr = self.bytes.ptr) allocator::free(self.allocator, ptr); - *self = { }; -} - -fn String ByteWriter.str_view(&self) @inline -{ - return (String)self.bytes[:self.index]; -} - -fn void! ByteWriter.ensure_capacity(&self, usz len) @inline -{ - if (self.bytes.len > len) return; - if (!self.allocator) return IoError.OUT_OF_SPACE?; - if (len < 16) len = 16; - usz new_capacity = math::next_power_of_2(len); - char* new_ptr = allocator::realloc_try(self.allocator, self.bytes.ptr, new_capacity)!; - self.bytes = new_ptr[:new_capacity]; -} - -fn usz! ByteWriter.write(&self, char[] bytes) @dynamic -{ - self.ensure_capacity(self.index + bytes.len)!; - mem::copy(&self.bytes[self.index], bytes.ptr, bytes.len); - self.index += bytes.len; - return bytes.len; -} - -fn void! ByteWriter.write_byte(&self, char c) @dynamic -{ - self.ensure_capacity(self.index + 1)!; - self.bytes[self.index++] = c; -} - -<* - @param [&inout] self - @param reader -*> -fn usz! ByteWriter.read_from(&self, InStream reader) @dynamic -{ - usz start_index = self.index; - if (&reader.available) - { - while (usz available = reader.available()!) - { - self.ensure_capacity(self.index + available)!; - usz read = reader.read(self.bytes[self.index..])!; - self.index += read; - } - return self.index - start_index; - } - if (self.bytes.len == 0) - { - self.ensure_capacity(16)!; - } - while (true) - { - // See how much we can read. - usz len_to_read = self.bytes.len - self.index; - // Less than 16 bytes? Double the capacity - if (len_to_read < 16) - { - self.ensure_capacity(self.bytes.len * 2)!; - len_to_read = self.bytes.len - self.index; - } - // Read into the rest of the buffer - usz read = reader.read(self.bytes[self.index..])!; - self.index += read; - // Ok, we reached the end. - if (read < len_to_read) return self.index - start_index; - // Otherwise go another round - } -} diff --git a/lib7/std/io/stream/limitreader.c3 b/lib7/std/io/stream/limitreader.c3 deleted file mode 100644 index e512e92b7..000000000 --- a/lib7/std/io/stream/limitreader.c3 +++ /dev/null @@ -1,44 +0,0 @@ -module std::io; - -struct LimitReader (InStream) -{ - InStream wrapped_stream; - usz limit; -} - -<* - @param [&inout] wrapped_stream "The stream to read from" - @param limit "The max limit to read" -*> -fn LimitReader* LimitReader.init(&self, InStream wrapped_stream, usz limit) -{ - *self = { .wrapped_stream = wrapped_stream, .limit = limit }; - return self; -} - -fn void! LimitReader.close(&self) @dynamic -{ - if (&self.wrapped_stream.close) return self.wrapped_stream.close(); -} - - -fn usz! LimitReader.read(&self, char[] bytes) @dynamic -{ - if (self.limit == 0) return IoError.EOF?; - usz m = min(bytes.len, self.limit); - usz n = self.wrapped_stream.read(bytes[:m])!; - self.limit -= n; - return n; -} - -fn char! LimitReader.read_byte(&self) @dynamic -{ - if (self.limit == 0) return IoError.EOF?; - defer try self.limit--; - return self.wrapped_stream.read_byte(); -} - -fn usz! LimitReader.available(&self) @inline @dynamic -{ - return self.limit; -} \ No newline at end of file diff --git a/lib7/std/io/stream/multireader.c3 b/lib7/std/io/stream/multireader.c3 deleted file mode 100644 index 6cfdd1eb5..000000000 --- a/lib7/std/io/stream/multireader.c3 +++ /dev/null @@ -1,70 +0,0 @@ -module std::io; - -/* MultiReader implements the InStream interface and provides a logical - * concatenation of the provided readers. They are read sequentially. If all the - * data has been read, IoError.EOF is returned. - */ -struct MultiReader (InStream) -{ - InStream[] readers; - usz index; - Allocator allocator; -} - - -<* - @param [&inout] self - @param [&inout] allocator - @require self.readers.len == 0 "Init may not run on already initialized data" - @ensure self.index == 0 -*> -fn MultiReader* MultiReader.init(&self, Allocator allocator, InStream... readers) -{ - InStream []copy = allocator::new_array(allocator, InStream, readers.len); - copy[..] = readers[..]; - *self = { .readers = copy, .allocator = allocator }; - return self; -} - -<* - @param [&inout] self - @require self.readers.len == 0 "Init may not run on already initialized data" - @ensure self.index == 0 -*> -fn MultiReader* MultiReader.tinit(&self, InStream... readers) -{ - return self.init(tmem(), ...readers); -} - -fn void MultiReader.free(&self) -{ - if (!self.allocator) return; - allocator::free(self.allocator, self.readers); - *self = {}; -} - -fn usz! MultiReader.read(&self, char[] bytes) @dynamic -{ - InStream r = self.readers[self.index]; - usz! n = r.read(bytes); - if (catch err = n) - { - case IoError.EOF: - self.index++; - if (self.index >= self.readers.len) - { - return IoError.EOF?; - } - return self.read(bytes); - default: - return err?; - } - return n; -} - -fn char! MultiReader.read_byte(&self) @dynamic -{ - char[1] data; - self.read(data[..])!; - return data[0]; -} diff --git a/lib7/std/io/stream/multiwriter.c3 b/lib7/std/io/stream/multiwriter.c3 deleted file mode 100644 index ec1dbc4c1..000000000 --- a/lib7/std/io/stream/multiwriter.c3 +++ /dev/null @@ -1,59 +0,0 @@ -module std::io; - -/* MultiWriter implements the OutStream interface and duplicates any write - * operation to all the wrapped writers. - */ -struct MultiWriter (OutStream) -{ - OutStream[] writers; - Allocator allocator; -} - -<* - @param [&inout] self - @param [&inout] allocator - @require writers.len > 0 - @require self.writers.len == 0 "Init may not run on already initialized data" -*> -fn MultiWriter* MultiWriter.init(&self, Allocator allocator, OutStream... writers) -{ - OutStream[] copy = allocator::new_array(allocator, OutStream, writers.len); - copy[..] = writers[..]; - *self = { .writers = copy, .allocator = allocator }; - return self; -} - -<* - @param [&inout] self - @require writers.len > 0 - @require self.writers.len == 0 "Init may not run on already initialized data" -*> -fn MultiWriter* MultiWriter.tinit(&self, OutStream... writers) -{ - return self.init(tmem(), ...writers); -} - -fn void MultiWriter.free(&self) -{ - if (!self.allocator) return; - allocator::free(self.allocator, self.writers); - *self = {}; -} - -fn usz! MultiWriter.write(&self, char[] bytes) @dynamic -{ - usz n; - foreach (w : self.writers) - { - n = w.write(bytes)!; - if (n != bytes.len) return IoError.INCOMPLETE_WRITE?; - } - return bytes.len; -} - -fn void! MultiWriter.write_byte(&self, char c) @dynamic -{ - char[1] data; - data[0] = c; - self.write(data[..])!; -} diff --git a/lib7/std/io/stream/scanner.c3 b/lib7/std/io/stream/scanner.c3 deleted file mode 100644 index 062970ec3..000000000 --- a/lib7/std/io/stream/scanner.c3 +++ /dev/null @@ -1,124 +0,0 @@ -module std::io; - -struct Scanner (InStream) -{ - InStream wrapped_stream; - char[] buf; - usz pattern_idx; - usz read_idx; -} - -<* - Scanner provides a way to read delimited data (with newlines as the default). - The supplied buffer must be at least as large as the expected data length - including its pattern. - - @param [&in] stream "The stream to read data from." - @require buffer.len > 0 "Non-empty buffer required." -*> -fn void Scanner.init(&self, InStream stream, char[] buffer) -{ - *self = { .wrapped_stream = stream, .buf = buffer }; -} - - -<* - Return and clear any remaining unscanned data. -*> -fn char[] Scanner.flush(&self) @dynamic -{ - assert(self.read_idx >= self.pattern_idx); - usz n = self.read_idx - self.pattern_idx; - char[] buf = self.buf[self.pattern_idx:n]; - self.pattern_idx = 0; - self.read_idx = 0; - return buf; -} - -fn void! Scanner.close(&self) @dynamic -{ - if (&self.wrapped_stream.close) return self.wrapped_stream.close(); -} - -<* - Scan the stream for the next split character and return data up to the match. - @require pattern.len > 0 "Non-empty pattern required." - @require self.buf.len > pattern.len "Pattern too large." -*> -fn char[]! Scanner.scan(&self, String pattern = "\n") -{ - if (self.read_idx == 0) - { - // First read. - self.read_idx = self.refill(self.buf)!; - self.pattern_idx = 0; - } - assert(self.read_idx >= self.pattern_idx); - usz n = self.read_idx - self.pattern_idx; - char[] buf = self.buf[self.pattern_idx:n]; - if (try i = self.find(buf, pattern)) - { - self.pattern_idx += i + pattern.len; - return buf[:i]; - } - if (self.pattern_idx == 0 || self.read_idx < self.buf.len) - { - // Split pattern not found with maximized search, abort. - // Split pattern not found and already read as much as possible. - return SearchResult.MISSING?; - } - // Split pattern not found: maximize the search and try one more time. - self.buf[:n] = buf[..]; - self.pattern_idx = 0; - - buf = self.buf[n..]; - usz p = self.refill(buf)!; - self.read_idx = n + p; - - buf = buf[:p]; - usz i = self.find(buf, pattern)!; - self.pattern_idx = n + i + pattern.len; - - return self.buf[:n + i]; -} - -macro usz! Scanner.find(&self, buf, pattern) @private -{ - return ((String)buf).index_of(pattern); -} - -macro usz! Scanner.refill(&self, buf) @private -{ - usz! n = self.wrapped_stream.read(buf); - if (catch err = n) - { - case IoError.EOF: - return SearchResult.MISSING?; - default: - return err?; - } - return n; -} - -fn usz! Scanner.read(&self, char[] bytes) @dynamic -{ - usz n; - if (self.pattern_idx < self.read_idx) - { - n = min(bytes.len, self.read_idx - self.pattern_idx); - bytes[:n] = self.buf[self.pattern_idx:n]; - self.pattern_idx += n; - bytes = bytes[n..]; - } - n += self.wrapped_stream.read(bytes)!; - return n; -} - -fn char! Scanner.read_byte(&self) @dynamic -{ - if (self.pattern_idx < self.read_idx) - { - return self.buf[self.pattern_idx++]; - } - return self.wrapped_stream.read_byte(); -} diff --git a/lib7/std/io/stream/teereader.c3 b/lib7/std/io/stream/teereader.c3 deleted file mode 100644 index fe71bcc0e..000000000 --- a/lib7/std/io/stream/teereader.c3 +++ /dev/null @@ -1,42 +0,0 @@ -module std::io; - -struct TeeReader (InStream) -{ - InStream r; - OutStream w; -} - -<* Returns a reader that implements InStream and that will write any data read - from the wrapped reader r to the writer w. There is no internal buffering. - - @param [&inout] r "Stream r to read from." - @param [&inout] w "Stream w to write to what it reads from r." -*> -macro TeeReader tee_reader(InStream r, OutStream w) => { r, w }; - -<* - @param [&inout] self - @param [&inout] r "Stream r to read from." - @param [&inout] w "Stream w to write to what it reads from r." - *> -fn TeeReader* TeeReader.init(&self, InStream r, OutStream w) -{ - *self = tee_reader(r, w); - return self; -} - -fn usz! TeeReader.read(&self, char[] bytes) @dynamic -{ - usz nr, nw; - nr = self.r.read(bytes)!; - nw = self.w.write(bytes[:nr])!; - if (nr != nw) return IoError.GENERAL_ERROR?; - return nr; -} - -fn char! TeeReader.read_byte(&self) @dynamic -{ - char[1] data; - self.read(data[..])!; - return data[0]; -} diff --git a/lib7/std/libc/libc.c3 b/lib7/std/libc/libc.c3 deleted file mode 100644 index 5696d10b4..000000000 --- a/lib7/std/libc/libc.c3 +++ /dev/null @@ -1,675 +0,0 @@ -// Copyright (c) 2021 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 libc; - -// Constants need to be per os/arch -const int EXIT_FAILURE = 1; -const int EXIT_SUCCESS = 0; -const int RAND_MAX = 0x7fffffff; - -struct DivResult -{ - CInt quot; - CInt rem; -} - -struct LongDivResult -{ - CLong quot; - CLong rem; -} - -fn Errno errno() -{ - return (Errno)os::errno(); -} - -fn void errno_set(Errno e) -{ - os::errno_set((int)e); -} - -distinct Errno = inline CInt; -def TerminateFunction = fn void(); -def CompareFunction = fn int(void*, void*); -def JmpBuf = uptr[$$JMP_BUF_SIZE]; -def Fd = CInt; -def Fpos_t = long; // TODO make sure fpos is correct on all targets. -def SignalFunction = fn void(CInt); - -const CInt SIGHUP = 1; -const CInt SIGINT = 2; -const CInt SIGQUIT = 3; -const CInt SIGILL = 4; -const CInt SIGTRAP = 5; -const CInt SIGABRT = 6; -const CInt SIGABTR @deprecated("use SIGABRT") = SIGABRT; -const CInt SIGBUS = BSD_FLAVOR_SIG ? 10 : 7; // Or Mips -const CInt SIGFPE = 8; -const CInt SIGKILL = 9; -const CInt SIGSEGV = 11; -const CInt SIGSYS = BSD_FLAVOR_SIG ? 12 : 31; -const CInt SIGPIPE = 13; -const CInt SIGALRM = 14; -const CInt SIGTERM = 15; -const CInt SIGURG = BSD_FLAVOR_SIG ? 16 : 23; -const CInt SIGSTOP = BSD_FLAVOR_SIG ? 17 : 19; -const CInt SIGTSTP = BSD_FLAVOR_SIG ? 18 : 20; -const CInt SIGCONT = BSD_FLAVOR_SIG ? 19 : 18; -const CInt SIGCHLD = BSD_FLAVOR_SIG ? 20 : 17; - -const bool BSD_FLAVOR_SIG @local = env::DARWIN || env::BSD_FAMILY; - -def Time_t = $typefrom(env::WIN32 ? long.typeid : CLong.typeid); -def Off_t = $typefrom(env::WIN32 ? int.typeid : usz.typeid); - -module libc @if(env::LIBC); - -extern fn void abort(); -extern fn CInt abs(CInt n); -extern fn ZString asctime(Tm* timeptr); -extern fn ZString asctime_r(Tm* timeptr, char* buf); -extern fn CInt atexit(TerminateFunction func); -extern fn double atof(char* str); -extern fn int atoi(char* str); -extern fn CLongLong atoll(char* str); -extern fn void bsearch(void* key, void *base, usz items, usz size, CompareFunction compare); -extern fn void* calloc(usz count, usz size); -extern fn void clearerr(CFile stream); -extern fn Clock_t clock(); -extern fn CInt close(CInt fd) @if(!env::WIN32); -extern fn double difftime(Time_t time1, Time_t time2) @if(!env::WIN32); -extern fn DivResult div(CInt numer, CInt denom); -extern fn void exit(CInt status); -extern fn CInt fclose(CFile stream); -extern fn CFile fdopen(CInt fd, ZString mode) @if(!env::WIN32); -extern fn CInt feof(CFile stream); -extern fn CInt ferror(CFile stream); -extern fn CInt fflush(CFile stream); -extern fn CInt fgetc(CFile stream); -extern fn ZString fgets(char* string, CInt n, CFile stream); -extern fn CInt fgetpos(CFile stream, Fpos_t* pos); -extern fn Fd fileno(CFile stream) @if(!env::WIN32); -extern fn CFile fopen(ZString filename, ZString mode); -extern fn CInt fprintf(CFile stream, ZString format, ...); -extern fn CInt fputc(CInt c, CFile stream); -extern fn CInt fputs(ZString string, CFile stream); -extern fn usz fread(void* ptr, usz size, usz nmemb, CFile stream); -extern fn void* free(void*); -extern fn CFile freopen(ZString filename, ZString mode, CFile stream); -extern fn CInt fscanf(CFile stream, ZString format, ...); -extern fn CInt fseek(CFile stream, SeekIndex offset, CInt whence) @if(!env::WIN32); -extern fn CInt fsetpos(CFile stream, Fpos_t* pos); -extern fn SeekIndex ftell(CFile stream) @if(!env::WIN32); -extern fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream); -extern fn CInt getc(CFile stream); -extern fn CInt getchar(); -extern fn ZString getenv(ZString name); -extern fn ZString gets(char* buffer); -extern fn Tm* gmtime(Time_t* timer); -extern fn Tm* gmtime_r(Time_t *timer, Tm* buf) @if(!env::WIN32); -extern fn CInt isatty(Fd fd) @if(!env::WIN32); -extern fn CLong labs(CLong x); -extern fn LongDivResult ldiv(CLong number, CLong denom); -extern fn Tm* localtime(Time_t* timer); -extern fn Tm* localtime_r(Time_t* timer, Tm* result) @if(!env::WIN32); -extern fn void longjmp(JmpBuf* buffer, CInt value) @if(!env::NETBSD && !env::OPENBSD); -extern fn void* malloc(usz size); -extern fn void* memchr(void* str, CInt c, usz n); -extern fn CInt memcmp(void* buf1, void* buf2, usz count); -extern fn void* memcpy(void* dest, void* src, usz n); -extern fn void* memmove(void* dest, void* src, usz n); -extern fn void* memset(void* dest, CInt value, usz n); -extern fn Time_t* mktime(Tm* time) @if(!env::WIN32); -extern fn void perror(ZString string); -extern fn CInt printf(ZString format, ...); -extern fn CInt putc(CInt c, CFile stream); -extern fn CInt putchar(CInt c); -extern fn CInt puts(ZString str); -extern fn void qsort(void* base, usz items, usz size, CompareFunction compare); -extern fn CInt raise(CInt signal); -extern fn CInt rand(); -extern fn isz read(Fd fd, void* buf, usz nbyte) @if(!env::WIN32); -extern fn void* realloc(void* ptr, usz size); -extern fn CInt remove(ZString filename); -extern fn CInt rename(ZString old_name, ZString new_name); -extern fn void rewind(CFile stream); -extern fn CInt scanf(ZString format, ...); -extern fn void setbuf(CFile stream, char* buffer); -extern fn int setenv(ZString name, ZString value, CInt overwrite); -extern fn CInt setjmp(JmpBuf* buffer) @if(!env::WIN32 && !env::NETBSD && !env::OPENBSD); -extern fn void setvbuf(CFile stream, char* buf, CInt type, usz size); -extern fn SignalFunction signal(CInt sig, SignalFunction function); -extern fn CInt snprintf(char* buffer, usz size, ZString format, ...); -extern fn CInt sprintf(char* buffer, ZString format, ...); -extern fn void srand(uint seed); -extern fn CInt sscanf(char* buffer, ZString format, ...); -extern fn ZString strcat(ZString dest, ZString src); -extern fn char* strchr(char* str, CInt c); -extern fn CInt strcmp(ZString str1, ZString str2); -extern fn CInt strcoll(ZString str1, ZString str2); -extern fn usz strcspn(ZString str1, ZString str2); -extern fn ZString strcpy(ZString dst, ZString src); -extern fn ZString strerror(CInt errn); -extern fn usz strftime(char* dest, usz maxsize, ZString format, Tm* timeptr); -extern fn usz strlen(ZString str); -extern fn ZString strncat(char* dest, char* src, usz n); -extern fn CInt strncmp(char* str1, char* str2, usz n); -extern fn char* strncpy(char* dst, char* src, usz n); -extern fn CULong stroul(char* str, char** endptr, int base); -extern fn char* strpbrk(ZString str1, ZString str2); -extern fn usz strspn(ZString str1, ZString str2); -extern fn ZString strptime(char* buf, ZString format, Tm* tm); -extern fn char* strrchr(ZString str, CInt c); -extern fn char* strstr(ZString haystack, ZString needle); -extern fn double strtod(char* str, char** endptr); -extern fn float strtof(char* str, char** endptr); -extern fn ZString strtok(ZString str, ZString delim); -extern fn CLong strtol(char* str, char** endptr, CInt base); -extern fn CULong strtoul(char* str, char** endptr, CInt base); -extern fn usz strxfrm(char* dest, ZString src, usz n); -extern fn CInt system(ZString str); -extern fn Time_t timegm(Tm *timeptr) @if(!env::WIN32); -extern fn ZString tmpnam(ZString str); -extern fn CFile tmpfile(); -extern fn CInt ungetc(CInt c, CFile stream); -extern fn CInt unsetenv(ZString name); -extern fn isz write(Fd fd, void* buffer, usz count) @if(!env::WIN32); - -extern fn CFile fmemopen(void* ptr, usz size, ZString mode); -extern fn isz getline(char** linep, usz* linecapp, CFile stream); -extern fn CInt timespec_get(TimeSpec* ts, CInt base); -extern fn CInt nanosleep(TimeSpec* req, TimeSpec* remaining); -extern fn ZString ctime(Time_t *timer); -extern fn Time_t time(Time_t *timer); - -const CInt STDIN_FD = 0; -const CInt STDOUT_FD = 1; -const CInt STDERR_FD = 2; - -module libc @if(env::LINUX); -extern CFile __stdin @extern("stdin"); -extern CFile __stdout @extern("stdout"); -extern CFile __stderr @extern("stderr"); -extern fn usz malloc_usable_size(void* ptr); -macro usz malloc_size(void* ptr) => malloc_usable_size(ptr); -extern fn void* aligned_alloc(usz align, usz size); -macro CFile stdin() => __stdin; -macro CFile stdout() => __stdout; -macro CFile stderr() => __stderr; - -module libc @if(env::NETBSD || env::OPENBSD); -extern fn int fcntl(CInt socket, int cmd, ...); -extern fn int _setjmp(void*); -macro int setjmp(void* ptr) => _setjmp(ptr); -extern fn int _longjmp(void*, int); -macro usz longjmp(void* ptr, CInt i) => _longjmp(ptr, i); -extern fn usz malloc_size(void* ptr); -extern fn void* aligned_alloc(usz align, usz size); -macro CFile stdin() { return fdopen(0, "r"); } -macro CFile stdout() { return fdopen(1, "w"); } -macro CFile stderr() { return fdopen(2, "w"); } - -module libc @if(env::DARWIN || env::FREEBSD); -extern CFile __stdinp; -extern CFile __stdoutp; -extern CFile __stderrp; -extern fn usz malloc_size(void* ptr) @if(!env::FREEBSD); -extern fn void* aligned_alloc(usz align, usz size); -macro CFile stdin() => __stdinp; -macro CFile stdout() => __stdoutp; -macro CFile stderr() => __stderrp; - -module libc @if(env::FREEBSD); -extern fn usz malloc_usable_size(void* ptr); -macro usz malloc_size(void* ptr) => malloc_usable_size(ptr); - -module libc @if(env::WIN32); -macro usz malloc_size(void* ptr) => _msize(ptr); -macro CFile stdin() => __acrt_iob_func(STDIN_FD); -macro CFile stdout() => __acrt_iob_func(STDOUT_FD); -macro CFile stderr() => __acrt_iob_func(STDERR_FD); - -module libc @if(env::LIBC && !env::WIN32 && !env::LINUX && !env::DARWIN && !env::BSD_FAMILY); -macro CFile stdin() { return (CFile*)(uptr)STDIN_FD; } -macro CFile stdout() { return (CFile*)(uptr)STDOUT_FD; } -macro CFile stderr() { return (CFile*)(uptr)STDERR_FD; } - -module libc @if(!env::LIBC); - -fn void longjmp(JmpBuf* buffer, CInt value) @weak @extern("longjmp") @nostrip -{ - unreachable("longjmp unavailable"); -} - -fn CInt setjmp(JmpBuf* buffer) @weak @extern("setjmp") @nostrip -{ - unreachable("setjmp unavailable"); -} - -fn void* malloc(usz size) @weak @extern("malloc") @nostrip -{ - unreachable("malloc unavailable"); -} -fn void* calloc(usz count, usz size) @weak @extern("calloc") @nostrip -{ - unreachable("calloc unavailable"); -} -fn void* free(void*) @weak @extern("free") -{ - unreachable("free unavailable"); -} - -fn void* realloc(void* ptr, usz size) @weak @extern("realloc") @nostrip -{ - unreachable("realloc unavailable"); -} - -fn void* memcpy(void* dest, void* src, usz n) @weak @extern("memcpy") @nostrip -{ - for (usz i = 0; i < n; i++) ((char*)dest)[i] = ((char*)src)[i]; - return dest; -} - -fn void* memmove(void* dest, void* src, usz n) @weak @extern("memmove") @nostrip -{ - return memcpy(dest, src, n) @inline; -} - -fn void* memset(void* dest, CInt value, usz n) @weak @extern("memset") @nostrip -{ - for (usz i = 0; i < n; i++) ((char*)dest)[i] = (char)value; - return dest; -} - -fn int fseek(CFile stream, SeekIndex offset, int whence) @weak @extern("fseek") @nostrip -{ - unreachable("'fseek' not available."); -} -fn CFile fopen(ZString filename, ZString mode) @weak @extern("fopen") @nostrip -{ - unreachable("'fopen' not available."); -} - -fn CFile freopen(ZString filename, ZString mode, CFile stream) @weak @extern("fopen") @nostrip -{ - unreachable("'freopen' not available."); -} - -fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream) @weak @extern("fwrite") @nostrip -{ - unreachable("'fwrite' not available."); -} - -fn usz fread(void* ptr, usz size, usz nmemb, CFile stream) @weak @extern("fread") @nostrip -{ - unreachable("'fread' not available."); -} - -fn CFile fclose(CFile) @weak @extern("fclose") @nostrip -{ - unreachable("'fclose' not available."); -} - -fn int fflush(CFile stream) @weak @extern("fflush") @nostrip -{ - unreachable("'fflush' not available."); -} - -fn int fputc(int c, CFile stream) @weak @extern("fputc") @nostrip -{ - unreachable("'fputc' not available."); -} - -fn char* fgets(ZString str, int n, CFile stream) @weak @extern("fgets") @nostrip -{ - unreachable("'fgets' not available."); -} - -fn int fgetc(CFile stream) @weak @extern("fgetc") @nostrip -{ - unreachable("'fgetc' not available."); -} - -fn int feof(CFile stream) @weak @extern("feof") @nostrip -{ - unreachable("'feof' not available."); -} - -fn int putc(int c, CFile stream) @weak @extern("putc") @nostrip -{ - unreachable("'putc' not available."); -} -fn int putchar(int c) @weak @extern("putchar") @nostrip -{ - unreachable("'putchar' not available."); -} -fn int puts(ZString str) @weak @extern("puts") @nostrip -{ - unreachable("'puts' not available."); -} - -module libc; - -// stdio - -def CFile = void*; - - -const HAS_MALLOC_SIZE = env::LINUX || env::WIN32 || env::DARWIN; - -// The following needs to be set per arch+os -// For now I have simply pulled the defaults from MacOS -const int SEEK_SET = 0; -const int SEEK_CUR = 1; -const int SEEK_END = 2; -const int _IOFBF = 0; // Fully buffered -const int _IOLBF = 1; // Line buffered -const int _IONBF = 2; // Unbuffered -const int BUFSIZ = 1024; -const int EOF = -1; -const int FOPEN_MAX = 20; -const int FILENAME_MAX = 1024; - -macro bool libc_S_ISTYPE(value, mask) @builtin => (value & S_IFMT) == mask; -const S_IFMT = 0o170000; // type of file mask -const S_IFIFO = 0o010000; // named pipe (fifo) -const S_IFCHR = 0o020000; // character special -const S_IFDIR = 0o040000; // directory -const S_IFBLK = 0o060000; // block special -const S_IFREG = 0o100000; // regular -const S_IFLNK = 0o120000; // symbolic link -const S_IFSOCK = 0o140000; // socket -const S_ISUID = 0o004000; // Set user id on execution -const S_ISGID = 0o002000; // Set group id on execution -const S_ISVTX = 0o001000; // Save swapped text even after use -const S_IRUSR = 0o000400; // Read permission, owner -const S_IWUSR = 0o000200; // Write permission, owner -const S_IXUSR = 0o000100; // Execute/search permission, owner - -def SeekIndex = CLong; - -// vsprintf vprintf not supported - -// time.h - -struct Tm -{ - CInt tm_sec; // seconds after the minute [0-60] - CInt tm_min; // minutes after the hour [0-59] - CInt tm_hour; // hours since midnight [0-23] - CInt tm_mday; // day of the month [1-31] - CInt tm_mon; // months since January [0-11] - CInt tm_year; // years since 1900 - CInt tm_wday; // days since Sunday [0-6] - CInt tm_yday; // days since January 1 [0-365] - CInt tm_isdst; // Daylight Savings Time flag - TimeOffset tm_gmtoff @if(!env::WIN32); /* offset from UTC in seconds */ - char *tm_zone @if(!env::WIN32); /* timezone abbreviation */ - CInt tm_nsec @if(env::WASI); -} - -struct TimeSpec -{ - Time_t s; - ulong ns @if(env::WIN32); - CLong ns @if(!env::WIN32); -} - - -def Clock_t = int @if(env::WIN32); -def Clock_t = CULong @if(!env::WIN32); - -def TimeOffset = int @if(env::WASI) ; -def TimeOffset = CLong @if(!env::WASI) ; - -const int TIME_UTC = 1; - - -// Likely wrong, must be per platform. -const CLOCKS_PER_SEC = 1000000; - -module libc::errno; - -const Errno OK = 0; -const Errno EPERM = 1; // Operation not permitted -const Errno ENOENT = 2; // No such file or directory -const Errno ESRCH = 3; // No such process -const Errno EINTR = 4; // Interrupted system call -const Errno EIO = 5; // I/O error -const Errno ENXIO = 6; // No such device or address -const Errno E2BIG = 7; // Argument list too long -const Errno ENOEXEC = 8; // Exec format error -const Errno EBADF = 9; // Bad file number -const Errno ECHILD = 10; // No child processes - - -const Errno EAGAIN @if(env::DARWIN) = 35; // Try again Macos -const Errno EAGAIN @if(!env::DARWIN) = 11; // Try again - -const Errno ENOMEM = 12; // Out of memory -const Errno EACCES = 13; // Permission denied -const Errno EFAULT = 14; // Bad address -const Errno ENOTBLK = 15; // Block device required, not on Win32 -const Errno EBUSY = 16; // Device or resource busy -const Errno EEXIST = 17; // File exists -const Errno EXDEV = 18; // Cross-device link -const Errno ENODEV = 19; // No such device -const Errno ENOTDIR = 20; // Not a directory -const Errno EISDIR = 21; // Is a directory -const Errno EINVAL = 22; // Invalid argument -const Errno ENFILE = 23; // File table overflow -const Errno EMFILE = 24; // Too many open files -const Errno ENOTTY = 25; // Not a typewriter -const Errno ETXTBSY = 26; // Text file busy, not on Win32 -const Errno EFBIG = 27; // File too large -const Errno ENOSPC = 28; // No space left on device -const Errno ESPIPE = 29; // Illegal seek -const Errno EROFS = 30; // Read-only file system -const Errno EMLINK = 31; // Too many links -const Errno EPIPE = 32; // Broken pipe -const Errno EDOM = 33; // Math argument out of domain of func -const Errno ERANGE = 34; // Math result not representable - -// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/intro.2.html -module libc::errno @if(env::DARWIN); -const Errno EWOULDBLOCK = EAGAIN; // Operation would block -const Errno EDEADLK = 11; // Resource deadlock would occur -const Errno EINPROGRESS = 36; // Operation now in progress -const Errno EALREADY = 37; // Operation already in progress -const Errno ENOTSOCK = 38; // Socket operation on non-socket -const Errno EDESTADDRREQ = 39; // Destination address required -const Errno EMSGSIZE = 40; // Message too long -const Errno EPROTOTYPE = 41; // Protocol wrong type for socket -const Errno ENOPROTOOPT = 42; // Protocol not available -const Errno EPROTONOSUPPORT = 43; // Protocol not supported -const Errno ESOCKTNOSUPPORT = 44; // Socket type not supported -const Errno ENOTSUP = 45; // Not supported -const Errno EPFNOSUPPORT = 46; // Protocol family not supported -const Errno EAFNOSUPPORT = 47; // Address family not supported by protocol family -const Errno EADDRINUSE = 48; // Address already in use -const Errno EADDRNOTAVAIL = 49; // Cannot assign requested address -const Errno ENETDOWN = 50; // Network is down -const Errno ENETUNREACH = 51; // Network is unreachable -const Errno ENETRESET = 52; // Network dropped connection on reset -const Errno ECONNABORTED = 53; // Software caused connection abort -const Errno ECONNRESET = 54; // Connection reset by peer -const Errno ENOBUFS = 55; // No buffer space available -const Errno EISCONN = 56; // Socket is already connected -const Errno ENOTCONN = 57; // Socket is not connected -const Errno ESHUTDOWN = 58; // Cannot send after socket shutdown -const Errno ETIMEDOUT = 60; // Operation timed out -const Errno ECONNREFUSED = 61; // Connection refused -const Errno ELOOP = 62; // Too many levels of symbolic links -const Errno ENAMETOOLONG = 63; // File name too long -const Errno EHOSTDOWN = 64; // Host is down -const Errno EHOSTUNREACH = 65; // No route to host -const Errno ENOTEMPTY = 66; // Directory not empty -const Errno EPROCLIM = 67; // Too many processes -const Errno EUSERS = 68; // Too many users -const Errno EDQUOT = 69; // Disc quota exceeded -const Errno ESTALE = 70; // Stale NFS file handle -const Errno EBADRPC = 72; // RPC struct is bad -const Errno ERPCMISMATCH = 73; // RPC version wrong -const Errno EPROGUNAVAIL = 74; // RPC prog. not avail -const Errno EPROGMISMATCH = 75; // Program version wrong -const Errno EPROCUNAVAIL = 76; // Bad procedure for program -const Errno ENOLCK = 77; // No locks available -const Errno ENOSYS = 78; // Function not implemented -const Errno EFTYPE = 79; // Inappropriate file type or format -const Errno EAUTH = 80; // Authentication error -const Errno ENEEDAUTH = 81; // Need authenticator -const Errno EPWROFF = 82; // Device power is off -const Errno EDEVERR = 83; // Device error -const Errno EOVERFLOW = 84; // Value too large to be stored in data type -const Errno EBADEXEC = 85; // Bad executable (or shared library) -const Errno EBADARCH = 86; // Bad CPU type in executable -const Errno ESHLIBVERS = 87; // Shared library version mismatch -const Errno EBADMACHO = 88; // Malformed Mach-o file -const Errno ECANCELED = 89; // Operation canceled -const Errno EIDRM = 90; // Identifier removed -const Errno ENOMSG = 91; // No message of desired type -const Errno EILSEQ = 92; // Illegal byte sequence -const Errno ENOATTR = 93; // Attribute not found -const Errno EBADMSG = 94; // Bad message -const Errno EMULTIHOP = 95; // Reserved -const Errno ENODATA = 96; // No message available -const Errno ENOLINK = 97; // Reserved -const Errno ENOSR = 98; // No STREAM resources -const Errno ENOSTR = 99; // Not a STREAM -const Errno EPROTO = 100; // Protocol error -const Errno ETIME = 101; // STREAM ioctl() timeout -const Errno EOPNOTSUPP = 102; // Operation not supported on socket - -module libc::errno @if(env::WIN32); -const Errno EDEADLK = 36; // Resource deadlock would occur Win32 -const Errno ENAMETOOLONG = 38; // File name too long Win32 -const Errno ENOTEMPTY = 41; // Directory not empty -const Errno ELOOP = 114; // Too many symbolic links encountered -const Errno EOVERFLOW = 132; // Value too large for defined data type -const Errno ENETDOWN = 116; // Network is down -const Errno ECONNRESET = 108; // Connection reset by peer -const Errno ENETUNREACH = 118; // Network is unreachable -const Errno ENETRESET = 117; // Network dropped connection because of reset -const Errno EOPNOTSUPP = 130; // Operation not supported on transport endpoint -const Errno ETIMEDOUT = 138; // Connection timed out -const Errno EALREADY = 103; // Operation already in progress -const Errno EINPROGRESS = 112; // Operation now in progress Win32 -const Errno EDQUOT = -122; // Quota exceeded, not in Win32 -const Errno EWOULDBLOCK = 140; // Operation would block - -module libc::errno @if(!env::WIN32 && !env::DARWIN); -const Errno EDEADLK = 35; // Resource deadlock would occur Linux (others?) -const Errno ENAMETOOLONG = 36; // File name too long Linux (others?) -const Errno ENOTEMPTY = 39; // Directory not empty -const Errno ELOOP = 40; // Too many symbolic links encountered -const Errno EWOULDBLOCK = EAGAIN; // Operation would block -const Errno EOVERFLOW = 75; // Value too large for defined data type -const Errno ENOTSOCK = 88; // Socket operation on non-socket -const Errno EOPNOTSUPP = 95; // Operation not supported on transport endpoint -const Errno EADDRINUSE = 98; // Address already in use -const Errno EADDRNOTAVAIL = 99; // Cannot assign requested address -const Errno ENETDOWN = 100; // Network is down -const Errno ENETUNREACH = 101; // Network is unreachable -const Errno ENETRESET = 102; // Network dropped connection because of reset -const Errno ECONNRESET = 104; // Connection reset by peer -const Errno EISCONN = 106; // Socket is already connected -const Errno ETIMEDOUT = 110; // Connection timed out -const Errno ECONNREFUSED = 111; // Connection refused -const Errno EALREADY = 114; // Operation already in progress -const Errno EINPROGRESS = 115; // Operation now in progress -const Errno EDQUOT = 122; // Quota exceeded - - -/* -const Errno ENOLCK = 37; /* No record locks available */ -const Errno ENOSYS = 38; /* Function not implemented */ - -const Errno ENOMSG = 42; /* No message of desired type */ -const Errno EIDRM = 43; /* Identifier removed */ -const Errno ECHRNG = 44; /* Channel number out of range */ -const Errno EL2NSYNC = 45; /* Level 2 not synchronized */ -const Errno EL3HLT = 46; /* Level 3 halted */ -const Errno EL3RST = 47; /* Level 3 reset */ -const Errno ELNRNG = 48; /* Link number out of range */ -const Errno EUNATCH = 49; /* Protocol driver not attached */ -const Errno ENOCSI = 50; /* No CSI structure available */ -const Errno EL2HLT = 51; /* Level 2 halted */ -const Errno EBADE = 52; /* Invalid exchange */ -const Errno EBADR = 53; /* Invalid request descriptor */ -const Errno EXFULL = 54; /* Exchange full */ -const Errno ENOANO = 55; /* No anode */ -const Errno EBADRQC = 56; /* Invalid request code */ -const Errno EBADSLT = 57; /* Invalid slot */ - -const Errno EBFONT = 59; /* Bad font file format */ -const Errno ENOSTR = 60; /* Device not a stream */ -const Errno ENODATA = 61; /* No data available */ -const Errno ETIME = 62; /* Timer expired */ -const Errno ENOSR = 63; /* Out of streams resources */ -const Errno ENONET = 64; /* Machine is not on the network */ -const Errno ENOPKG = 65; /* Package not installed */ -const Errno EREMOTE = 66; /* Object is remote */ -const Errno ENOLINK = 67; /* Link has been severed */ -const Errno EADV = 68; /* Advertise error */ -const Errno ESRMNT = 69; /* Srmount error */ -const Errno ECOMM = 70; /* Communication error on send */ -const Errno EPROTO = 71; /* Protocol error */ -const Errno EMULTIHOP = 72; /* Multihop attempted */ -const Errno EDOTDOT = 73; /* RFS specific error */ -const Errno EBADMSG = 74; /* Not a data message */ -const Errno ENOTUNIQ = 76; /* Name not unique on network */ -const Errno EBADFD = 77; /* File descriptor in bad state */ -const Errno EREMCHG = 78; /* Remote address changed */ -const Errno ELIBACC = 79; /* Can not access a needed shared library */ -const Errno ELIBBAD = 80; /* Accessing a corrupted shared library */ -const Errno ELIBSCN = 81; /* .lib section in a.out corrupted */ -const Errno ELIBMAX = 82; /* Attempting to link in too many shared libraries */ -const Errno ELIBEXEC = 83; /* Cannot exec a shared library directly */ -const Errno EILSEQ = 84; /* Illegal byte sequence */ -const Errno ERESTART = 85; /* Interrupted system call should be restarted */ -const Errno ESTRPIPE = 86; /* Streams pipe error */ -const Errno EUSERS = 87; /* Too many users */ -const Errno ENOTSOCK = 88; /* Socket operation on non-socket */ -const Errno EDESTADDRREQ = 89; /* Destination address required */ -const Errno EMSGSIZE = 90; /* Message too long */ -const Errno EPROTOTYPE = 91; /* Protocol wrong type for socket */ -const Errno ENOPROTOOPT = 92; /* Protocol not available */ -const Errno EPROTONOSUPPORT = 93; /* Protocol not supported */ -const Errno ESOCKTNOSUPPORT = 94; /* Socket type not supported */ -const Errno EPFNOSUPPORT = 96; /* Protocol family not supported */ -const Errno EAFNOSUPPORT = 97; /* Address family not supported by protocol */ -const Errno EADDRINUSE = 98; /* Address already in use */ -const Errno ECONNABORTED = 103; /* Software caused connection abort */ -const Errno ENOBUFS = 105; /* No buffer space available */ -const Errno EISCONN = 106; /* Transport endpoint is already connected */ -const Errno ENOTCONN = 107; /* Transport endpoint is not connected */ -const Errno ESHUTDOWN = 108; /* Cannot send after transport endpoint shutdown */ -const Errno ETOOMANYREFS = 109; /* Too many references: cannot splice */ -const Errno ECONNREFUSED = 111; /* Connection refused */ -const Errno EHOSTDOWN = 112; /* Host is down */ -const Errno EHOSTUNREACH = 113; /* No route to host */ -*/ - - -/* -const Errno ESTALE = 116; /* Stale NFS file handle */ -const Errno EUCLEAN = 117; /* Structure needs cleaning */ -const Errno ENOTNAM = 118; /* Not a XENIX named type file */ -const Errno ENAVAIL = 119; /* No XENIX semaphores available */ -const Errno EISNAM = 120; /* Is a named type file */ -const Errno EREMOTEIO = 121; /* Remote I/O error */ - -const Errno ENOMEDIUM = 123; /* No medium found */ -const Errno EMEDIUMTYPE = 124; /* Wrong medium type */ -const Errno ECANCELED = 125; /* Operation Canceled */ -const Errno ENOKEY = 126; /* Required key not available */ -const Errno EKEYEXPIRED = 127; /* Key has expired */ -const Errno EKEYREVOKED = 128; /* Key has been revoked */ -const Errno EKEYREJECTED = 129; /* Key was rejected by service */ - -const Errno EOWNERDEAD = 130; /* Owner died */ -const Errno ENOTRECOVERABLE = 131; /* State not recoverable */ -*/ - diff --git a/lib7/std/libc/libc_extra.c3 b/lib7/std/libc/libc_extra.c3 deleted file mode 100644 index 4c4142513..000000000 --- a/lib7/std/libc/libc_extra.c3 +++ /dev/null @@ -1,26 +0,0 @@ -module libc; -import std::time; - -<* - Return a "timespec" from a duration. - - @require self >= 0 -*> -fn TimeSpec NanoDuration.to_timespec(self) @inline -{ - CLong ns = (CLong)(self % 1000_000_000); - Time_t sec = (Time_t)(self / 1000_000_000); - return { .s = sec, .ns = ns }; -} - -<* - Convert a duration to a timespec. - - @require self >= 0 -*> -fn TimeSpec Duration.to_timespec(self) @inline -{ - CLong ns = (CLong)(1000 * (self % time::SEC)); - Time_t sec = (Time_t)(self / time::SEC); - return { .s = sec, .ns = ns }; -} diff --git a/lib7/std/libc/os/darwin.c3 b/lib7/std/libc/os/darwin.c3 deleted file mode 100644 index ab632aae9..000000000 --- a/lib7/std/libc/os/darwin.c3 +++ /dev/null @@ -1,35 +0,0 @@ -module libc @if(env::DARWIN); - -def Dev_t = int; -def Mode_t = ushort; -def Nlink_t = ushort; -def Blkcnt_t = long; -def Blksize_t = int; -def Ino_t = ulong; - -struct Stat -{ - Dev_t st_dev; - Mode_t st_mode; - Nlink_t st_nlink; - Ino_t st_ino; - Uid_t st_uid; - Gid_t st_gid; - Dev_t st_rdev; - - TimeSpec st_atimespec; // time of last access - TimeSpec st_mtimespec; // time of last data modification - TimeSpec st_ctimespec; // time of last status change - TimeSpec st_birthtimespec; // time of file creation(birth) - Off_t st_size; // file size, in bytes - Blkcnt_t st_blocks; // blocks allocated for file - Blksize_t st_blocksize; // optimal blocksize for I/O - uint st_flags; // user defined flags for file - uint st_gen; // file generation number - int st_lspare; // RESERVED - long[2] st_qspare; // RESERVED -} - -extern fn int stat(ZString str, Stat* stat) @extern("stat64"); - -extern fn CInt sysctl(CInt *name, CUInt namelen, void *oldp, usz *oldlenp, void *newp, usz newlen); diff --git a/lib7/std/libc/os/errno.c3 b/lib7/std/libc/os/errno.c3 deleted file mode 100644 index 66679fb01..000000000 --- a/lib7/std/libc/os/errno.c3 +++ /dev/null @@ -1,28 +0,0 @@ -module libc::os; - -// Linux -extern fn int* __errno_location() @if(env::LINUX); -macro int errno() @if(env::LINUX) => *__errno_location(); -macro void errno_set(int err) @if(env::LINUX) => *(__errno_location()) = err; - -// Darwin -extern fn int* __error() @if(env::DARWIN); -macro int errno() @if(env::DARWIN) => *__error(); -macro void errno_set(int err) @if(env::DARWIN) => *(__error()) = err; - -// Win32 -macro int errno() @if(env::WIN32) -{ - int holder; - _get_errno(&holder); - return holder; -} -macro void errno_set(int err) @if(env::WIN32) => _set_errno(err); -extern fn void _get_errno(int* result) @if(env::WIN32); -extern fn void _set_errno(int err) @if(env::WIN32); - -// Default -const ERRNO_DEFAULT @local = !env::LINUX && !env::DARWIN && !env::WIN32; -tlocal int _errno_c3 @if(ERRNO_DEFAULT) = 0; -fn void errno_set(int err) @if(ERRNO_DEFAULT) => _errno_c3 = err; -fn int errno() @if(ERRNO_DEFAULT) => _errno_c3; \ No newline at end of file diff --git a/lib7/std/libc/os/freebsd.c3 b/lib7/std/libc/os/freebsd.c3 deleted file mode 100644 index 39b81b24c..000000000 --- a/lib7/std/libc/os/freebsd.c3 +++ /dev/null @@ -1,62 +0,0 @@ -module libc @if(env::FREEBSD); - -// Checked for x86_64, this is notoriously incorrect when comparing with Rust code etc - -def Blksize_t = $typefrom(env::X86_64 ? long.typeid : CInt.typeid); -def Nlink_t = $typefrom(env::X86_64 ? ulong.typeid : CUInt.typeid); -def Blkcnt_t = long; -def Ino_t = ulong; -def Dev_t = ulong; -def Mode_t = uint; -def Ino64_t = ulong; -def Blkcnt64_t = long; - -struct Stat @if(env::X86_64) -{ - Dev_t st_dev; - Ino_t st_ino; - Nlink_t st_nlink; - Mode_t st_mode; - Uid_t st_uid; - Gid_t st_gid; - CInt __pad0; - Dev_t st_rdev; - Off_t st_size; - Blksize_t st_blksize; - Blkcnt_t st_blocks; - Time_t st_atime; - long st_atime_nsec; - Time_t st_mtime; - long st_mtime_nsec; - Time_t st_ctime; - long st_ctime_nsec; - long[3] __unused; -} - -struct Stat @if(!env::X86_64) -{ - Dev_t st_dev; - Ino_t st_ino; - Mode_t st_mode; - Nlink_t st_nlink; - Uid_t st_uid; - Gid_t st_gid; - Dev_t st_rdev; - CInt __pad1; - Off_t st_size; - Blksize_t st_blksize; - CInt __pad2; - Blkcnt_t st_blocks; - Time_t st_atime; - long st_atime_nsec; - Time_t st_mtime; - long st_mtime_nsec; - Time_t st_ctime; - long st_ctime_nsec; - CInt[2] __unused; -} - -extern fn CInt stat(ZString path, Stat* stat); - -extern fn CInt get_nprocs(); -extern fn CInt get_nprocs_conf(); diff --git a/lib7/std/libc/os/linux.c3 b/lib7/std/libc/os/linux.c3 deleted file mode 100644 index 8564233bc..000000000 --- a/lib7/std/libc/os/linux.c3 +++ /dev/null @@ -1,62 +0,0 @@ -module libc @if(env::LINUX); - -// Checked for x86_64, this is notoriously incorrect when comparing with Rust code etc - -def Blksize_t = $typefrom(env::X86_64 ? long.typeid : CInt.typeid); -def Nlink_t = $typefrom(env::X86_64 ? ulong.typeid : CUInt.typeid); -def Blkcnt_t = long; -def Ino_t = ulong; -def Dev_t = ulong; -def Mode_t = uint; -def Ino64_t = ulong; -def Blkcnt64_t = long; - -struct Stat @if(env::X86_64) -{ - Dev_t st_dev; - Ino_t st_ino; - Nlink_t st_nlink; - Mode_t st_mode; - Uid_t st_uid; - Gid_t st_gid; - CInt __pad0; - Dev_t st_rdev; - Off_t st_size; - Blksize_t st_blksize; - Blkcnt_t st_blocks; - Time_t st_atime; - long st_atime_nsec; - Time_t st_mtime; - long st_mtime_nsec; - Time_t st_ctime; - long st_ctime_nsec; - long[3] __unused; -} - -struct Stat @if(!env::X86_64) -{ - Dev_t st_dev; - Ino_t st_ino; - Mode_t st_mode; - Nlink_t st_nlink; - Uid_t st_uid; - Gid_t st_gid; - Dev_t st_rdev; - CInt __pad1; - Off_t st_size; - Blksize_t st_blksize; - CInt __pad2; - Blkcnt_t st_blocks; - Time_t st_atime; - long st_atime_nsec; - Time_t st_mtime; - long st_mtime_nsec; - Time_t st_ctime; - long st_ctime_nsec; - CInt[2] __unused; -} - -extern fn CInt stat(ZString path, Stat* stat); - -extern fn CInt get_nprocs(); -extern fn CInt get_nprocs_conf(); diff --git a/lib7/std/libc/os/posix.c3 b/lib7/std/libc/os/posix.c3 deleted file mode 100644 index 7ca9a89d1..000000000 --- a/lib7/std/libc/os/posix.c3 +++ /dev/null @@ -1,200 +0,0 @@ -module libc @if(env::POSIX); - -const CInt SHUT_RD = 0; -const CInt SHUT_WR = 1; -const CInt SHUT_RDWR = 2; -extern fn CInt shutdown(Fd sockfd, CInt how); - -extern fn isz recv(Fd socket, void *buffer, usz length, CInt flags); -extern fn isz send(Fd socket, void *buffer, usz length, CInt flags); - -extern fn void* dlopen(ZString path, int flags); -extern fn CInt dlclose(void*); -extern fn void* dlsym(void* handle, ZString symbol); - -const int RTLD_LAZY = 0x1; -const int RTLD_NOW = 0x2; -const int RTLD_LOCAL = 0x4; -const int RTLD_GLOBAL = 0x8; -const int RTLD_NODELETE = 0x1000; - -def Pid_t = int; -def Uid_t = uint; -def Gid_t = uint; - -const CUInt SA_ONSTACK = env::LINUX ? 0x08000000 : 0x0001; -const CUInt SA_RESTART = env::LINUX ? 0x10000000 : 0x0002; -const CUInt SA_RESETHAND = env::LINUX ? 0x80000000 : 0x0004; -const CUInt SA_SIGINFO = env::LINUX ? 0x00000004 : 0x0040; - -def Sigset_t = uint @if(!env::LINUX); -def Sigset_t = ulong[16] @if(env::LINUX); -def SigActionFunction = fn void(CInt, void*, void*); - -struct Sigaction -{ - union - { - SignalFunction sa_handler; - SigActionFunction sa_sigaction; - } - CInt sa_flags @if(env::BSD_FAMILY); - Sigset_t sa_mask; // 128 - CInt sa_flags @if(!env::BSD_FAMILY); - void* sa_restorer @if(env::LINUX); -} - -struct Stack_t -{ - void* ss_sp; - struct @if(!env::LINUX) - { - usz ss_size; - CInt ss_flags; - } - struct @if(env::LINUX) - { - CInt ss_flags; - usz ss_size; - } -} - -extern fn CInt sigaltstack(Stack_t* ss, Stack_t* old_ss); -extern fn CInt sigaction(CInt signum, Sigaction *action, Sigaction *oldaction); - -module libc::termios @if(env::LIBC &&& env::POSIX); - -distinct Cc = char; -distinct Speed = CUInt; -distinct Tcflags = CUInt; -distinct Tcactions = CInt; - -const Tcactions TCOOFF = 0; -const Tcactions TCOON = 1; -const Tcactions TCIOFF = 2; -const Tcactions TCION = 3; -const Tcactions TCIFLUSH = 0; -const Tcactions TCOFLUSH = 1; -const Tcactions TCIOFLUSH = 2; -const Tcactions TCSANOW = 0; -const Tcactions TCSADRAIN = 1; -const Tcactions TCSAFLUSH = 2; -const Speed B0 = 0000000; -const Speed B50 = 0000001; -const Speed B75 = 0000002; -const Speed B110 = 0000003; -const Speed B134 = 0000004; -const Speed B150 = 0000005; -const Speed B200 = 0000006; -const Speed B300 = 0000007; -const Speed B600 = 0000010; -const Speed B1200 = 0000011; -const Speed B1800 = 0000012; -const Speed B2400 = 0000013; -const Speed B4800 = 0000014; -const Speed B9600 = 0000015; -const Speed B19200 = 0000016; -const Speed B38400 = 0000017; -const Speed B57600 = 0010001; -const Speed B115200 = 0010002; -const Speed B230400 = 0010003; -const Speed B460800 = 0010004; -const Speed B500000 = 0010005; -const Speed B576000 = 0010006; -const Speed B921600 = 0010007; -const Speed B1000000 = 0010010; -const Speed B1152000 = 0010011; -const Speed B1500000 = 0010012; -const Speed B2000000 = 0010013; -const Speed B2500000 = 0010014; -const Speed B3000000 = 0010015; -const Speed B3500000 = 0010016; -const Speed B4000000 = 0010017; -const Speed MAX_BAUD = B4000000; -const Tcflags VINTR = 0; -const Tcflags VQUIT = 1; -const Tcflags VERASE = 2; -const Tcflags VKILL = 3; -const Tcflags VEOF = 4; -const Tcflags VTIME = 5; -const Tcflags VMIN = 6; -const Tcflags VSWTC = 7; -const Tcflags VSTART = 8; -const Tcflags VSTOP = 9; -const Tcflags VSUSP = 10; -const Tcflags VEOL = 11; -const Tcflags VREPRINT = 12; -const Tcflags VDISCARD = 13; -const Tcflags VWERASE = 14; -const Tcflags VLNEXT = 15; -const Tcflags VEOL2 = 16; -const Tcflags ISIG = 0000001; -const Tcflags ICANON = 0000002; -const Tcflags ECHO = 0000010; -const Tcflags ECHOE = 0000020; -const Tcflags ECHOK = 0000040; -const Tcflags ECHONL = 0000100; -const Tcflags NOFLSH = 0000200; -const Tcflags TOSTOP = 0000400; -const Tcflags IEXTEN = 0100000; -const Tcflags CSIZE = 0000060; -const Tcflags CS5 = 0000000; -const Tcflags CS6 = 0000020; -const Tcflags CS7 = 0000040; -const Tcflags CS8 = 0000060; -const Tcflags CSTOPB = 0000100; -const Tcflags CREAD = 0000200; -const Tcflags PARENB = 0000400; -const Tcflags PARODD = 0001000; -const Tcflags HUPCL = 0002000; -const Tcflags CLOCAL = 0004000; -const Tcflags OPOST = 0000001; -const Tcflags OLCUC = 0000002; -const Tcflags ONLCR = 0000004; -const Tcflags OCRNL = 0000010; -const Tcflags ONOCR = 0000020; -const Tcflags ONLRET = 0000040; -const Tcflags OFILL = 0000100; -const Tcflags OFDEL = 0000200; -const Tcflags VTDLY = 0040000; -const Tcflags VT0 = 0000000; -const Tcflags VT1 = 0040000; -const Tcflags IGNBRK = 0000001; -const Tcflags BRKINT = 0000002; -const Tcflags IGNPAR = 0000004; -const Tcflags PARMRK = 0000010; -const Tcflags INPCK = 0000020; -const Tcflags ISTRIP = 0000040; -const Tcflags INLCR = 0000100; -const Tcflags IGNCR = 0000200; -const Tcflags ICRNL = 0000400; -const Tcflags IUCLC = 0001000; -const Tcflags IXON = 0002000; -const Tcflags IXANY = 0004000; -const Tcflags IXOFF = 0010000; -const Tcflags IMAXBEL = 0020000; -const Tcflags IUTF8 = 0040000; - -extern fn CInt tcgetattr(Fd fd, Termios* self); -extern fn CInt tcsetattr(Fd fd, Tcactions optional_actions, Termios* self); -extern fn CInt tcsendbreak(Fd fd, CInt duration); -extern fn CInt tcdrain(Fd fd); -extern fn CInt tcflush(Fd fd, CInt queue_selector); -extern fn CInt tcflow(Fd fd, CInt action); -extern fn Speed cfgetospeed(Termios* self); -extern fn Speed cfgetispeed(Termios* self); -extern fn CInt cfsetospeed(Termios* self, Speed speed); -extern fn CInt cfsetispeed(Termios* self, Speed speed); - -const CInt NCCS = 32; -struct Termios { - Tcflags c_iflag; - Tcflags c_oflag; - Tcflags c_cflag; - Tcflags c_lflag; - Cc c_line; - Cc[NCCS] c_cc; - Speed c_ispeed; - Speed c_ospeed; -} - diff --git a/lib7/std/libc/os/win32.c3 b/lib7/std/libc/os/win32.c3 deleted file mode 100644 index baaa4659d..000000000 --- a/lib7/std/libc/os/win32.c3 +++ /dev/null @@ -1,59 +0,0 @@ -module libc @if(env::WIN32); -import std::os::win32; - -extern fn CFile __acrt_iob_func(CInt c); -extern fn CInt _close(Fd fd); def close = _close; -extern fn double _difftime64(Time_t time1, Time_t time2); def difftime = _difftime64; -extern fn CFile _fdopen(Fd fd, ZString mode); def fdopen = _fdopen; -extern fn CInt _fileno(CFile stream); def fileno = _fileno; -extern fn CInt _fseeki64(CFile, long, int); def fseek = _fseeki64; -extern fn CLong _ftelli64(CFile); def ftell = _ftelli64; -extern fn Errno _get_timezone(CLong *timezone); -extern fn Tm* _gmtime64_s(Tm* buf, Time_t *timer); -extern fn CInt _isatty(Fd fd); def isatty = _isatty; -extern fn Tm* _localtime64_s(Tm* buf, Time_t *timer); -extern fn Time_t _mkgmtime64(Tm* timeptr); def timegm = _mkgmtime64; -extern fn Time_t _mktime64(Tm *timeptr); def mktime = _mktime64; -extern fn usz _msize(void* ptr); -extern fn CInt _read(Fd fd, void* buffer, CUInt buffer_size); -extern fn CInt _setjmp(JmpBuf* buffer, void* frameptr); -extern fn CFile _wfopen(WString, WString); -extern fn CFile _wfreopen(WString, WString, CFile); -extern fn CInt _write(Fd fd, void* buffer, CUInt count); -extern fn CInt _wremove(WString); -extern fn int recv(Win32_SOCKET s, void* buf, int len, int flags); -extern fn int send(Win32_SOCKET s, void* buf, int len, int flags); - -const CInt SD_RECEIVE = 0; -const CInt SD_SEND = 1; -const CInt SD_BOTH = 2; -extern fn CInt shutdown(Win32_SOCKET s, CInt how); - -struct SystemInfo -{ - union { - uint dwOemId; - struct { - ushort wProcessorArchitecture; - ushort wReserved; - } - } - uint dwPageSize; - void* lpMinimumApplicationAddress; - void* lpMaximumApplicationAddress; - usz dwActiveProcessorMask; - uint dwNumberOfProcessors; - uint dwProcessorType; - uint dwAllocationGranularity; - ushort wProcessorLevel; - ushort wProcessorRevision; -} - -extern fn CInt get_system_info(SystemInfo*) @extern("GetSystemInfo"); - -// Aliases to simplify libc use -macro Tm* localtime_r(Time_t* timer, Tm* buf) => _localtime64_s(buf, timer); -macro CInt setjmp(JmpBuf* buffer) => _setjmp(buffer, null); -macro Tm* gmtime_r(Time_t* timer, Tm* buf) => _gmtime64_s(buf, timer); -macro isz read(Fd fd, void* buffer, usz buffer_size) => _read(fd, buffer, (CUInt)buffer_size); -macro isz write(Fd fd, void* buffer, usz count) => _write(fd, buffer, (CUInt)count); diff --git a/lib7/std/libc/termios.c3 b/lib7/std/libc/termios.c3 deleted file mode 100644 index de33e4875..000000000 --- a/lib7/std/libc/termios.c3 +++ /dev/null @@ -1,122 +0,0 @@ -module libc::termios @if(env::LIBC &&& env::POSIX); - -fn int sendBreak(Fd fd, int duration) => tcsendbreak(fd, duration); -fn int drain(Fd fd) => tcdrain(fd); -fn int flush(Fd fd, int queue_selector) => tcflush(fd, queue_selector); -fn int flow(Fd fd, int action) => tcflow(fd, action); -fn Speed Termios.getOSpeed(Termios* self) => cfgetospeed(self); -fn Speed Termios.getISpeed(Termios* self) => cfgetispeed(self); -fn int Termios.setOSpeed(Termios* self, Speed speed) => cfsetospeed(self, speed); -fn int Termios.setISpeed(Termios* self, Speed speed) => cfsetispeed(self, speed); -fn int Termios.getAttr(Termios* self, Fd fd) => tcgetattr(fd, self); -fn int Termios.setAttr(Termios* self, Fd fd, Tcactions optional_actions) => tcsetattr(fd, optional_actions, self); - -module libc::termios @if(!env::LIBC ||| !env::POSIX); - -distinct Cc = char; -distinct Speed = CUInt; -distinct Tcflags = CUInt; -struct Termios { - void* dummy; -} - -fn CInt tcgetattr(Fd fd, Termios* self) -{ - unreachable("tcgetattr unavailable"); -} - -fn CInt tcsetattr(Fd fd, CInt optional_actions, Termios* self) -{ - unreachable("tcsetattr unavailable"); -} - -fn CInt tcsendbreak(Fd fd, CInt duration) -{ - unreachable("tcsendbreak unavailable"); -} - -fn CInt tcdrain(Fd fd) -{ - unreachable("tcdrain unavailable"); -} - -fn CInt tcflush(Fd fd, CInt queue_selector) -{ - unreachable("tcflush unavailable"); -} - -fn CInt tcflow(Fd fd, CInt action) -{ - unreachable("tcflow unavailable"); -} - -fn Speed cfgetospeed(Termios* self) -{ - unreachable("cfgetospeed unavailable"); -} - -fn Speed cfgetispeed(Termios* self) -{ - unreachable("cfgetispeed unavailable"); -} - -fn CInt cfsetospeed(Termios* self, Speed speed) -{ - unreachable("cfsetospeed unavailable"); -} - -fn CInt cfsetispeed(Termios* self, Speed speed) -{ - unreachable("cfsetispeed unavailable"); -} - -fn int sendBreak(Fd fd, int duration) -{ - unreachable("sendBreak unavailable"); -} - -fn int drain(Fd fd) -{ - unreachable("drain unavailable"); -} - -fn int flush(Fd fd, int queue_selector) -{ - unreachable("flush unavailable"); -} - -fn int flow(Fd fd, int action) -{ - unreachable("flow unavailable"); -} - -fn Speed Termios.getOSpeed(Termios* self) -{ - unreachable("Termios.getOSpeed unavailable"); -} - -fn Speed Termios.getISpeed(Termios* self) -{ - unreachable("Termios.getISpeed unavailable"); -} - -fn int Termios.setOSpeed(Termios* self, Speed speed) -{ - unreachable("Termios.setOSpeed unavailable"); -} - -fn int Termios.setISpeed(Termios* self, Speed speed) -{ - unreachable("Termios.setISpeed unavailable"); -} - -fn int Termios.getAttr(Termios* self, Fd fd) -{ - unreachable("Termios.getAttr unavailable"); -} - -fn int Termios.setAttr(Termios* self, Fd fd, int optional_actions) -{ - unreachable("Termios.setAttr unavailable"); -} - diff --git a/lib7/std/math/bigint.c3 b/lib7/std/math/bigint.c3 deleted file mode 100644 index ba3267a86..000000000 --- a/lib7/std/math/bigint.c3 +++ /dev/null @@ -1,1121 +0,0 @@ -<* -Untested new big int implementation, missing unit tests. Etc -*> -module std::math::bigint; -import std::math::random; -import std::io; - -const MAX_LEN = 4096 * 2 / 32; - -const BigInt ZERO = { .len = 1 }; -const BigInt ONE = { .len = 1, .data[0] = 1 }; - -struct BigInt (Printable) -{ - uint[MAX_LEN] data; - uint len; -} - -fn BigInt from_int(int128 val) -{ - BigInt b @noinit; - b.init(val); - return b; -} - -fn BigInt* BigInt.init(&self, int128 value) -{ - self.data[..] = 0; - int128 tmp = value; - uint len = 0; - while (tmp != 0 && len < MAX_LEN) - { - self.data[len] = (uint)(tmp & 0xFFFFFFFF); - tmp >>= 32; - len++; - } - assert(value < 0 || tmp == 0 || !self.is_negative()); - assert(value >= 0 || tmp == -1 || self.is_negative()); - self.len = len; - self.reduce_len(); - return self; -} - -fn BigInt* BigInt.init_with_u128(&self, uint128 value) -{ - self.data[..] = 0; - uint128 tmp = value; - uint len = 0; - while (tmp != 0) - { - self.data[len] = (uint)(tmp & 0xFFFFFFFF); - tmp >>= 32; - len++; - } - assert(!self.is_negative()); - self.len = max(len, 1); - return self; -} - -<* - @require values.len <= MAX_LEN -*> -fn BigInt* BigInt.init_with_array(&self, uint[] values) -{ - self.data[..] = 0; - - if (values.len == 0) - { - self.len = 1; - return self; - } - - self.len = values.len; - - foreach_r(i, val : values) - { - self.data[values.len - 1 - i] = val; - } - while (self.len > 1 && self.data[self.len - 1] == 0) - { - self.len--; - } - return self; -} - -fn BigInt*! BigInt.init_string_radix(&self, String value, int radix) -{ - isz len = value.len; - isz limit = value[0] == '-' ? 1 : 0; - *self = ZERO; - BigInt multiplier = ONE; - BigInt radix_big = from_int(radix); - for (isz i = len - 1; i >= limit; i--) - { - int pos_val = value[i]; - switch (pos_val) - { - case '0'..'9': - pos_val -= '0'; - case 'A'..'Z': - pos_val -= 'A' - 10; - case 'a'..'z': - pos_val -= 'a' - 10; - default: - return NumberConversion.MALFORMED_INTEGER?; - } - if (pos_val >= radix) return NumberConversion.MALFORMED_INTEGER?; - if (limit == 1) pos_val = -pos_val; - self.add_this(multiplier.mult(from_int(pos_val))); - if (i - 1 >= limit) - { - multiplier.mult_this(radix_big); - } - } - switch - { - case limit && !self.is_negative(): - return NumberConversion.INTEGER_OVERFLOW?; - case !limit && self.is_negative(): - return NumberConversion.INTEGER_OVERFLOW?; - } - return self; -} - -fn bool BigInt.is_negative(&self) -{ - return self.data[MAX_LEN - 1] & 0x80000000 != 0; -} - -fn BigInt BigInt.add(self, BigInt other) -{ - self.add_this(other); - return self; -} - -fn void BigInt.add_this(&self, BigInt other) -{ - bool sign = self.is_negative(); - bool sign_arg = other.is_negative(); - - self.len = max(self.len, other.len); - - ulong carry = 0; - for (uint i = 0; i < self.len; i++) - { - ulong sum = (ulong)self.data[i] + (ulong)other.data[i] + carry; - carry = sum >> 32; - self.data[i] = (uint)(sum & 0xFFFFFFFF); - } - - if (carry != 0 && self.len < MAX_LEN) - { - self.data[self.len++] = (uint)carry; - } - - self.reduce_len(); - - assert(sign != sign_arg || sign == self.is_negative(), "Overflow in addition"); -} - -fn void BigInt.reduce_len(&self) @private -{ - self.len = max(find_length(&self.data, self.len), 1); -} - -macro uint find_length(uint* data, uint length) -{ - while (length > 1 && data[length - 1] == 0) - { - --length; - } - return length; -} - -fn BigInt BigInt.mult(self, BigInt bi2) -{ - self.mult_this(bi2); - return self; -} - -fn void BigInt.mult_this(&self, BigInt bi2) -{ - if (bi2.is_zero()) - { - *self = ZERO; - return; - } - if (bi2.is_one()) return; - - BigInt res = ZERO; - - bool negative_sign = false; - - if (self.is_negative()) - { - self.negate(); - negative_sign = !negative_sign; - } - if (bi2.is_negative()) - { - bi2.negate(); - negative_sign = !negative_sign; - } - - // multiply the absolute values - for (uint i = 0; i < self.len; i++) - { - if (self.data[i] == 0) continue; - ulong mcarry = 0; - for (int j = 0, int k = i; j < bi2.len; j++, k++) - { - // k = i + j - ulong bi1_val = (ulong)self.data[i]; - ulong bi2_val = (ulong)bi2.data[j]; - ulong res_val = (ulong)res.data[k]; - ulong val = (bi1_val * bi2_val) + res_val + mcarry; - res.data[k] = (uint)(val & 0xFFFFFFFF); - mcarry = val >> 32; - } - - if (mcarry != 0) - { - res.data[i + bi2.len] = (uint)mcarry; - } - } - - res.len = min(self.len + bi2.len, (uint)MAX_LEN); - - res.reduce_len(); - - // overflow check (result is -ve) - assert(!res.is_negative(), "Multiplication overflow"); - - if (negative_sign) - { - res.negate(); - } - *self = res; -} - -fn void BigInt.negate(&self) -{ - if (self.is_zero()) return; - - bool was_negative = self.is_negative(); - // 1's complement - for (uint i = 0; i < MAX_LEN; i++) - { - self.data[i] = (uint)~(self.data[i]); - } - // add one to result of 1's complement - ulong carry = 1; - int index = 0; - - while (carry != 0 && index < MAX_LEN) - { - ulong val = self.data[index]; - val++; - - self.data[index] = (uint)(val & 0xFFFFFFFF); - carry = val >> 32; - index++; - } - - assert(self.is_negative() != was_negative, "Overflow in negation"); - - self.len = MAX_LEN; - self.reduce_len(); -} - -macro bool BigInt.is_zero(&self) => self.len == 1 && self.data[0] == 0; - -fn BigInt BigInt.sub(self, BigInt other) -{ - self.sub_this(other); - return self; -} - -fn BigInt* BigInt.sub_this(&self, BigInt other) -{ - self.len = max(self.len, other.len); - - bool sign = self.is_negative(); - bool sign_arg = other.is_negative(); - - long carry_in = 0; - for (int i = 0; i < self.len; i++) - { - long diff = (long)self.data[i] - (long)other.data[i] - carry_in; - self.data[i] = (uint)(diff & 0xFFFFFFFF); - carry_in = diff < 0 ? 1 : 0; - } - - // roll over to negative - if (carry_in != 0) - { - for (uint i = self.len; i < MAX_LEN; i++) - { - self.data[i] = 0xFFFFFFFF; - } - self.len = MAX_LEN; - } - - self.reduce_len(); - - // overflow check - - assert(sign == sign_arg || sign == self.is_negative(), "Overflow in subtraction"); - return self; -} - -fn int BigInt.bitcount(&self) -{ - self.reduce_len(); - uint val = self.data[self.len - 1]; - uint mask = 0x80000000; - int bits = 32; - - while (bits > 0 && (val & mask) == 0) - { - bits--; - mask >>= 1; - } - bits += (self.len - 1) << 5; - return bits; -} - -fn BigInt BigInt.unary_minus(&self) -{ - if (self.is_zero()) return *self; - BigInt result = *self; - result.negate(); - return result; -} - - -macro BigInt BigInt.div(self, BigInt other) -{ - self.div_this(other); - return self; -} - -fn void BigInt.div_this(&self, BigInt other) -{ - bool negate_answer = self.is_negative(); - - if (negate_answer) - { - self.negate(); - } - if (other.is_negative()) - { - negate_answer = !negate_answer; - other.negate(); - } - - if (self.less_than(other)) - { - *self = ZERO; - } - - BigInt quotient = ZERO; - BigInt remainder = ZERO; - - if (other.len == 1) - { - self.single_byte_divide(&other, "ient, &remainder); - } - else - { - self.multi_byte_divide(&other, "ient, &remainder); - } - if (negate_answer) - { - quotient.negate(); - } - *self = quotient; -} - -fn BigInt BigInt.mod(self, BigInt bi2) -{ - self.mod_this(bi2); - return self; -} - -fn void BigInt.mod_this(&self, BigInt bi2) -{ - if (bi2.is_negative()) - { - bi2.negate(); - } - - bool negate_answer = self.is_negative(); - if (negate_answer) - { - self.negate(); - } - - if (self.less_than(bi2)) - { - if (negate_answer) self.negate(); - return; - } - - BigInt quotient = ZERO; - BigInt remainder = ZERO; - - if (bi2.len == 1) - { - self.single_byte_divide(&bi2, "ient, &remainder); - } - else - { - self.multi_byte_divide(&bi2, "ient, &remainder); - } - if (negate_answer) - { - remainder.negate(); - } - *self = remainder; -} - -fn void BigInt.bit_negate_this(&self) -{ - foreach (&r : self.data) *r = ~*r; - - self.len = MAX_LEN; - self.reduce_len(); -} - -fn BigInt BigInt.bit_negate(self) -{ - self.bit_negate_this(); - return self; -} - -fn BigInt BigInt.shr(self, int shift) -{ - self.shr_this(shift); - return self; -} - -fn void BigInt.shr_this(self, int shift) -{ - self.len = shift_right(&self.data, self.len, shift); -} - -fn BigInt BigInt.shl(self, int shift) -{ - self.shl_this(shift); - return self; -} - -macro bool BigInt.equals(&self, BigInt other) -{ - if (self.len != other.len) return false; - return self.data[:self.len] == other.data[:self.len]; -} - -macro bool BigInt.greater_than(&self, BigInt other) -{ - if (self.is_negative() && !other.is_negative()) return false; - if (!self.is_negative() && other.is_negative()) return true; - int pos; - // same sign - int len = max(self.len, other.len); - for (pos = len - 1; pos >= 0 && self.data[pos] == other.data[pos]; pos--); - return pos >= 0 && self.data[pos] > other.data[pos]; -} -macro bool BigInt.less_than(&self, BigInt other) -{ - if (self.is_negative() && !other.is_negative()) return true; - if (!self.is_negative() && other.is_negative()) return false; - - // same sign - int len = max(self.len, other.len); - int pos; - for (pos = len - 1; pos >= 0 && self.data[pos] == other.data[pos]; pos--); - return pos >= 0 && self.data[pos] < other.data[pos]; -} - -fn bool BigInt.is_odd(&self) -{ - return (self.data[0] & 0x1) != 0; -} - - -fn bool BigInt.is_one(&self) -{ - return self.len == 1 && self.data[0] == 1; -} - - -macro bool BigInt.greater_or_equal(&self, BigInt other) -{ - return self.greater_than(other) || self.equals(other); -} - -macro bool BigInt.less_or_equal(&self, BigInt) -{ - return self.less_than(other) || self.equals(other); -} - -fn BigInt BigInt.abs(&self) -{ - return self.is_negative() ? self.unary_minus() : *self; -} - -fn usz! BigInt.to_format(&self, Formatter* format) @dynamic -{ - @stack_mem(4100; Allocator mem) - { - return format.print(self.to_string_with_radix(10, mem)); - }; -} - -fn String BigInt.to_string(&self, Allocator allocator) @dynamic -{ - return self.to_string_with_radix(10, allocator); -} - -<* - @require radix > 1 && radix <= 36 "Radix must be 2-36" -*> -fn String BigInt.to_string_with_radix(&self, int radix, Allocator allocator) -{ - if (self.is_zero()) return "0".copy(allocator); - - const char[?] CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - @stack_mem(4100; Allocator mem) - { - BigInt a = *self; - DString str; - str.init(mem, 4096); - bool negative = self.is_negative(); - if (negative) - { - a.negate(); - } - BigInt quotient = ZERO; - BigInt remainder = ZERO; - BigInt big_radix = from_int(radix); - - while (!a.is_zero()) - { - a.single_byte_divide(&big_radix, "ient, &remainder); - - if (remainder.data[0] < 10) - { - str.append((char)(remainder.data[0] + '0')); - } - else - { - str.append(CHARS[remainder.data[0] - 10]); - } - a = quotient; - } - if (negative) str.append("-"); - str.reverse(); - return str.copy_str(allocator); - }; -} - -<* - @require !exp.is_negative() "Positive exponents only" -*> -fn BigInt BigInt.mod_pow(&self, BigInt exp, BigInt mod) -{ - BigInt result_num = ONE; - - bool was_neg = self.is_negative(); - BigInt num = *self; - if (was_neg) - { - num.negate(); - } - - if (mod.is_negative()) - { - mod.negate(); - } - - num.mod_this(mod); - - // calculate constant = b^(2k) / m - BigInt constant = ZERO; - - uint i = mod.len << 1; - constant.data[i] = 0x00000001; - constant.len = i + 1; - - constant.div_this(mod); - int total_bits = exp.bitcount(); - int count = 0; - - // perform squaring and multiply exponentiation - for (int pos = 0; pos < exp.len; pos++) - { - uint mask = 0x01; - for (int index = 0; index < 32; index++) - { - if (exp.data[pos] & mask != 0) - { - result_num = barrett_reduction(result_num.mult(num), mod, constant); - } - - mask <<= 1; - - num = barrett_reduction(num.mult(num), mod, constant); - - if (num.len == 1 && num.data[0] == 1) - { - if (was_neg && (exp.data[0] & 0x1) != 0) - { - //odd exp - result_num.negate(); - } - return result_num; - } - count++; - if (count == total_bits) break; - } - } - - if (was_neg && exp.is_odd()) - { - //odd exp - result_num.negate(); - } - return result_num; -} - -<* - Fast calculation of modular reduction using Barrett's reduction. - Requires x < b^(2k), where b is the base. In this case, base is - 2^32 (uint). -*> -fn BigInt barrett_reduction(BigInt x, BigInt n, BigInt constant) -{ - int k = n.len; - int k_plus_one = k + 1; - int k_minus_one = k - 1; - - BigInt q1 = ZERO; - // q1 = x / b^(k-1) - q1.len = x.len - k_minus_one; - if (q1.len == 0) - { - q1.len = 1; - } - else - { - q1.data[:q1.len] = x.data[k_minus_one:q1.len]; - } - - BigInt q2 = q1.mult(constant); - BigInt q3 = ZERO; - - // q3 = q2 / b^(k+1) - int length = q2.len - k_plus_one; - assert(length >= 0); - if (length) - { - q3.data[:length] = q2.data[k_plus_one:length]; - q3.len = length; - } - else - { - q3.len = 1; - } - - // r1 = x mod b^(k+1) - // i.e. keep the lowest (k+1) words - BigInt r1 = ZERO; - int length_to_copy = (x.len > k_plus_one) ? k_plus_one : x.len; - assert(length_to_copy >= 0); - r1.data[:length_to_copy] = x.data[0:length_to_copy]; - r1.len = length_to_copy; - - // r2 = (q3 * n) mod b^(k+1) - // partial multiplication of q3 and n - - BigInt r2 = ZERO; - for (int i = 0; i < q3.len; i++) - { - if (q3.data[i] == 0) continue; - - ulong mcarry = 0; - int t = i; - for (int j = 0; j < n.len && t < k_plus_one; j++, t++) - { - // t = i + j - ulong val = (ulong)q3.data[i] * n.data[j] + r2.data[t] + mcarry; - - r2.data[t] = (uint)(val & 0xFFFFFFFF); - mcarry = val >> 32; - } - - if (t < k_plus_one) - { - r2.data[t] = (uint)mcarry; - } - } - - r2.len = k_plus_one; - r2.reduce_len(); - - r1.sub_this(r2); - if (r1.is_negative()) - { - BigInt val = ZERO; - val.data[k_plus_one] = 0x00000001; - val.len = k_plus_one + 1; - r1.add_this(val); - } - - while (r1.greater_or_equal(n)) - { - r1.sub_this(n); - } - - return r1; -} - -fn BigInt BigInt.sqrt(&self) -{ - uint num_bits = self.bitcount(); - - num_bits = num_bits & 0x1 != 0 ? num_bits >> 1 + 1 : num_bits >> 1; - - uint byte_pos = num_bits >> 5; - int bit_pos = num_bits & 0x1F; - - uint mask; - BigInt result = ZERO; - - if (bit_pos == 0) - { - mask = 0x80000000; - } - else - { - mask = 1U << bit_pos; - byte_pos++; - } - result.len = byte_pos; - - for (int i = byte_pos - 1; i >= 0; i--) - { - while (mask != 0) - { - // guess - result.data[i] ^= mask; - - // undo the guess if its square is larger than this - if (result.mult(result).greater_than(*self)) - { - result.data[i] ^= mask; - } - mask >>= 1; - } - mask = 0x80000000; - } - return result; -} - -fn BigInt BigInt.bit_and(self, BigInt bi2) -{ - self.bit_and_this(bi2); - return self; -} - -fn void BigInt.bit_and_this(&self, BigInt bi2) -{ - uint len = max(self.len, bi2.len); - foreach (i, &ref : self.data[:len]) - { - *ref = *ref & bi2.data[i]; - } - self.len = MAX_LEN; - - self.reduce_len(); -} - -fn BigInt BigInt.bit_or(self, BigInt bi2) -{ - self.bit_or_this(bi2); - return self; -} - -fn void BigInt.bit_or_this(&self, BigInt bi2) -{ - uint len = max(self.len, bi2.len); - foreach (i, &ref : self.data[:len]) - { - *ref = *ref | bi2.data[i]; - } - self.len = MAX_LEN; - - self.reduce_len(); -} - -fn BigInt BigInt.bit_xor(self, BigInt bi2) -{ - self.bit_xor_this(bi2); - return self; -} - -fn void BigInt.bit_xor_this(&self, BigInt bi2) -{ - uint len = max(self.len, bi2.len); - foreach (i, &ref : self.data[:len]) - { - *ref = *ref ^ bi2.data[i]; - } - self.len = MAX_LEN; - - self.reduce_len(); -} - -fn void BigInt.shl_this(&self, int shift) -{ - self.len = shift_left(&self.data, self.len, shift); -} - -fn BigInt BigInt.gcd(&self, BigInt other) -{ - BigInt x = self.abs(); - BigInt y = other.abs(); - BigInt g = y; - while (!x.is_zero()) - { - g = x; - x = y.mod(x); - y = g; - } - return g; -} - -fn BigInt BigInt.lcm(&self, BigInt other) -{ - BigInt x = self.abs(); - BigInt y = other.abs(); - BigInt g = y.mult(x); - return g.div(x.gcd(y)); -} - -<* - @require bits >> 5 < MAX_LEN "Required bits > maxlength" -*> -fn void BigInt.randomize_bits(&self, Random random, int bits) -{ - int dwords = bits >> 5; - int rem_bits = bits & 0x1F; - - if (rem_bits != 0) - { - dwords++; - } - - for (int i = 0; i < dwords; i++) - { - self.data[i] = random.next_int(); - } - - for (int i = dwords; i < MAX_LEN; i++) - { - self.data[i] = 0; - } - - if (rem_bits != 0) - { - uint mask = (uint)(0x01 << (rem_bits - 1)); - self.data[dwords - 1] |= mask; - - mask = (uint)(0xFFFFFFFF >> (32 - rem_bits)); - self.data[dwords - 1] &= mask; - } - else - { - self.data[dwords - 1] |= 0x80000000; - } - - self.len = (int)dwords; - - if (self.len == 0) - { - self.len = 1; - } -} - -module std::math::bigint @private; - - -<* - @param [&out] quotient - @param [&in] bi2 - @param [&out] remainder -*> -fn void BigInt.single_byte_divide(&self, BigInt* bi2, BigInt* quotient, BigInt* remainder) -{ - uint[MAX_LEN] result; - int result_pos = 0; - - // copy dividend to reminder - *remainder = *self; - - while (remainder.len > 1 && remainder.data[remainder.len - 1] == 0) - { - remainder.len--; - } - - ulong divisor = bi2.data[0]; - int pos = remainder.len - 1; - ulong dividend = remainder.data[pos]; - - if (dividend >= divisor) - { - ulong q = dividend / divisor; - result[result_pos++] = (uint)q; - remainder.data[pos] = (uint)(dividend % divisor); - } - pos--; - - while (pos >= 0) - { - dividend = (ulong)remainder.data[pos + 1] << 32 + remainder.data[pos]; - ulong q = dividend / divisor; - result[result_pos++] = (uint)q; - - remainder.data[pos + 1] = 0; - remainder.data[pos--] = (uint)(dividend % divisor); - } - - quotient.len = result_pos; - uint j = 0; - for (int i = result_pos - 1; i >= 0; i--, j++) - { - quotient.data[j] = result[i]; - } - - quotient.data[j..] = 0; - quotient.reduce_len(); - remainder.reduce_len(); -} - -<* - @param [&out] quotient - @param [&in] other - @param [&out] remainder -*> -fn void BigInt.multi_byte_divide(&self, BigInt* other, BigInt* quotient, BigInt* remainder) -{ - uint[MAX_LEN] result; - uint[MAX_LEN] r; - uint[MAX_LEN] dividend_part; - - int remainder_len = self.len + 1; - - uint mask = 0x80000000; - uint val = other.data[other.len - 1]; - int shift = 0; - int result_pos = 0; - - while (mask != 0 && (val & mask) == 0) - { - shift++; - mask >>= 1; - } - - r[:self.len] = self.data[:self.len]; - - remainder_len = shift_left(&r, remainder_len, shift); - - BigInt bi2 = other.shl(shift); - - int j = remainder_len - bi2.len; - int pos = remainder_len - 1; - - ulong first_divisor_byte = bi2.data[bi2.len - 1]; - ulong second_divisor_byte = bi2.data[bi2.len - 2]; - - int divisor_len = bi2.len + 1; - - while (j > 0) - { - ulong dividend = (ulong)r[pos] << 32 + r[pos - 1]; - - ulong q_hat = dividend / first_divisor_byte; - ulong r_hat = dividend % first_divisor_byte; - - bool done = false; - while (!done) - { - done = true; - - if (q_hat == 0x10000000 || (q_hat * second_divisor_byte) > (r_hat << 32 + r[pos - 2])) - { - q_hat--; - r_hat += first_divisor_byte; - - if (r_hat < 0x10000000) done = false; - } - } - - for (int h = 0; h < divisor_len; h++) - { - dividend_part[h] = r[pos - h]; - } - - BigInt kk = { dividend_part, divisor_len }; - BigInt ss = bi2.mult(from_int(q_hat)); - - while (ss.greater_than(kk)) - { - q_hat--; - - ss.sub_this(bi2); - } - BigInt yy = kk.sub(ss); - - for (int h = 0; h < divisor_len; h++) - { - r[pos - h] = yy.data[bi2.len - h]; - } - - result[result_pos++] = (uint)q_hat; - - pos--; - j--; - - } - - quotient.len = result_pos; - uint y = 0; - - for (int x = quotient.len - 1; x >= 0; x--, y++) - { - quotient.data[y] = result[x]; - } - - for (; y < MAX_LEN; y++) - { - quotient.data[y] = 0; - } - - quotient.reduce_len(); - - remainder.len = shift_right(&r, remainder_len, shift); - - remainder.data[:remainder.len] = r[:remainder.len]; - - remainder.data[y..] = 0; -} - -fn int shift_left(uint* data, int len, int shift_val) @inline -{ - int shift_amount = 32; - int buf_len = len; - - while (buf_len > 1 && data[buf_len - 1] == 0) buf_len--; - - for (int count = shift_val; count > 0;) - { - if (count < shift_amount) shift_amount = count; - - ulong carry = 0; - for (int i = 0; i < buf_len; i++) - { - ulong val = (ulong)data[i] << shift_amount; - val |= carry; - - data[i] = (uint)(val & 0xFFFFFFFF); - carry = val >> 32; - } - - if (carry != 0) - { - if (buf_len + 1 <= len) - { - data[buf_len++] = (uint)carry; - } - } - count -= shift_amount; - } - return buf_len; -} - -fn int shift_right(uint* data, int len, int shift_val) @inline -{ - int shift_amount = 32; - int inv_shift = 0; - int buf_len = len; - - while (buf_len > 1 && data[buf_len - 1] == 0) - { - buf_len--; - } - - for (int count = shift_val; count > 0;) - { - if (count < shift_amount) - { - shift_amount = count; - inv_shift = 32 - shift_amount; - } - - ulong carry = 0; - for (int i = buf_len - 1; i >= 0; i--) - { - ulong val = (ulong)data[i] >> shift_amount; - val |= carry; - - carry = (ulong)data[i] << inv_shift; - data[i] = (uint)(val); - } - - count -= shift_amount; - } - return find_length(data, buf_len); -} diff --git a/lib7/std/math/math.c3 b/lib7/std/math/math.c3 deleted file mode 100644 index a07a04854..000000000 --- a/lib7/std/math/math.c3 +++ /dev/null @@ -1,1281 +0,0 @@ -// Copyright (c) 2021 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::math; -import std::math::complex; -import std::math::matrix; -import std::math::quaternion; - -// TODO Define these using quad precision. -const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466; -const LOG2E = 1.44269504088896340735992468100189214; // log2(e) -const LOG10E = 0.434294481903251827651128918916605082; // log10(e) -const LN2 = 0.693147180559945309417232121458176568; // ln(2) -const LN10 = 2.30258509299404568401799145468436421; // ln(10) -const PI = 3.14159265358979323846264338327950288419716939937510; // pi -const PI_2 = 1.57079632679489661923132169163975144; // pi / 2 -const PI_4 = 0.785398163397448309615660845819875721; // pi / 4 -const DIV_PI = 0.318309886183790671537767526745028724; // 1 / pi -const DIV_2_PI = 0.636619772367581343075535053490057448; // 2 / pi -const DIV_2_SQRTPI = 1.12837916709551257389615890312154517; // 2/sqrt(pi) -const SQRT2 = 1.41421356237309504880168872420969808; // sqrt(2) -const double DIV_1_SQRT2 = 0.707106781186547524400844362104849039; // 1 / sqrt(2) - -const HALF_MAX = 6.5504e+4; -const HALF_MIN = 6.103515625e-5; -const HALF_DENORM_MIN = 5.9604644775390625e-8; -const HALF_DIG = 3; -const HALF_DEC_DIGITS = 5; -const HALF_MANT_DIG = 11; -const HALF_MAX_10_EXP = 4; -const HALF_MIN_10_EXP = -4; -const HALF_MAX_EXP = 16; -const HALF_MIN_EXP = -13; -const HALF_EPSILON = 9.765625e-4; - -const FLOAT_MAX = 0x1.fffffep+127; -const FLOAT_MIN = 1.17549435e-38; -const FLOAT_DENORM_MIN = 1.40129846432481707092e-45; -const FLOAT_DIG = 6; -const FLOAT_DEC_DIGITS = 9; -const FLOAT_MANT_DIG = 24; -const FLOAT_MAX_10_EXP = 38; -const FLOAT_MIN_10_EXP = -37; -const FLOAT_MAX_EXP = 128; -const FLOAT_MIN_EXP = -125; -const FLOAT_EPSILON = 1.1920928955078125e-07; - -const DOUBLE_MAX = 1.79769313486231570815e+308; -const DOUBLE_MIN = 2.2250738585072014e-308; -const DOUBLE_DENORM_MIN = 4.94065645841246544177e-324; -const DOUBLE_DIG = 15; -const DOUBLE_DEC_DIGITS = 17; -const DOUBLE_MANT_DIG = 53; -const DOUBLE_MAX_10_EXP = 308; -const DOUBLE_MIN_10_EXP = -307; -const DOUBLE_MAX_EXP = 1024; -const DOUBLE_MIN_EXP = -1021; -const DOUBLE_EPSILON = 2.22044604925031308085e-16; - -const QUAD_MANT_DIG = 113; - -/* -const QUAD_MAX = 1.18973149535723176508575932662800702e+4932; -const QUAD_MIN = 3.36210314311209350626267781732175260e-4932; -const QUAD_DENORM_MIN = 6.47517511943802511092443895822764655e-4966; -const QUAD_DIG = 33; -const QUAD_DEC_DIGITS = 36; -const QUAD_MAX_10_EXP = 4932; -const QUAD_MIN_10_EXP = -4931; -const QUAD_MAX_EXP = 16384; -const QUAD_MIN_EXP = -16481; -const QUAD_EPSILON = 1.92592994438723585305597794258492732e-34; -*/ - -enum RoundingMode : int -{ - TOWARD_ZERO, - TO_NEAREST, - TOWARD_INFINITY, - TOWARD_NEG_INFINITY -} - -fault MathError -{ - OVERFLOW, -} - -fault MatrixError -{ - MATRIX_INVERSE_DOESNT_EXIST, -} - -def Complexf = Complex{float}; -def Complex = Complex{double}; -def COMPLEX_IDENTITY = complex::IDENTITY{double} @builtin; -def COMPLEXF_IDENTITY = complex::IDENTITY{float} @builtin; - -def Quaternionf = Quaternion{float}; -def Quaternion = Quaternion{double}; -def QUATERNION_IDENTITY = quaternion::IDENTITY{double} @builtin; -def QUATERNIONF_IDENTITY = quaternion::IDENTITY{float} @builtin; - -def Matrix2f = Matrix2x2{float}; -def Matrix2 = Matrix2x2{double}; -def Matrix3f = Matrix3x3{float}; -def Matrix3 = Matrix3x3{double}; -def Matrix4f = Matrix4x4{float}; -def Matrix4 = Matrix4x4{double}; -def matrix4_ortho = matrix::ortho{double} @builtin; -def matrix4_perspective = matrix::perspective{double} @builtin; -def matrix4f_ortho = matrix::ortho{float} @builtin; -def matrix4f_perspective = matrix::perspective{float} @builtin; - -def MATRIX2_IDENTITY = matrix::IDENTITY2{double} @builtin; -def MATRIX2F_IDENTITY = matrix::IDENTITY2{float} @builtin; -def MATRIX3_IDENTITY = matrix::IDENTITY3{double} @builtin; -def MATRIX3F_IDENTITY = matrix::IDENTITY3{float} @builtin; -def MATRIX4_IDENTITY = matrix::IDENTITY4{double} @builtin; -def MATRIX4F_IDENTITY = matrix::IDENTITY4{float} @builtin; - - -<* - @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector` -*> -macro deg_to_rad(x) { - return x * PI / 180; -} - -<* - @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector` -*> -macro abs(x) => $$abs(x); - -<* - @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" - @require values::@is_int(y) || values::@is_float(y) "Expected an integer or floating point value" -*> -macro is_approx(x, y, eps) -{ - if (x == y) return true; - if (is_nan(x) || is_nan(y)) return false; - return abs(x-y) <= eps; -} - -<* - @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" - @require values::@is_int(y) || values::@is_float(y) "Expected an integer or floating point value" -*> -macro is_approx_rel(x, y, eps) -{ - if (x == y) return true; - if (is_nan(x) || is_nan(y)) return false; - return abs(x-y) <= eps * max(abs(x), abs(y)); -} - -<* - @require values::@is_int(x) `The input must be an integer` -*> -macro sign(x) -{ - var $Type = $typeof(x); - $if $Type.kindof == TypeKind.UNSIGNED_INT: - return ($Type)(x > 0); - $else - return ($Type)(x > 0) - ($Type)(x < 0); - $endif -} - -<* - @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" - @require values::@is_int(y) || values::@is_float(y) "Expected an integer or floating point value" -*> -macro atan2(x, y) -{ - $if @typeis(x, float) && @typeis(y, float): - return _atan2f(x, y); - $else - return _atan2(x, y); - $endif -} - -<* - @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" - @require @typekind(sinp) == POINTER "Expected sinp to be a pointer" - @require values::@is_same_type(sinp, cosp) "Expected sinp and cosp to have the same type" - @require $assignable(x, $typeof(*sinp)) "Expected x and sinp/cosp to have the same type" -*> -macro sincos_ref(x, sinp, cosp) -{ - $if @typeid(*sinp) == float.typeid: - return _sincosf(x, sinp, cosp); - $else - return _sincos(x, sinp, cosp); - $endif -} - -<* - Return a vector with sin / cos of the given angle. - - @param x `the angle in radians` - @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" -*> -macro sincos(x) -{ - $if @typeid(x) == float.typeid: - float[<2>] v @noinit; - _sincosf(x, &v[0], &v[1]); - $else - double[<2>] v @noinit; - _sincos(x, &v[0], &v[1]); - $endif - return v; -} - -<* - @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" -*> -macro atan(x) -{ - $if @typeid(x) == float.typeid: - return _atanf(x); - $else - return _atan(x); - $endif -} - -<* - @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" -*> -macro atanh(x) -{ - $if @typeid(x) == float.typeid: - return _atanhf(x); - $else - return _atanh(x); - $endif -} - -<* - @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" -*> -macro acos(x) -{ - $if @typeid(x) == float.typeid: - return _acosf(x); - $else - return _acos(x); - $endif -} - -<* - @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" -*> -macro acosh(x) -{ - $if @typeid(x) == float.typeid: - return _acoshf(x); - $else - return _acosh(x); - $endif -} - -<* - @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" -*> -macro asin(x) -{ - $if @typeid(x) == float.typeid: - return _asinf(x); - $else - return _asin(x); - $endif -} - -<* - @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value" -*> -macro asinh(x) -{ - $if @typeid(x) == float.typeid: - return _asinhf(x); - $else - return _asinh(x); - $endif -} - -<* - @require values::@is_floatlike(x) `The input must be a floating point value or float vector` -*> -macro ceil(x) => $$ceil(x); - -<* - Constrain the value to lie within the given interval. - - @param x "the value to clamp, may be a number or a numerical vector." - @param lower "the lower bounds" - @param upper "the upper bounds" - @return "lower if x < lower, upper if x > upper, otherwise return x." - - @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector` - @require values::@assign_to(lower, x) `The lower bound must be convertable to the value type.` - @require values::@assign_to(upper, x) `The upper bound must be convertable to the value type.` -*> -macro clamp(x, lower, upper) => $$max(($typeof(x))lower, $$min(x, ($typeof(x))upper)); - -<* - @require values::@is_promotable_to_floatlike(mag) `The input must be a number value or float vector` - @require $defined(($typeof(values::promote_int(mag)))mag) `It's not possible to cast the sign to the type of the magnitude` -*> -macro copysign(mag, sgn) => $$copysign(values::promote_int_same(mag, sgn), ($typeof(values::promote_int_same(mag, sgn)))sgn); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector` -*> -macro cos(x) => $$cos(values::promote_int(x)); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector` -*> -macro cosec(x) => 1 / sin(x); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector` -*> -macro cosech(x) => 2 / (exp(x) - exp(-x)); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector` -*> -macro cosh(x) => (exp(x) + exp(-x)) / 2.0; - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector` -*> -macro cotan(x) => cos(x) / sin(x); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector` -*> -macro cotanh(x) => (exp(2.0 * x) + 1.0) / (exp(2.0 * x) - 1.0); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector` -*> -macro exp(x) => $$exp(values::promote_int(x)); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector` -*> -macro exp2(x) => $$exp2(values::promote_int(x)); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector` -*> -macro floor(x) => $$floor(values::promote_int(x)); - -<* - @require values::@is_promotable_to_floatlike(a) `The input must be a number or float vector` - @require values::@is_promotable_to_floatlike(b) `The input must be a number or float vector` - @require values::@is_promotable_to_floatlike(c) `The input must be a number or float vector` - @require values::@is_same_vector_type(a, b) `The input types must be equal` - @require values::@is_same_vector_type(a, c) `The input types must match` -*> -macro fma(a, b, c) => $$fma(a, b, c); - - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` - @require values::@is_promotable_to_floatlike(y) `The input must be a number or a float vector` - @require values::@is_same_vector_type(x, y) `The input types must match` -*> -macro hypot(x, y) => sqrt(sqr(x) + sqr(y)); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` -*> -macro ln(x) => $$log(values::promote_int(x)); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` - @require values::@is_promotable_to_floatlike(base) `The base must be a number or a float vector` -*> -macro log(x, base) -{ - return $$log(values::promote_int_same(x, base)) / $$log(values::promote_int_same(base, x)); -} - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` -*> -macro log2(x) => $$log2(values::promote_int(x)); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` -*> -macro log10(x) => $$log10(values::promote_int(x)); - -<* - @require types::is_numerical($typeof(x)) `The input must be a floating point value or float vector` - @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal` -*> -macro max(x, y, ...) -{ - $if $vacount == 0: - return $$max(x, y); - $else - var m = $$max(x, y); - $for (var $i = 0; $i < $vacount; $i++) - m = $$max(m, $vaarg[$i]); - $endfor - return m; - $endif -} - -<* - @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector` - @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal` -*> -macro min(x, y, ...) -{ - $if $vacount == 0: - return $$min(x, y); - $else - var m = $$min(x, y); - $for (var $i = 0; $i < $vacount; $i++) - m = $$min(m, $vaarg[$i]); - $endfor - return m; - $endif -} - -<* - @require types::@is_float(a) `The input must be a floating point value` - @require types::@has_same(a, b, c) `The input types must be equal` -*> -macro muladd(a, b, c) => $$fmuladd(a, b, c); - -<* - @require values::@is_floatlike(x) `The input must be a floating point value or float vector` -*> -macro nearbyint(x) => $$nearbyint(x); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` - @require $assignable(exp, $typeof(values::promote_int(x))) || values::@is_int(exp) `The input must be an integer, castable to the type of x` -*> -macro pow(x, exp) -{ - $if types::is_floatlike($typeof(exp)): - return $$pow(values::promote_int_same(x, exp), ($typeof(values::promote_int_same(x, exp)))exp); - $else - return $$pow_int(values::promote_int(x), exp); - $endif -} - -<* - @require values::@is_promotable_to_float(x) : `The input must be integer or floating type` -*> -macro frexp(x, int* e) -{ - $switch ($typeof(x)) - $case float: - $case float16: - return _frexpf((float)x, e); - $default: - return _frexp((double)x, e); - $endswitch -} - -<* - @require values::@is_promotable_to_float(x) : `The input must be integer or floating type` -*> -macro int signbit(x) -{ - $switch ($typeof(x)) - $case float: - $case float16: - return bitcast((float)x, uint) >> 31; - $default: - return (int)(bitcast((double)x, ulong) >> 63); - $endswitch -} - -<* - @require values::@is_floatlike(x) `The input must be a number or a float vector` -*> -macro rint(x) => $$rint(x); - -<* - @require values::@is_floatlike(x) `The input must be a floating point value or float vector` -*> -macro round(x) => $$round(x); - - -<* - @require values::@is_floatlike(x) `The input must be a floating point value or float vector` -*> -macro round_to_decimals(x, int decimal_places) -{ - var div = $$pow_int(($typeof(x))10, decimal_places); - return round(div * x) / div; -} - -<* - @require values::@is_floatlike(x) `The input must be a floating point value or float vector` -*> -macro roundeven(x) => $$roundeven(x); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` -*> -macro sec(x) => 1 / cos(x); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` -*> -macro sech(x) => 2 / (exp(x) + exp(-x)); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` -*> -macro sin(x) => $$sin(values::promote_int(x)); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` -*> -macro sinh(x) => (exp(x) - exp(-x)) / 2.0; - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` -*> -macro sqr(x) => values::promote_int(x) * values::promote_int(x); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` -*> -macro sqrt(x) => $$sqrt(values::promote_int(x)); - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` -*> -macro tan(x) -{ - var $Type = $typeof(x); - $switch - $case types::is_vector($Type): - return $$sin(x) / $$cos(x); - $case $Type.typeid == float.typeid: - return _tanf(x); - $default: - return _tan(x); - $endswitch -} - -<* - @require values::@is_promotable_to_float(x) `The input must be a float` -*> -macro bool is_finite(x) -{ - $switch ($typeof(x)) - $case float: - $case float16: - return bitcast((float)x, uint) & 0x7fffffff < 0x7f800000; - $default: - return bitcast((double)x, ulong) & (~0u64 >> 1) < 0x7ffu64 << 52; - $endswitch -} - -<* - @require values::@is_promotable_to_float(x) `The input must be a float` -*> -macro is_nan(x) -{ - $switch ($typeof(x)) - $case float: - $case float16: - return bitcast((float)x, uint) & 0x7fffffff > 0x7f800000; - $default: - return bitcast((double)x, ulong) & (~0u64 >> 1) > 0x7ffu64 << 52; - $endswitch -} - -<* - @require values::@is_promotable_to_float(x) `The input must be a float` -*> -macro is_inf(x) -{ - $switch ($typeof(x)) - $case float: - $case float16: - return bitcast((float)x, uint) & 0x7fffffff == 0x7f800000; - $default: - return bitcast((double)x, ulong) & (~0u64 >> 1) == 0x7ffu64 << 52; - $endswitch -} - -<* - @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector` -*> -macro tanh(x) => (exp(2.0 * x) - 1.0) / (exp(2.0 * x) + 1.0); - -<* - @require values::@is_floatlike(x) `The input must be a floating point value or float vector` -*> -macro trunc(x) => $$trunc(x); - -macro lerp(x, y, amount) @private => x + (y - x) * amount; -macro reflect(x, y) @private -{ - var dot = x.dot(y); - return x - 2 * y * dot; -} -macro normalize(x) @private -{ - var len = x.length(); - if (len == 0) return x; - return x * (1 / len); -} - -<* - Use a mask to select values from either "then" or "else" vectors. - - @param mask "The mask to use for the select, 'true' will pick the then_value, 'false' the else_value" - @param then_value "The vector to get elements from where the mask is 'true'" - @param else_value "The vector to get elements from where the mask is 'false'" - @require values::@is_vector(then_value) && values::@is_vector(else_value) "'Then' and 'else' must be vectors." - @require values::@is_same_type(then_value, else_value) "'Then' and 'else' vectors must be of the same type." - @require then_value.len == mask.len "Mask and selected vectors must be of the same width." - - @return "a vector of the same type as then/else" -*> -macro select(bool[] mask, then_value, else_value) -{ - return $$select(mask, then_value, else_value); -} - -macro float float.ceil(float x) => $$ceil(x); -macro float float.clamp(float x, float lower, float upper) => $$max(lower, $$min(x, upper)); -macro float float.copysign(float mag, float sgn) => $$copysign(mag, sgn); -macro float float.floor(float x) => $$floor(x); -macro float float.fma(float a, float b, float c) => $$fma(a, b, c); -macro float float.muladd(float a, float b, float c) => $$fmuladd(a, b, c); -macro float float.nearbyint(float x) => $$nearbyint(x); -macro float float.pow(float x, exp) => pow(x, exp); -macro float float.rint(float x) => $$rint(x); -macro float float.round(float x) => $$round(x); -macro float float.roundeven(float x) => $$roundeven(x); -macro float float.trunc(float x) => $$trunc(x); - -macro float float[].sum(float[] x, float start = 0.0) => $$reduce_fadd(x, start); -macro float float[].product(float[] x, float start = 1.0) => $$reduce_fmul(x, start); -macro float float[].max(float[] x) => $$reduce_max(x); -macro float float[].min(float[] x) => $$reduce_min(x); -macro float[] float[].ceil(float[] x) => $$ceil(x); -macro float[] float[].clamp(float[] x, float[] lower, float[] upper) => $$max(lower, $$min(x, upper)); -macro float[] float[].copysign(float[] mag, float[] sgn) => $$copysign(mag, sgn); -macro float[] float[].fma(float[] a, float[] b, float[] c) => $$fma(a, b, c); -macro float[] float[].floor(float[] x) => $$floor(x); -macro float[] float[].nearbyint(float[] x) => $$nearbyint(x); -macro float[] float[].pow(float[] x, exp) => pow(x, exp); -macro float[] float[].rint(float[] x) => $$rint(x); -macro float[] float[].round(float[] x) => $$round(x); -macro float[] float[].roundeven(float[] x) => $$roundeven(x); -macro float[] float[].trunc(float[] x) => $$trunc(x); -macro float float[].dot(float[] x, float[] y) => (x * y).sum(); -macro float float[].length(float[] x) => $$sqrt(x.dot(x)); -macro float float[].distance(float[] x, float[] y) => (x - y).length(); -macro float[] float[].normalize(float[] x) => normalize(x); -macro float[] float[].lerp(float[] x, float[] y, float amount) => lerp(x, y, amount); -macro float[] float[].reflect(float[] x, float[] y) => reflect(x, y); -macro bool float[].equals(float[] x, float[] y) => equals_vec(x, y); - -macro bool[] float[].comp_lt(float[] x, float[] y) => $$veccomplt(x, y); -macro bool[] float[].comp_le(float[] x, float[] y) => $$veccomple(x, y); -macro bool[] float[].comp_eq(float[] x, float[] y) => $$veccompeq(x, y); -macro bool[] float[].comp_gt(float[] x, float[] y) => $$veccompgt(x, y); -macro bool[] float[].comp_ge(float[] x, float[] y) => $$veccompge(x, y); -macro bool[] float[].comp_ne(float[] x, float[] y) => $$veccompne(x, y); - -macro double double.ceil(double x) => $$ceil(x); -macro double double.clamp(double x, double lower, double upper) => $$max(lower, $$min(x, upper)); -macro double double.copysign(double mag, double sgn) => $$copysign(mag, sgn); -macro double double.floor(double x) => $$floor(x); -macro double double.fma(double a, double b, double c) => $$fma(a, b, c); -macro double double.muladd(double a, double b, double c) => $$fmuladd(a, b, c); -macro double double.nearbyint(double x) => $$nearbyint(x); -macro double double.pow(double x, exp) => pow(x, exp); -macro double double.rint(double x) => $$rint(x); -macro double double.round(double x) => $$round(x); -macro double double.roundeven(double x) => $$roundeven(x); -macro double double.trunc(double x) => $$trunc(x); - -macro double double[].sum(double[] x, double start = 0.0) => $$reduce_fadd(x, start); -macro double double[].product(double[] x, double start = 1.0) => $$reduce_fmul(x, start); -macro double double[].max(double[] x) => $$reduce_fmax(x); -macro double double[].min(double[] x) => $$reduce_fmin(x); -macro double[] double[].ceil(double[] x) => $$ceil(x); -macro double[] double[].clamp(double[] x, double[] lower, double[] upper) => $$max(lower, $$min(x, upper)); -macro double[] double[].copysign(double[] mag, double[] sgn) => $$copysign(mag, sgn); -macro double[] double[].floor(double[] x) => $$floor(x); -macro double[] double[].fma(double[] a, double[] b, double[] c) => $$fma(a, b, c); -macro double[] double[].nearbyint(double[] x) => $$nearbyint(x); -macro double[] double[].pow(double[] x, exp) => pow(x, exp); -macro double[] double[].rint(double[] x) => $$rint(x); -macro double[] double[].round(double[] x) => $$round(x); -macro double[] double[].roundeven(double[] x) => $$roundeven(x); -macro double[] double[].trunc(double[] x) => $$trunc(x); -macro double double[].dot(double[] x, double[] y) => (x * y).sum(); -macro double double[].length(double[] x) => $$sqrt(x.dot(x)); -macro double double[].distance(double[] x, double[] y) => (x - y).length(); -macro double[] double[].normalize(double[] x) => normalize(x); -macro double[] double[].reflect(double[] x, double[] y) => reflect(x, y); -macro double[] double[].lerp(double[] x, double[] y, double amount) => lerp(x, y, amount); -macro bool double[].equals(double[] x, double[] y) => equals_vec(x, y); - -macro bool[] double[].comp_lt(double[] x, double[] y) => $$veccomplt(x, y); -macro bool[] double[].comp_le(double[] x, double[] y) => $$veccomple(x, y); -macro bool[] double[].comp_eq(double[] x, double[] y) => $$veccompeq(x, y); -macro bool[] double[].comp_gt(double[] x, double[] y) => $$veccompgt(x, y); -macro bool[] double[].comp_ge(double[] x, double[] y) => $$veccompge(x, y); -macro bool[] double[].comp_ne(double[] x, double[] y) => $$veccompne(x, y); - -macro bool[] ichar[].comp_lt(ichar[] x, ichar[] y) => $$veccomplt(x, y); -macro bool[] ichar[].comp_le(ichar[] x, ichar[] y) => $$veccomple(x, y); -macro bool[] ichar[].comp_eq(ichar[] x, ichar[] y) => $$veccompeq(x, y); -macro bool[] ichar[].comp_gt(ichar[] x, ichar[] y) => $$veccompgt(x, y); -macro bool[] ichar[].comp_ge(ichar[] x, ichar[] y) => $$veccompge(x, y); -macro bool[] ichar[].comp_ne(ichar[] x, ichar[] y) => $$veccompne(x, y); - -macro ichar ichar[].sum(ichar[] x) => $$reduce_add(x); -macro ichar ichar[].product(ichar[] x) => $$reduce_mul(x); -macro ichar ichar[].and(ichar[] x) => $$reduce_and(x); -macro ichar ichar[].or(ichar[] x) => $$reduce_or(x); -macro ichar ichar[].xor(ichar[] x) => $$reduce_xor(x); -macro ichar ichar[].max(ichar[] x) => $$reduce_max(x); -macro ichar ichar[].min(ichar[] x) => $$reduce_min(x); -macro ichar ichar[].dot(ichar[] x, ichar[] y) => (x * y).sum(); - -macro bool[] short[].comp_lt(short[] x, short[] y) => $$veccomplt(x, y); -macro bool[] short[].comp_le(short[] x, short[] y) => $$veccomple(x, y); -macro bool[] short[].comp_eq(short[] x, short[] y) => $$veccompeq(x, y); -macro bool[] short[].comp_gt(short[] x, short[] y) => $$veccompgt(x, y); -macro bool[] short[].comp_ge(short[] x, short[] y) => $$veccompge(x, y); -macro bool[] short[].comp_ne(short[] x, short[] y) => $$veccompne(x, y); - -macro short short[].sum(short[] x) => $$reduce_add(x); -macro short short[].product(short[] x) => $$reduce_mul(x); -macro short short[].and(short[] x) => $$reduce_and(x); -macro short short[].or(short[] x) => $$reduce_or(x); -macro short short[].xor(short[] x) => $$reduce_xor(x); -macro short short[].max(short[] x) => $$reduce_max(x); -macro short short[].min(short[] x) => $$reduce_min(x); -macro short short[].dot(short[] x, short[] y) => (x * y).sum(); - -macro bool[] int[].comp_lt(int[] x, int[] y) => $$veccomplt(x, y); -macro bool[] int[].comp_le(int[] x, int[] y) => $$veccomple(x, y); -macro bool[] int[].comp_eq(int[] x, int[] y) => $$veccompeq(x, y); -macro bool[] int[].comp_gt(int[] x, int[] y) => $$veccompgt(x, y); -macro bool[] int[].comp_ge(int[] x, int[] y) => $$veccompge(x, y); -macro bool[] int[].comp_ne(int[] x, int[] y) => $$veccompne(x, y); - -macro int int[].sum(int[] x) => $$reduce_add(x); -macro int int[].product(int[] x) => $$reduce_mul(x); -macro int int[].and(int[] x) => $$reduce_and(x); -macro int int[].or(int[] x) => $$reduce_or(x); -macro int int[].xor(int[] x) => $$reduce_xor(x); -macro int int[].max(int[] x) => $$reduce_max(x); -macro int int[].min(int[] x) => $$reduce_min(x); -macro int int[].dot(int[] x, int[] y) => (x * y).sum(); - -macro bool[] long[].comp_lt(long[] x, long[] y) => $$veccomplt(x, y); -macro bool[] long[].comp_le(long[] x, long[] y) => $$veccomple(x, y); -macro bool[] long[].comp_eq(long[] x, long[] y) => $$veccompeq(x, y); -macro bool[] long[].comp_gt(long[] x, long[] y) => $$veccompgt(x, y); -macro bool[] long[].comp_ge(long[] x, long[] y) => $$veccompge(x, y); -macro bool[] long[].comp_ne(long[] x, long[] y) => $$veccompne(x, y); -macro long long[].sum(long[] x) => $$reduce_add(x); -macro long long[].product(long[] x) => $$reduce_mul(x); -macro long long[].and(long[] x) => $$reduce_and(x); -macro long long[].or(long[] x) => $$reduce_or(x); -macro long long[].xor(long[] x) => $$reduce_xor(x); -macro long long[].max(long[] x) => $$reduce_max(x); -macro long long[].min(long[] x) => $$reduce_min(x); -macro long long[].dot(long[] x, long[] y) => (x * y).sum(); - -macro bool[] int128[].comp_lt(int128[] x, int128[] y) => $$veccomplt(x, y); -macro bool[] int128[].comp_le(int128[] x, int128[] y) => $$veccomple(x, y); -macro bool[] int128[].comp_eq(int128[] x, int128[] y) => $$veccompeq(x, y); -macro bool[] int128[].comp_gt(int128[] x, int128[] y) => $$veccompgt(x, y); -macro bool[] int128[].comp_ge(int128[] x, int128[] y) => $$veccompge(x, y); -macro bool[] int128[].comp_ne(int128[] x, int128[] y) => $$veccompne(x, y); -macro int128 int128[].sum(int128[] x) => $$reduce_add(x); -macro int128 int128[].product(int128[] x) => $$reduce_mul(x); -macro int128 int128[].and(int128[] x) => $$reduce_and(x); -macro int128 int128[].or(int128[] x) => $$reduce_or(x); -macro int128 int128[].xor(int128[] x) => $$reduce_xor(x); -macro int128 int128[].max(int128[] x) => $$reduce_max(x); -macro int128 int128[].min(int128[] x) => $$reduce_min(x); -macro int128 int128[].dot(int128[] x, int128[] y) => (x * y).sum(); - -macro bool[] bool[].comp_lt(bool[] x, bool[] y) => $$veccomplt(x, y); -macro bool[] bool[].comp_le(bool[] x, bool[] y) => $$veccomple(x, y); -macro bool[] bool[].comp_eq(bool[] x, bool[] y) => $$veccompeq(x, y); -macro bool[] bool[].comp_gt(bool[] x, bool[] y) => $$veccompgt(x, y); -macro bool[] bool[].comp_ge(bool[] x, bool[] y) => $$veccompge(x, y); -macro bool[] bool[].comp_ne(bool[] x, bool[] y) => $$veccompne(x, y); - -macro bool bool[].sum(bool[] x) => $$reduce_add(x); -macro bool bool[].product(bool[] x) => $$reduce_mul(x); -macro bool bool[].and(bool[] x) => $$reduce_and(x); -macro bool bool[].or(bool[] x) => $$reduce_or(x); -macro bool bool[].xor(bool[] x) => $$reduce_xor(x); -macro bool bool[].max(bool[] x) => $$reduce_max(x); -macro bool bool[].min(bool[] x) => $$reduce_min(x); - -macro bool[] char[].comp_lt(char[] x, char[] y) => $$veccomplt(x, y); -macro bool[] char[].comp_le(char[] x, char[] y) => $$veccomple(x, y); -macro bool[] char[].comp_eq(char[] x, char[] y) => $$veccompeq(x, y); -macro bool[] char[].comp_gt(char[] x, char[] y) => $$veccompgt(x, y); -macro bool[] char[].comp_ge(char[] x, char[] y) => $$veccompge(x, y); -macro bool[] char[].comp_ne(char[] x, char[] y) => $$veccompne(x, y); - -macro char char[].sum(char[] x) => $$reduce_add(x); -macro char char[].product(char[] x) => $$reduce_mul(x); -macro char char[].and(char[] x) => $$reduce_and(x); -macro char char[].or(char[] x) => $$reduce_or(x); -macro char char[].xor(char[] x) => $$reduce_xor(x); -macro char char[].max(char[] x) => $$reduce_max(x); -macro char char[].min(char[] x) => $$reduce_min(x); -macro char char[].dot(char[] x, char[] y) => (x * y).sum(); - -macro bool[] ushort[].comp_lt(ushort[] x, ushort[] y) => $$veccomplt(x, y); -macro bool[] ushort[].comp_le(ushort[] x, ushort[] y) => $$veccomple(x, y); -macro bool[] ushort[].comp_eq(ushort[] x, ushort[] y) => $$veccompeq(x, y); -macro bool[] ushort[].comp_gt(ushort[] x, ushort[] y) => $$veccompgt(x, y); -macro bool[] ushort[].comp_ge(ushort[] x, ushort[] y) => $$veccompge(x, y); -macro bool[] ushort[].comp_ne(ushort[] x, ushort[] y) => $$veccompne(x, y); - -macro ushort ushort[].sum(ushort[] x) => $$reduce_add(x); -macro ushort ushort[].product(ushort[] x) => $$reduce_mul(x); -macro ushort ushort[].and(ushort[] x) => $$reduce_and(x); -macro ushort ushort[].or(ushort[] x) => $$reduce_or(x); -macro ushort ushort[].xor(ushort[] x) => $$reduce_xor(x); -macro ushort ushort[].max(ushort[] x) => $$reduce_max(x); -macro ushort ushort[].min(ushort[] x) => $$reduce_min(x); -macro ushort ushort[].dot(ushort[] x, ushort[] y) => (x * y).sum(); - -macro bool[] uint[].comp_lt(uint[] x, uint[] y) => $$veccomplt(x, y); -macro bool[] uint[].comp_le(uint[] x, uint[] y) => $$veccomple(x, y); -macro bool[] uint[].comp_eq(uint[] x, uint[] y) => $$veccompeq(x, y); -macro bool[] uint[].comp_gt(uint[] x, uint[] y) => $$veccompgt(x, y); -macro bool[] uint[].comp_ge(uint[] x, uint[] y) => $$veccompge(x, y); -macro bool[] uint[].comp_ne(uint[] x, uint[] y) => $$veccompne(x, y); - -macro uint uint[].sum(uint[] x) => $$reduce_add(x); -macro uint uint[].product(uint[] x) => $$reduce_mul(x); -macro uint uint[].and(uint[] x) => $$reduce_and(x); -macro uint uint[].or(uint[] x) => $$reduce_or(x); -macro uint uint[].xor(uint[] x) => $$reduce_xor(x); -macro uint uint[].max(uint[] x) => $$reduce_max(x); -macro uint uint[].min(uint[] x) => $$reduce_min(x); -macro uint uint[].dot(uint[] x, uint[] y) => (x * y).sum(); - -macro bool[] ulong[].comp_lt(ulong[] x, ulong[] y) => $$veccomplt(x, y); -macro bool[] ulong[].comp_le(ulong[] x, ulong[] y) => $$veccomple(x, y); -macro bool[] ulong[].comp_eq(ulong[] x, ulong[] y) => $$veccompeq(x, y); -macro bool[] ulong[].comp_gt(ulong[] x, ulong[] y) => $$veccompgt(x, y); -macro bool[] ulong[].comp_ge(ulong[] x, ulong[] y) => $$veccompge(x, y); -macro bool[] ulong[].comp_ne(ulong[] x, ulong[] y) => $$veccompne(x, y); - -macro ulong ulong[].sum(ulong[] x) => $$reduce_add(x); -macro ulong ulong[].product(ulong[] x) => $$reduce_mul(x); -macro ulong ulong[].and(ulong[] x) => $$reduce_and(x); -macro ulong ulong[].or(ulong[] x) => $$reduce_or(x); -macro ulong ulong[].xor(ulong[] x) => $$reduce_xor(x); -macro ulong ulong[].max(ulong[] x) => $$reduce_max(x); -macro ulong ulong[].min(ulong[] x) => $$reduce_min(x); -macro ulong ulong[].dot(ulong[] x, ulong[] y) => (x * y).sum(); - -macro bool[] uint128[].comp_lt(uint128[] x, uint128[] y) => $$veccomplt(x, y); -macro bool[] uint128[].comp_le(uint128[] x, uint128[] y) => $$veccomple(x, y); -macro bool[] uint128[].comp_eq(uint128[] x, uint128[] y) => $$veccompeq(x, y); -macro bool[] uint128[].comp_gt(uint128[] x, uint128[] y) => $$veccompgt(x, y); -macro bool[] uint128[].comp_ge(uint128[] x, uint128[] y) => $$veccompge(x, y); -macro bool[] uint128[].comp_ne(uint128[] x, uint128[] y) => $$veccompne(x, y); - -macro uint128 uint128[].sum(uint128[] x) => $$reduce_add(x); -macro uint128 uint128[].product(uint128[] x) => $$reduce_mul(x); -macro uint128 uint128[].and(uint128[] x) => $$reduce_and(x); -macro uint128 uint128[].or(uint128[] x) => $$reduce_or(x); -macro uint128 uint128[].xor(uint128[] x) => $$reduce_xor(x); -macro uint128 uint128[].max(uint128[] x) => $$reduce_max(x); -macro uint128 uint128[].min(uint128[] x) => $$reduce_min(x); -macro uint128 uint128[].dot(uint128[] x, uint128[] y) => (x * y).sum(); - -macro char char.sat_add(char x, char y) => $$sat_add(x, y); -macro char char.sat_sub(char x, char y) => $$sat_sub(x, y); -macro char char.sat_mul(char x, char y) => $$sat_mul(x, y); -macro char char.sat_shl(char x, char y) => $$sat_shl(x, y); -macro char! char.overflow_add(char x, char y) => overflow_add_helper(x, y); -macro char! char.overflow_sub(char x, char y) => overflow_sub_helper(x, y); -macro char! char.overflow_mul(char x, char y) => overflow_mul_helper(x, y); - - -macro ichar ichar.sat_add(ichar x, ichar y) => $$sat_add(x, y); -macro ichar ichar.sat_sub(ichar x, ichar y) => $$sat_sub(x, y); -macro ichar ichar.sat_mul(ichar x, ichar y) => $$sat_mul(x, y); -macro ichar ichar.sat_shl(ichar x, ichar y) => $$sat_shl(x, y); -macro ichar! ichar.overflow_add(ichar x, ichar y) => overflow_add_helper(x, y); -macro ichar! ichar.overflow_sub(ichar x, ichar y) => overflow_sub_helper(x, y); -macro ichar! ichar.overflow_mul(ichar x, ichar y) => overflow_mul_helper(x, y); - -macro ushort ushort.sat_add(ushort x, ushort y) => $$sat_add(x, y); -macro ushort ushort.sat_sub(ushort x, ushort y) => $$sat_sub(x, y); -macro ushort ushort.sat_mul(ushort x, ushort y) => $$sat_mul(x, y); -macro ushort ushort.sat_shl(ushort x, ushort y) => $$sat_shl(x, y); -macro ushort! ushort.overflow_add(ushort x, ushort y) => overflow_add_helper(x, y); -macro ushort! ushort.overflow_sub(ushort x, ushort y) => overflow_sub_helper(x, y); -macro ushort! ushort.overflow_mul(ushort x, ushort y) => overflow_mul_helper(x, y); - -macro short short.sat_add(short x, short y) => $$sat_add(x, y); -macro short short.sat_sub(short x, short y) => $$sat_sub(x, y); -macro short short.sat_mul(short x, short y) => $$sat_mul(x, y); -macro short short.sat_shl(short x, short y) => $$sat_shl(x, y); -macro short! short.overflow_add(short x, short y) => overflow_add_helper(x, y); -macro short! short.overflow_sub(short x, short y) => overflow_sub_helper(x, y); -macro short! short.overflow_mul(short x, short y) => overflow_mul_helper(x, y); - -macro uint uint.sat_add(uint x, uint y) => $$sat_add(x, y); -macro uint uint.sat_sub(uint x, uint y) => $$sat_sub(x, y); -macro uint uint.sat_mul(uint x, uint y) => $$sat_mul(x, y); -macro uint uint.sat_shl(uint x, uint y) => $$sat_shl(x, y); -macro uint! uint.overflow_add(uint x, uint y) => overflow_add_helper(x, y); -macro uint! uint.overflow_sub(uint x, uint y) => overflow_sub_helper(x, y); -macro uint! uint.overflow_mul(uint x, uint y) => overflow_mul_helper(x, y); - -macro int int.sat_add(int x, int y) => $$sat_add(x, y); -macro int int.sat_sub(int x, int y) => $$sat_sub(x, y); -macro int int.sat_mul(int x, int y) => $$sat_mul(x, y); -macro int int.sat_shl(int x, int y) => $$sat_shl(x, y); -macro int! int.overflow_add(int x, int y) => overflow_add_helper(x, y); -macro int! int.overflow_sub(int x, int y) => overflow_sub_helper(x, y); -macro int! int.overflow_mul(int x, int y) => overflow_mul_helper(x, y); - -macro ulong ulong.sat_add(ulong x, ulong y) => $$sat_add(x, y); -macro ulong ulong.sat_sub(ulong x, ulong y) => $$sat_sub(x, y); -macro ulong ulong.sat_mul(ulong x, ulong y) => $$sat_mul(x, y); -macro ulong ulong.sat_shl(ulong x, ulong y) => $$sat_shl(x, y); -macro ulong! ulong.overflow_add(ulong x, ulong y) => overflow_add_helper(x, y); -macro ulong! ulong.overflow_sub(ulong x, ulong y) => overflow_sub_helper(x, y); -macro ulong! ulong.overflow_mul(ulong x, ulong y) => overflow_mul_helper(x, y); - -macro long long.sat_add(long x, long y) => $$sat_add(x, y); -macro long long.sat_sub(long x, long y) => $$sat_sub(x, y); -macro long long.sat_mul(long x, long y) => $$sat_mul(x, y); -macro long long.sat_shl(long x, long y) => $$sat_shl(x, y); -macro long! long.overflow_add(long x, long y) => overflow_add_helper(x, y); -macro long! long.overflow_sub(long x, long y) => overflow_sub_helper(x, y); -macro long! long.overflow_mul(long x, long y) => overflow_mul_helper(x, y); - -macro uint128 uint128.sat_add(uint128 x, uint128 y) => $$sat_add(x, y); -macro uint128 uint128.sat_sub(uint128 x, uint128 y) => $$sat_sub(x, y); -macro uint128 uint128.sat_mul(uint128 x, uint128 y) => $$sat_mul(x, y); -macro uint128 uint128.sat_shl(uint128 x, uint128 y) => $$sat_shl(x, y); -macro uint128! uint128.overflow_add(uint128 x, uint128 y) => overflow_add_helper(x, y); -macro uint128! uint128.overflow_sub(uint128 x, uint128 y) => overflow_sub_helper(x, y); -macro uint128! uint128.overflow_mul(uint128 x, uint128 y) => overflow_mul_helper(x, y); - -macro int128 int128.sat_add(int128 x, int128 y) => $$sat_add(x, y); -macro int128 int128.sat_sub(int128 x, int128 y) => $$sat_sub(x, y); -macro int128 int128.sat_mul(int128 x, int128 y) => $$sat_mul(x, y); -macro int128 int128.sat_shl(int128 x, int128 y) => $$sat_shl(x, y); -macro int128! int128.overflow_add(int128 x, int128 y) => overflow_add_helper(x, y); -macro int128! int128.overflow_sub(int128 x, int128 y) => overflow_sub_helper(x, y); -macro int128! int128.overflow_mul(int128 x, int128 y) => overflow_mul_helper(x, y); -<* - @require values::@is_int(x) `The input must be an integer` -*> -macro bool is_odd(x) => (bool)(x & 1); - -<* - @require values::@is_int(x) `The input must be an integer` -*> -macro bool is_even(x) => !is_odd(x); - -macro bool char.is_even(char x) => is_even(x); -macro bool char.is_odd(char x) => is_odd(x); - -macro bool ichar.is_even(ichar x) => is_even(x); -macro bool ichar.is_odd(ichar x) => is_odd(x); - -macro bool ushort.is_even(ushort x) => is_even(x); -macro bool ushort.is_odd(ushort x) => is_odd(x); - -macro bool short.is_even(short x) => is_even(x); -macro bool short.is_odd(short x) => is_odd(x); - -macro bool uint.is_even(uint x) => is_even(x); -macro bool uint.is_odd(uint x) => is_odd(x); - -macro bool int.is_even(int x) => is_even(x); -macro bool int.is_odd(int x) => is_odd(x); - -macro bool ulong.is_even(ulong x) => is_even(x); -macro bool ulong.is_odd(ulong x) => is_odd(x); - -macro bool long.is_even(long x) => is_even(x); -macro bool long.is_odd(long x) => is_odd(x); - -macro bool uint128.is_even(uint128 x) => is_even(x); -macro bool uint128.is_odd(uint128 x) => is_odd(x); - -macro bool int128.is_even(int128 x) => is_even(x); -macro bool int128.is_odd(int128 x) => is_odd(x); - -<* - @require types::is_underlying_int($typeof(x)) : `is_power_of_2 may only be used on integer types` -*> -macro bool is_power_of_2(x) -{ - return x != 0 && (x & (x - 1)) == 0; -} - -macro next_power_of_2(x) -{ - $typeof(x) y = 1; - while (y < x) y += y; - return y; -} - -macro equals_vec(v1, v2) @private -{ - var $elements = v1.len; - var abs_diff = math::abs(v1 - v2); - var abs_v1 = math::abs(v1); - var abs_v2 = math::abs(v2); - $typeof(abs_v2) eps = 1; - return abs_diff.comp_le(FLOAT_EPSILON * math::max(abs_v1, abs_v2, eps)).and(); -} - -macro uint double.high_word(double d) => (uint)(bitcast(d, ulong) >> 32); -macro uint double.low_word(double d) => (uint)bitcast(d, ulong); -macro uint float.word(float d) => bitcast(d, uint); - -macro void double.set_high_word(double* d, uint u) -{ - ulong rep = bitcast(*d, ulong); - rep = ((ulong)u << 32) | (rep & 0xffffffff); - *d = bitcast(rep, double); -} - -macro void double.set_low_word(double* d, uint u) -{ - ulong rep = bitcast(*d, ulong); - rep = (rep & 0xffffffff00000000) | (ulong)u; - *d = bitcast(rep, double); -} - -macro void float.set_word(float* f, uint u) => *f = bitcast(u, float); - -macro double scalbn(double x, int n) => _scalbn(x, n); - -extern fn double _atan(double x) @extern("atan"); -extern fn float _atanf(float x) @extern("atanf"); -extern fn double _atan2(double, double) @extern("atan2"); -extern fn float _atan2f(float, float) @extern("atan2f"); -extern fn void _sincos(double, double*, double*) @extern("__sincos") @link("m") @if(env::DARWIN); -extern fn void _sincosf(float, float*, float*) @extern("__sincosf") @link("m") @if(env::DARWIN); -extern fn void _sincos(double, double*, double*) @extern("sincos") @link("m") @if(!env::DARWIN); -extern fn void _sincosf(float, float*, float*) @extern("sincosf") @link("m") @if(!env::DARWIN); -extern fn double _tan(double x) @extern("tan"); -extern fn float _tanf(float x) @extern("tanf"); -extern fn double _scalbn(double x, int n) @extern("scalbn"); -extern fn double _acos(double x) @extern("acos"); -extern fn double _asin(double x) @extern("asin"); -extern fn double _acosh(double x) @extern("acosh"); -extern fn double _asinh(double x) @extern("asinh"); -extern fn double _atanh(double x) @extern("atanh"); -extern fn float _acosf(float x) @extern("acosf"); -extern fn float _asinf(float x) @extern("asinf"); -extern fn float _acoshf(float x) @extern("acoshf"); -extern fn float _asinhf(float x) @extern("asinhf"); -extern fn float _atanhf(float x) @extern("atanhf"); - - -fn double _frexp(double x, int* e) -{ - ulong i = bitcast(x, ulong); - int ee = (int)((i >> 52) & 0x7ff); - switch - { - case !ee: - if (!x) - { - *e = 0; - return x; - } - x = _frexp(x * 0x1p64, e); - *e -= 64; - return x; - case ee == 0x7ff: - return x; - default: - *e = ee - 0x3fe; - i &= 0x800fffffffffffffu64; - i |= 0x3fe0000000000000u64; - return bitcast(i, double); - } -} - -fn float _frexpf(float x, int* e) -{ - uint i = bitcast(x, uint); - int ee = (i >> 23) & 0xff; - - switch - { - case !ee: - if (!x) - { - *e = 0; - return x; - } - x = _frexpf(x * 0x1p64, e); - *e -= 64; - return x; - case ee == 0xff: - return x; - default: - *e = ee - 0x7e; - i &= 0x807fffffu32; - i |= 0x3f000000u32; - return bitcast(i, float); - } -} - -macro overflow_add_helper(x, y) @local -{ - $typeof(x) res @noinit; - if ($$overflow_add(x, y, &res)) return MathError.OVERFLOW?; - return res; -} - -macro overflow_sub_helper(x, y) @local -{ - $typeof(x) res @noinit; - if ($$overflow_sub(x, y, &res)) return MathError.OVERFLOW?; - return res; -} - -macro overflow_mul_helper(x, y) @local -{ - $typeof(x) res @noinit; - if ($$overflow_mul(x, y, &res)) return MathError.OVERFLOW?; - return res; -} - -macro mul_div_helper(val, mul, div) @private -{ - var $Type = $typeof(val); - return ($Type)(($Type)mul * (val / ($Type)div) + ($Type)mul * (val % ($Type)div) / ($Type)div); -} -macro char char.muldiv(self, char mul, char div) => mul_div_helper(self, mul, div); -macro ichar ichar.muldiv(self, ichar mul, ichar div) => mul_div_helper(self, mul, div); -macro short short.muldiv(self, short mul, short div) => mul_div_helper(self, mul, div); -macro ushort ushort.muldiv(self, ushort mul, ushort div) => mul_div_helper(self, mul, div); -macro int int.muldiv(self, int mul, int div) => mul_div_helper(self, mul, div); -macro uint uint.muldiv(self, uint mul, uint div) => mul_div_helper(self, mul, div); -macro long long.muldiv(self, long mul, long div) => mul_div_helper(self, mul, div); -macro ulong ulong.muldiv(self, ulong mul, ulong div) => mul_div_helper(self, mul, div); - -macro bool @is_same_vector_or_scalar(#vector_value, #vector_or_scalar) @private -{ - return (values::@is_vector(#vector_or_scalar) &&& values::@is_same_vector_type(#vector_value, #vector_or_scalar)) ||| values::@is_int(#vector_or_scalar); -} - -<* - @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar` - @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar` -*> -macro char[] char[].muldiv(self, mul, div) => mul_div_helper(self, mul, div); - -<* - @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar` - @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar` -*> -macro ichar[] ichar[].muldiv(self, mul, div) => mul_div_helper(self, mul, div); - -<* - @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar` - @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar` -*> -macro short[] short[].muldiv(self, mul, div) => mul_div_helper(self, mul, div); - -<* - @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar` - @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar` -*> -macro ushort[] ushort[].muldiv(self, mul, div) => mul_div_helper(self, mul, div); - -<* - @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar` - @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar` -*> -macro int[] int[].muldiv(self, mul, div) => mul_div_helper(self, mul, div); - -<* - @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar` - @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar` -*> -macro uint[] uint[].muldiv(self, mul, div) => mul_div_helper(self, mul, div); - -<* - @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar` - @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar` -*> -macro long[] long[].muldiv(self, mul, div) => mul_div_helper(self, mul, div); - -<* - @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar` - @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar` -*> -macro ulong[] ulong[].muldiv(self, mul, div) => mul_div_helper(self, mul, div); - -<* -@require types::is_int($typeof(a)) `The input must be an integer` -@require types::is_int($typeof(b)) `The input must be an integer` -*> -macro _gcd(a, b) @private -{ - if (a == 0) return b; - if (b == 0) return a; - - var $Type = $typeof(a); - $Type r, aa, ab; - aa = abs(a); - ab = abs(b); - while (ab != 0) - { - r = aa % ab; - aa = ab; - ab = r; - } - return aa; -} - -<* -Calculate the least common multiple for the provided arguments. - -@require $vacount >= 2 `At least two arguments are required.` -*> -macro lcm(...) -{ - $typeof($vaarg[0]) result = $vaarg[0]; - $for (var $i = 1; $i < $vacount; $i++) - $if $defined(result.lcm): - result = result.lcm($vaarg[$i]); - $else - result = (abs($vaarg[$i]) * abs(result)) / (_gcd($vaarg[$i], result)); - $endif - $endfor - return result; -} - -<* -Calculate the greatest common divisor for the provided arguments. - -@require $vacount >= 2 `At least two arguments are required.` -*> -macro gcd(...) -{ - $typeof($vaarg[0]) result = $vaarg[0]; - $for (var $i = 1; $i < $vacount; $i++) - $if $defined(result.gcd): - result = result.gcd($vaarg[$i]); - $else - result = _gcd($vaarg[$i], result); - $endif - $endfor - return result; -} diff --git a/lib7/std/math/math_builtin.c3 b/lib7/std/math/math_builtin.c3 deleted file mode 100644 index a7f246974..000000000 --- a/lib7/std/math/math_builtin.c3 +++ /dev/null @@ -1,27 +0,0 @@ -module std::math; - -fn float __roundevenf(float f) @extern("roundevenf") @weak @nostrip -{ - // Slow implementation - return round(f / 2) * 2; -} - -fn double __roundeven(double d) @extern("roundeven") @weak @nostrip -{ - // Slow implementation - return round(d / 2) * 2; -} - -fn double __powidf2(double a, int b) @extern("__powidf2") @weak @nostrip -{ - bool recip = b < 0; - double r = 1; - while (1) - { - if (b & 1) r *= a; - b /= 2; - if (b == 0) break; - a *= a; - } - return recip ? 1 / r : r; -} \ No newline at end of file diff --git a/lib7/std/math/math_complex.c3 b/lib7/std/math/math_complex.c3 deleted file mode 100644 index df3a39543..000000000 --- a/lib7/std/math/math_complex.c3 +++ /dev/null @@ -1,31 +0,0 @@ -module std::math::complex{Real}; - -union Complex -{ - struct - { - Real r, c; - } - Real[<2>] v; -} - -const Complex IDENTITY = { 1, 0 }; -const Complex IMAGINARY = { 0, 1 }; -macro Complex Complex.add(self, Complex b) => { .v = self.v + b.v }; -macro Complex Complex.add_each(self, Real b) => { .v = self.v + b }; -macro Complex Complex.sub(self, Complex b) => { .v = self.v - b.v }; -macro Complex Complex.sub_each(self, Real b) => { .v = self.v - b }; -macro Complex Complex.scale(self, Real s) => { .v = self.v * s }; -macro Complex Complex.mul(self, Complex b) => { self.r * b.r - self.c * b.c, self.r * b.c + b.r * self.c }; -macro Complex Complex.div(self, Complex b) -{ - Real div = b.v.dot(b.v); - return { (self.r * b.r + self.c * b.c) / div, (self.c * b.r - self.r * b.c) / div }; -} -macro Complex Complex.inverse(self) -{ - Real sqr = self.v.dot(self.v); - return { self.r / sqr, -self.c / sqr }; -} -macro Complex Complex.conjugate(self) => { .r = self.r, .c = -self.c }; -macro bool Complex.equals(self, Complex b) => self.v == b.v; diff --git a/lib7/std/math/math_easings.c3 b/lib7/std/math/math_easings.c3 deleted file mode 100644 index c91ddbef0..000000000 --- a/lib7/std/math/math_easings.c3 +++ /dev/null @@ -1,201 +0,0 @@ -module std::math::easing; - -/* Easing equations ported from Robert Penner's equations */ - -/* - * - * TERMS OF USE - EASING EQUATIONS - * - * Open source under the BSD License. - * - * Copyright © 2001 Robert Penner - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * Neither the name of the author nor the names of contributors may be used to endorse - * or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -// Linear Easing functions -fn float linear_none(float t, float b, float c, float d) @inline => c * t / d + b; -fn float linear_in(float t, float b, float c, float d) @inline => c * t / d + b; -fn float linear_out(float t, float b, float c, float d) @inline => c * t / d + b; -fn float linear_inout(float t, float b, float c, float d) @inline => c * t / d + b; - -// Sine Easing functions -fn float sine_in(float t, float b, float c, float d) @inline => -c * math::cos(t / d * (float)math::PI_2) + c + b; -fn float sine_out(float t, float b, float c, float d) @inline => c * math::sin(t / d * (float)math::PI_2) + b; -fn float sine_inout(float t, float b, float c, float d) @inline => (-c / 2) * (math::cos((float)math::PI * t / d) - 1) + b; - -// Circular Easing functions -fn float circ_in(float t, float b, float c, float d) @inline => -c * (math::sqrt(1 - sq(t / d)) - 1) + b; -fn float circ_out(float t, float b, float c, float d) @inline => c * math::sqrt(1 - sq(t / d - 1)) + b; -fn float circ_inout(float t, float b, float c, float d) @inline -{ - t /= d / 2; - return t < 1 - ? (-c / 2) * (math::sqrt(1 - sq(t)) - 1) + b - : (c / 2) * (math::sqrt(1 - sq(t - 2)) + 1) + b; -} - -// Cubic Easing functions -fn float cubic_in(float t, float b, float c, float d) @inline => c * cube(t / d) + b; -fn float cubic_out(float t, float b, float c, float d) @inline => c * (cube(t / d - 1) + 1) + b; -fn float cubic_inout(float t, float b, float c, float d) @inline -{ - t /= d / 2; - return t < 1 - ? (c / 2) * cube(t) + b - : c / 2 * (cube(t - 2) + 2) + b; -} - -// Quadratic Easing functions -fn float quad_in(float t, float b, float c, float d) @inline => c * sq(t / d) + b; -fn float quad_out(float t, float b, float c, float d) @inline -{ - t /= d; - return -c * t * (t - 2) + b; -} - -fn float quad_inout(float t, float b, float c, float d) @inline -{ - t /= d / 2; - return t < 1 - ? (c / 2) * sq(t) + b - : (-c / 2) * ((t - 1) * (t - 3) - 1) + b; -} - -// Exponential Easing functions -fn float expo_in(float t, float b, float c, float d) @inline => t ? b : c * math::pow(2.0f, 10 * (t / d - 1)) + b; -fn float expo_out(float t, float b, float c, float d) @inline -{ - return (t == d) ? b + c : c * (-math::pow(2.0f, -10 * t / d) + 1) + b; -} -fn float expo_inout(float t, float b, float c, float d) @inline // Ease: Exponential In Out -{ - if (t == 0) return b; - if (t == d) return b + c; - t /= d / 2; - return t < 1 - ? (c / 2) * math::pow(2.0f, 10 * (t - 1)) + b - : (c / 2) * (-math::pow(2.0f, -10 * (t - 1)) + 2) + b; -} - -// Back Easing functions -fn float back_in(float t, float b, float c, float d, float s = 1.70158f) @inline -{ - t /= d; - return c * sq(t) * ((s + 1) * t - s) + b; -} - -fn float back_out(float t, float b, float c, float d, float s = 1.70158f) @inline -{ - t = t / d - 1; - return c * (sq(t) * ((s + 1) * t + s) + 1) + b; -} - -fn float back_inout(float t, float b, float c, float d, float s = 1.70158f) @inline -{ - s *= 1.525f; - t /= d / 2; - if (t < 1) - { - return (c / 2) * sq(t) * ((s + 1) * t - s) + b; - } - t -= 2.0f; - return (c / 2) * (sq(t) * ((s + 1) * t + s) + 2) + b; -} - -// Bounce Easing functions -fn float bounce_out(float t, float b, float c, float d) @inline -{ - t /= d; - switch - { - case t < 1 / 2.75f: - return c * 7.5625f * sq(t) + b; - case t < 2 / 2.75f: - t -= 1.5f / 2.75f; - return c * (7.5625f * sq(t) + 0.75f) + b; - case t < 2.5f / 2.75f: - t -= 2.25f / 2.75f; - return c * (7.5625f * sq(t) + 0.9375f) + b; - default: - t -= 2.625f / 2.75f; - return c * (7.5625f * sq(t) + 0.984375f) + b; - } -} - -fn float bounce_in(float t, float b, float c, float d) @inline => c - bounce_out(d - t, 0, c, d) + b; -fn float bounce_inout(float t, float b, float c, float d) @inline -{ - return t < d / 2 - ? bounce_in(t * 2, 0, c, d) * 0.5f + b - : bounce_out(t * 2 - d, 0, c, d) * 0.5f + b; -} - -// Elastic Easing functions -fn float elastic_in(float t, float b, float c, float d) @inline -{ - if (t == 0) return b; - t /= d; - if (t == 1) return b + c; - - float p = d * 0.3f; - float a = c; - float s = p / 4; - t -= 1; - return -a * math::pow(2.0f, 10 * t) * math::sin((t * d - s) * (2 * (float)math::PI) / p) + b; -} - -fn float elastic_out(float t, float b, float c, float d) @inline -{ - if (t == 0) return b; - t /= d; - if (t == 1) return b + c; - - float p = d * 0.3f; - float a = c; - float s = p / 4; - - return a * math::pow(2.0f, -10 * t) * math::sin((t * d - s) * (2 * (float)math::PI) / p) + c + b; -} - -fn float elastic_inout(float t, float b, float c, float d) @inline -{ - if (t == 0) return b; - t /= d / 2; - if (t == 2) return b + c; - - float p = d * (0.3f * 1.5f); - float a = c; - float s = p / 4; - - t -= 1; - return t < 0 - ? -0.5f * a * math::pow(2.0f, 10 * t) * math::sin((t * d - s) * (2 * (float)math::PI)/p) + b - : a * math::pow(2.0f, -10 * t) * math::sin((t * d - s) * (2 * (float)math::PI) / p) * 0.5f + c + b; -} - -macro sq(x) @private => x * x; -macro cube(x) @private => x * x * x; - diff --git a/lib7/std/math/math_i128.c3 b/lib7/std/math/math_i128.c3 deleted file mode 100644 index eeedfeb2b..000000000 --- a/lib7/std/math/math_i128.c3 +++ /dev/null @@ -1,510 +0,0 @@ -module std::math; - -fn int128 __divti3(int128 a, int128 b) @extern("__divti3") @weak @nostrip -{ - int128 sign_a = a >> 127; // -1 : 0 - int128 sign_b = b >> 127; // -1 : 0 - uint128 unsigned_a = (uint128)(a ^ sign_a) + (-sign_a); - uint128 unsigned_b = (uint128)(b ^ sign_b) + (-sign_b); - sign_a ^= sign_b; // quotient sign - return __udivti3(unsigned_a, unsigned_b) @inline ^ sign_a + (-sign_a); -} - -macro uint128 @__udivmodti4(uint128 a, uint128 b, bool $return_rem) -{ - Int128bits n = { .all = a }; - Int128bits d = { .all = b }; - Int128bits q @noinit; - Int128bits r @noinit; - uint sr; - if (n.high == 0) - { - if (d.high == 0) - { - $if $return_rem: - return n.low % d.low; - $else - return n.low / d.low; - $endif - } - $if $return_rem: - return n.low; - $else - return 0; - $endif - } - if (d.low == 0) - { - if (d.high == 0) - { - $if $return_rem: - return n.high % d.low; - $else - return n.high / d.low; - $endif - } - if (n.low == 0) - { - $if $return_rem: - r.high = n.high % d.high; - r.low = 0; - return r.all; - $else - return n.high / d.high; - $endif - } - if (d.high & (d.high - 1) == 0) // d is pot - { - $if $return_rem: - r.low = n.low; - r.high = n.high & (d.high - 1); - return r.all; - $else - return (uint128)(n.high >> $$ctz(d.high)); - $endif - } - sr = (uint)$$clz(d.high) - (uint)$$clz(n.high); - // 0 <= sr <= n_udword_bits - 2 or sr large - if (sr > 64 - 2) - { - $if $return_rem: - return n.all; - $else - return 0; - $endif - } - sr++; - // 1 <= sr <= n_udword_bits - 1 - // q.all = n.all << (n_utword_bits - sr); - q.low = 0; - q.high = n.low << (64 - sr); - r.high = n.high >> sr; - r.low = (n.high << (64 - sr)) | (n.low >> sr); - } - else // d.s.low != 0 - { - if (d.high == 0) - { - if (d.low & (d.low - 1) == 0) // if d is a power of 2 - { - $if $return_rem: - return (uint128)(n.low & (d.low - 1)); - $else - if (d.low == 1) return n.all; - sr = (uint)$$ctz(d.low); - q.high = n.high >> sr; - q.low = (n.high << (64 - sr)) | (n.low >> sr); - return q.all; - $endif - } - sr = 1 + 64 + (uint)$$clz(d.low) - (uint)$$clz(n.high); - // 2 <= sr <= n_utword_bits - 1 - // q.all = n.all << (n_utword_bits - sr); - // r.all = n.all >> sr; - switch - { - case sr == 64: - q.low = 0; - q.high = n.low; - r.high = 0; - r.low = n.high; - case sr < 64: - q.low = 0; - q.high = n.low << (64 - sr); - r.high = n.high >> sr; - r.low = (n.high << (64 - sr)) | (n.low >> sr); - default: // n_udword_bits + 1 <= sr <= n_utword_bits - 1 - q.low = n.low << (128 - sr); - q.high = (n.high << (128 - sr)) | (n.low >> (sr - 64)); - r.high = 0; - r.low = n.high >> (sr - 64); - } - } - else - { - sr = (uint)$$clz(d.high) - (uint)$$clz(n.high); - // 0 <= sr <= n_udword_bits - 1 or sr large - if (sr > 64 - 1) - { - $if $return_rem: - return n.all; - $else - return 0; - $endif - } - - sr++; - // 1 <= sr <= n_udword_bits - // q.all = n.all << (n_utword_bits - sr); - // r.all = n.all >> sr; - q.low = 0; - if (sr == 64) - { - q.high = n.low; - r.high = 0; - r.low = n.high; - } - else - { - r.high = n.high >> sr; - r.low = (n.high << (64 - sr)) | (n.low >> sr); - q.high = n.low << (64 - sr); - } - } - } - // Not a special case - // q and r are initialized with: - // q.all = n.all << (128 - sr); - // r.all = n.all >> sr; - // 1 <= sr <= n_utword_bits - 1 - uint carry = 0; - for (; sr > 0; sr--) - { - // r:q = ((r:q) << 1) | carry - r.high = (r.high << 1) | (r.low >> (64 - 1)); - r.low = (r.low << 1) | (q.high >> (64 - 1)); - q.high = (q.high << 1) | (q.low >> (64 - 1)); - q.low = (q.low << 1) | carry; - // carry = 0; - // if (r.all >= d.all) - // { - // r.all -= d.all; - // carry = 1; - // } - int128 s = (int128)(d.all - r.all - 1) >> (128 - 1); - carry = (uint)(s & 1); - r.all -= d.all & s; - } - $if $return_rem: - return r.all; - $else - return (q.all << 1) | carry; - $endif -} - -fn uint128 __umodti3(uint128 n, uint128 d) @extern("__umodti3") @weak @nostrip -{ - return @__udivmodti4(n, d, true); -} - -fn uint128 __udivti3(uint128 n, uint128 d) @extern("__udivti3") @weak @nostrip -{ - return @__udivmodti4(n, d, false); -} - -fn int128 __modti3(int128 a, int128 b) @extern("__modti3") @weak @nostrip -{ - int128 sign = b >> 127; - uint128 unsigned_b = (uint128)(b ^ sign) + (-sign); - sign = a >> 127; - uint128 unsigned_a = (uint128)(a ^ sign) + (-sign); - - return __umodti3(unsigned_a, unsigned_b) ^ sign + (-sign); -} - -union Int128bits @private -{ - struct - { - ulong low; - ulong high; - } - uint128 all; -} - -fn uint128 __lshrti3(uint128 a, uint b) @extern("__lshrti3") @weak @nostrip -{ - Int128bits result; - result.all = a; - if (b >= 64) - { - result.low = result.high >> (b - 64); - result.high = 0; - } - else - { - if (b == 0) return a; - result.low = (result.high << (64 - b)) | (result.low >> b); - result.high = result.high >> b; - } - return result.all; -} - -fn int128 __ashrti3(int128 a, uint b) @extern("__ashrti3") @weak @nostrip -{ - Int128bits result; - result.all = a; - if (b >= 64) - { - result.low = result.high >> (b - 64); - result.high = result.high >> 63; - } - else - { - if (b == 0) return a; - result.low = result.high << (64 - b) | (result.low >> b); - result.high = result.high >> b; - } - return result.all; -} - -fn int128 __ashlti3(int128 a, uint b) @extern("__ashlti3") @weak @nostrip -{ - Int128bits result; - result.all = a; - if (b >= 64) - { - result.low = 0; - result.high = result.low << (b - 64); - } - else - { - if (b == 0) return a; - result.high = (result.high << b) | (result.low >> (64 - b)); - result.low = result.low << b; - } - return result.all; -} - -// Returns: a * b - -fn int128 __mulddi3(ulong a, ulong b) @private -{ - Int128bits r; - const ulong LOWER_MASK = 0xffff_ffff; - r.low = (a & LOWER_MASK) * (b & LOWER_MASK); - ulong t = r.low >> 32; - r.low &= LOWER_MASK; - t += (a >> 32) * (b & LOWER_MASK); - r.low += (t & LOWER_MASK) << 32; - r.high = t >> 32; - t = r.low >> 32; - r.low &= LOWER_MASK; - t += (b >> 32) * (a & LOWER_MASK); - r.low += (t & LOWER_MASK) << 32; - r.high += t >> 32; - r.high += (a >> 32) * (b >> 32); - return r.all; -} - -fn int128 __multi3(int128 a, int128 b) @extern("__multi3") @weak @nostrip -{ - Int128bits x = { .all = a }; - Int128bits y = { .all = b }; - Int128bits r = { .all = __mulddi3(x.low, y.low) }; - r.high += x.high * y.low + x.low * y.high; - return r.all; -} - -fn float __floattisf(int128 a) @extern("__floattisf") @weak @nostrip => float_from_i128(float, a); -fn double __floattidf(int128 a) @extern("__floattidf") @weak @nostrip => float_from_i128(double, a); -fn float __floatuntisf(uint128 a) @extern("__floatuntisf") @weak @nostrip => float_from_u128(float, a); -fn double __floatuntidf(uint128 a) @extern("__floatuntidf") @weak @nostrip => float_from_u128(double, a); -fn uint128 __fixunsdfti(double a) @weak @extern("__fixunsdfti") @nostrip => fixuint(a); -fn uint128 __fixunssfti(float a) @weak @extern("__fixunssfti") @nostrip => fixuint(a); -fn int128 __fixdfti(double a) @weak @extern("__fixdfti") @nostrip => fixint(a); -fn int128 __fixsfti(float a) @weak @extern("__fixsfti") @nostrip => fixint(a); - - -macro float_from_i128($Type, a) @private -{ - var $Rep; - $switch ($Type) - $case double: - $Rep = ulong; - const MANT_DIG = DOUBLE_MANT_DIG; - const SIGNIFICANT_BITS = 52; - const EXP_BIAS = 1023; - const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64; - const SIGN_BIT = 1u64 << 63; - $case float: - $Rep = uint; - const MANT_DIG = FLOAT_MANT_DIG; - const EXP_BIAS = 127; - const SIGNIFICANT_BITS = 23; - const MANTISSA_MASK = 0x7F_FFFFu32; - const SIGN_BIT = 1u32 << 31; - $case float16: - $Rep = ushort; - const MANT_DIG = HALF_MANT_DIG; - $case float128: - $Rep = uint128; - const MANT_DIG = QUAD_MANT_DIG; - $endswitch - if (a == 0) return ($Type)0; - // Grab and remove sign. - int128 sign = a >> 127; - a = (a ^ sign) - sign; - int sd = 128 - (int)$$clz(a); // digits - int e = sd - 1; // exponent - if (sd > MANT_DIG) - { - switch (sd) - { - case MANT_DIG + 1: - a <<= 1; - case MANT_DIG + 2: - break; - default: - a = (a >> (sd - (MANT_DIG + 2))) - | (uint128)((a & ((uint128)(-1) >> ((128 + MANT_DIG + 2) - sd))) != 0); - } - a |= (uint128)((a & 4) != 0); - a++; - a >>= 2; - if (a & (1i128 << MANT_DIG)) - { - a >>= 1; - e++; - } - } - else - { - a <<= (MANT_DIG - sd); - } - return bitcast((($Rep)sign & SIGN_BIT) | ((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type); -} - -macro float_from_u128($Type, a) @private -{ - var $Rep; - $switch ($Type) - $case double: - $Rep = ulong; - const MANT_DIG = DOUBLE_MANT_DIG; - const SIGNIFICANT_BITS = 52; - const EXP_BIAS = 1023; - const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64; - $case float: - $Rep = uint; - const MANT_DIG = FLOAT_MANT_DIG; - const EXP_BIAS = 127; - const SIGNIFICANT_BITS = 23; - const MANTISSA_MASK = 0x7F_FFFFu32; - $case float16: - $Rep = ushort; - const MANT_DIG = HALF_MANT_DIG; - $case float128: - $Rep = uint128; - const MANT_DIG = QUAD_MANT_DIG; - $endswitch - if (a == 0) return ($Type)0; - int sd = 128 - (int)$$clz(a); // digits - int e = sd - 1; // exponent - if (sd > MANT_DIG) - { - switch (sd) - { - case MANT_DIG + 1: - a <<= 1; - case MANT_DIG + 2: - break; - default: - a = (a >> (sd - (MANT_DIG + 2))) - | (uint128)((a & ((uint128)(-1) >> ((128 + MANT_DIG + 2) - sd))) != 0); - } - a |= (uint128)((a & 4) != 0); - a++; - a >>= 2; - if (a & (1i128 << MANT_DIG)) - { - a >>= 1; - e++; - } - } - else - { - a <<= (MANT_DIG - sd); - } - return bitcast(((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type); -} - - -macro fixuint(a) @private -{ - var $Rep; - $switch ($typeof(a)) - $case double: - $Rep = ulong; - const EXPONENT_BITS = 11; - const SIGNIFICANT_BITS = 52; - $case float: - $Rep = uint; - const EXPONENT_BITS = 8; - const SIGNIFICANT_BITS = 23; - $case float16: - $Rep = ushort; - const EXPONENT_BITS = 5; - const SIGNIFICANT_BITS = 10; - $case float128: - $Rep = uint128; - const EXPONENT_BITS = 15; - const SIGNIFICANT_BITS = 112; - $endswitch - const $Rep MAX_EXPONENT = ($Rep)1 << EXPONENT_BITS - 1u; - const $Rep EXPONENT_BIAS = MAX_EXPONENT >> 1u; - const $Rep ONE_REP =EXPONENT_BIAS << SIGNIFICANT_BITS; - const $Rep SIGN_BIT = ($Rep)1 << (EXPONENT_BITS + SIGNIFICANT_BITS); - const $Rep ABS_MASK = SIGN_BIT - 1u; - const $Rep IMPLICIT_BIT = ($Rep)1 << SIGNIFICANT_BITS; - const $Rep SIGNIFICANT_MASK = IMPLICIT_BIT - 1u; - const $Rep EXPONENT_MASK = ABS_MASK ^ SIGNIFICANT_MASK; - const $Rep QUIET_BIT = IMPLICIT_BIT >> 1; - const $Rep QNAN_REP = EXPONENT_MASK | QUIET_BIT; - const $Rep INF_REP = EXPONENT_MASK; - - $Rep rep = bitcast(a, $Rep); - $Rep abs = rep & ABS_MASK; - int sign = rep & SIGN_BIT ? -1 : 1; - int exponent = (int)((abs >> SIGNIFICANT_BITS) - EXPONENT_BIAS); - $Rep significand = (abs & SIGNIFICANT_MASK) | IMPLICIT_BIT; - if (sign == -1 || exponent < 0) return 0u128; - if ((uint)exponent >= uint128.sizeof * 8) return ~0u128; - if (exponent < SIGNIFICANT_BITS) return (uint128)significand >> (SIGNIFICANT_BITS - exponent); - return (uint128)significand << (exponent - SIGNIFICANT_BITS); -} - -macro fixint(a) @private -{ - var $Rep; - $switch ($typeof(a)) - $case double: - $Rep = ulong; - const EXPONENT_BITS = 11; - const SIGNIFICANT_BITS = 52; - $case float: - $Rep = uint; - const EXPONENT_BITS = 8; - const SIGNIFICANT_BITS = 23; - $case float16: - $Rep = ushort; - const EXPONENT_BITS = 5; - const SIGNIFICANT_BITS = 10; - $case float128: - $Rep = uint128; - const EXPONENT_BITS = 15; - const SIGNIFICANT_BITS = 112; - $endswitch - const $Rep MAX_EXPONENT = ($Rep)1 << EXPONENT_BITS - 1u; - const $Rep EXPONENT_BIAS = MAX_EXPONENT >> 1u; - const $Rep ONE_REP = EXPONENT_BIAS << SIGNIFICANT_BITS; - const $Rep SIGN_BIT = ($Rep)1 << (EXPONENT_BITS + SIGNIFICANT_BITS); - const $Rep ABS_MASK = SIGN_BIT - 1u; - const $Rep IMPLICIT_BIT = ($Rep)1 << SIGNIFICANT_BITS; - const $Rep SIGNIFICANT_MASK = IMPLICIT_BIT - 1u; - const $Rep EXPONENT_MASK = ABS_MASK ^ SIGNIFICANT_MASK; - const $Rep QUIET_BIT = IMPLICIT_BIT >> 1; - const $Rep QNAN_REP = EXPONENT_MASK | QUIET_BIT; - const $Rep INF_REP = EXPONENT_MASK; - - $Rep rep = bitcast(a, $Rep); - $Rep abs = rep & ABS_MASK; - int sign = rep & SIGN_BIT ? -1 : 1; - int exponent = (int)((abs >> SIGNIFICANT_BITS) - EXPONENT_BIAS); - $Rep significand = (abs & SIGNIFICANT_MASK) | IMPLICIT_BIT; - if (exponent < 0) return 0; - if ((uint)exponent >= uint128.sizeof * 8) return sign == 1 ? int128.max : int128.min; - - if (exponent < SIGNIFICANT_BITS) return sign * ((int128)significand >> (SIGNIFICANT_BITS - exponent)); - return sign * ((int128)significand << (exponent - SIGNIFICANT_BITS)); -} diff --git a/lib7/std/math/math_libc.c3 b/lib7/std/math/math_libc.c3 deleted file mode 100644 index 0f467c540..000000000 --- a/lib7/std/math/math_libc.c3 +++ /dev/null @@ -1,47 +0,0 @@ -/* origin: FreeBSD /usr/src/lib/msun/src/s_atan.c - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* atan(x) - * Method - * 1. Reduce x to positive by atan(x) = -atan(-x). - * 2. According to the integer k=4t+0.25 chopped, t=x, the argument - * is further reduced to one of the following intervals and the - * arctangent of t is evaluated by the corresponding formula: - * - * [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...) - * [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) ) - * [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) ) - * [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) ) - * [39/16,INF] atan(x) = atan(INF) + atan( -1/t ) - * - * Constants: - * The hexadecimal values are the intended ones for the following - * constants. The decimal values may be used, provided that the - * compiler will convert from decimal to binary accurately enough - * to produce the hexadecimal values shown. - */ -/* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -module std::math; - - - diff --git a/lib7/std/math/math_matrix.c3 b/lib7/std/math/math_matrix.c3 deleted file mode 100644 index afd060aff..000000000 --- a/lib7/std/math/math_matrix.c3 +++ /dev/null @@ -1,450 +0,0 @@ -module std::math::matrix{Real}; -import std::math::vector; - -struct Matrix2x2 -{ - union - { - struct - { - Real m00, m01; - Real m10, m11; - } - Real[4] m; - } -} - -struct Matrix3x3 -{ - union - { - struct - { - Real m00, m01, m02; - Real m10, m11, m12; - Real m20, m21, m22; - } - Real[9] m; - } -} - -struct Matrix4x4 -{ - union - { - struct - { - Real m00, m01, m02, m03; - Real m10, m11, m12, m13; - Real m20, m21, m22, m23; - Real m30, m31, m32, m33; - } - Real[16] m; - } -} - -fn Real[<2>] Matrix2x2.apply(&self, Real[<2>] vec) -{ - return { - self.m00 * vec[0] + self.m01 * vec[1], - self.m10 * vec[0] + self.m11 * vec[1], - }; -} - -fn Real[<3>] Matrix3x3.apply(&self, Real[<3>] vec) -{ - return { - self.m00 * vec[0] + self.m01 * vec[1] + self.m02 * vec[2], - self.m10 * vec[0] + self.m11 * vec[1] + self.m12 * vec[2], - self.m20 * vec[0] + self.m21 * vec[1] + self.m22 * vec[2], - }; -} - -fn Real[<4>] Matrix4x4.apply(&self, Real[<4>] vec) -{ - return { - self.m00 * vec[0] + self.m01 * vec[1] + self.m02 * vec[2] + self.m03 * vec[3], - self.m10 * vec[0] + self.m11 * vec[1] + self.m12 * vec[2] + self.m13 * vec[3], - self.m20 * vec[0] + self.m21 * vec[1] + self.m22 * vec[2] + self.m23 * vec[3], - self.m30 * vec[0] + self.m31 * vec[1] + self.m32 * vec[2] + self.m33 * vec[3], - }; -} - - -fn Matrix2x2 Matrix2x2.mul(&self, Matrix2x2 b) -{ - return { - self.m00 * b.m00 + self.m01 * b.m10, self.m00 * b.m01 + self.m01 * b.m11, - self.m10 * b.m00 + self.m11 * b.m10, self.m10 * b.m01 + self.m11 * b.m11, - }; -} - -fn Matrix3x3 Matrix3x3.mul(&self, Matrix3x3 b) -{ - return { - self.m00 * b.m00 + self.m01 * b.m10 + self.m02 * b.m20, - self.m00 * b.m01 + self.m01 * b.m11 + self.m02 * b.m21, - self.m00 * b.m02 + self.m01 * b.m12 + self.m02 * b.m22, - - self.m10 * b.m00 + self.m11 * b.m10 + self.m12 * b.m20, - self.m10 * b.m01 + self.m11 * b.m11 + self.m12 * b.m21, - self.m10 * b.m02 + self.m11 * b.m12 + self.m12 * b.m22, - - self.m20 * b.m00 + self.m21 * b.m10 + self.m22 * b.m20, - self.m20 * b.m01 + self.m21 * b.m11 + self.m22 * b.m21, - self.m20 * b.m02 + self.m21 * b.m12 + self.m22 * b.m22, - }; -} - -fn Matrix4x4 Matrix4x4.mul(Matrix4x4* a, Matrix4x4 b) -{ - return { - a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20 + a.m03 * b.m30, - a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21 + a.m03 * b.m31, - a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22 + a.m03 * b.m32, - a.m00 * b.m03 + a.m01 * b.m13 + a.m02 * b.m23 + a.m03 * b.m33, - - a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20 + a.m13 * b.m30, - a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31, - a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32, - a.m10 * b.m03 + a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33, - - a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20 + a.m23 * b.m30, - a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31, - a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32, - a.m20 * b.m03 + a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33, - - a.m30 * b.m00 + a.m31 * b.m10 + a.m32 * b.m20 + a.m33 * b.m30, - a.m30 * b.m01 + a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31, - a.m30 * b.m02 + a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32, - a.m30 * b.m03 + a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33, - }; -} - -fn Matrix2x2 Matrix2x2.component_mul(&self, Real s) => matrix_component_mul(self, s); -fn Matrix3x3 Matrix3x3.component_mul(&self, Real s) => matrix_component_mul(self, s); -fn Matrix4x4 Matrix4x4.component_mul(&self, Real s) => matrix_component_mul(self, s); - -fn Matrix2x2 Matrix2x2.add(&self, Matrix2x2 mat2) => matrix_add(self, mat2); -fn Matrix3x3 Matrix3x3.add(&self, Matrix3x3 mat2) => matrix_add(self, mat2); -fn Matrix4x4 Matrix4x4.add(&self, Matrix4x4 mat2) => matrix_add(self, mat2); - -fn Matrix2x2 Matrix2x2.sub(&self, Matrix2x2 mat2) => matrix_sub(self, mat2); -fn Matrix3x3 Matrix3x3.sub(&self, Matrix3x3 mat2) => matrix_sub(self, mat2); -fn Matrix4x4 Matrix4x4.sub(&self, Matrix4x4 mat2) => matrix_sub(self, mat2); - -fn Matrix4x4 look_at(Real[<3>] eye, Real[<3>] target, Real[<3>] up) => matrix_look_at(Matrix4x4, eye, target, up); - - -fn Matrix2x2 Matrix2x2.transpose(&self) -{ - return { - self.m00, self.m10, - self.m01, self.m11 - }; -} - -fn Matrix3x3 Matrix3x3.transpose(&self) -{ - return { - self.m00, self.m10, self.m20, - self.m01, self.m11, self.m21, - self.m02, self.m12, self.m22, - }; -} - -fn Matrix4x4 Matrix4x4.transpose(&self) -{ - return { - self.m00, self.m10, self.m20, self.m30, - self.m01, self.m11, self.m21, self.m31, - self.m02, self.m12, self.m22, self.m32, - self.m03, self.m13, self.m23, self.m33, - }; -} - - -fn Real Matrix2x2.determinant(&self) -{ - return self.m00 * self.m11 - self.m01 * self.m10; -} - -fn Real Matrix3x3.determinant(&self) -{ - return - self.m00 * (self.m11 * self.m22 - self.m21 * self.m12) - - self.m01 * (self.m10 * self.m22 - self.m20 * self.m12) + - self.m02 * (self.m10 * self.m21 - self.m20 * self.m11); -} - -fn Real Matrix4x4.determinant(&self) -{ - return - self.m00 * (self.m11 * (self.m22 * self.m33 - self.m32 * self.m23) - - self.m12 * (self.m21 * self.m33 - self.m31 * self.m23) + - self.m13 * (self.m21 * self.m32 - self.m31 * self.m22) ) - - self.m01 * (self.m10 * (self.m22 * self.m33 - self.m32 * self.m23) - - self.m12 * (self.m20 * self.m33 - self.m30 * self.m23) + - self.m13 * (self.m20 * self.m32 - self.m30 * self.m22) ) + - self.m02 * (self.m10 * (self.m21 * self.m33 - self.m31 * self.m23) - - self.m11 * (self.m20 * self.m33 - self.m30 * self.m23) + - self.m13 * (self.m20 * self.m31 - self.m30 * self.m21) ) - - self.m03 * (self.m10 * (self.m21 * self.m32 - self.m31 * self.m22) - - self.m11 * (self.m20 * self.m32 - self.m30 * self.m22) + - self.m12 * (self.m20 * self.m31 - self.m30 * self.m21) ); -} - - -fn Matrix2x2 Matrix2x2.adjoint(&self) -{ - return { self.m11, -self.m01, -self.m10, self.m00 }; -} - -fn Matrix3x3 Matrix3x3.adjoint(&self) -{ - return { - (self.m11 * self.m22 - self.m21 * self.m12), - -(self.m10 * self.m22 - self.m20 * self.m12), - (self.m10 * self.m21 - self.m20 * self.m11), - - -(self.m01 * self.m22 - self.m21 * self.m02), - (self.m00 * self.m22 - self.m20 * self.m02), - -(self.m00 * self.m21 - self.m20 * self.m01), - - (self.m01 * self.m12 - self.m11 * self.m02), - -(self.m00 * self.m12 - self.m10 * self.m02), - (self.m00 * self.m11 - self.m10 * self.m01), - }; -} - -fn Matrix4x4 Matrix4x4.adjoint(&self) -{ - return { - (self.m11 * (self.m22 * self.m33 - self.m32 * self.m23) - - self.m12 * (self.m21 * self.m33 - self.m31 * self.m23) + - self.m13 * (self.m21 * self.m32 - self.m31 * self.m22)), - -(self.m10 * (self.m22 * self.m33 - self.m32 * self.m23) - - self.m12 * (self.m20 * self.m33 - self.m30 * self.m23) + - self.m13 * (self.m20 * self.m32 - self.m30 * self.m22)), - (self.m10 * (self.m21 * self.m33 - self.m31 * self.m23) - - self.m11 * (self.m20 * self.m33 - self.m30 * self.m23) + - self.m13 * (self.m20 * self.m31 - self.m30 * self.m21)), - -(self.m10 * (self.m21 * self.m32 - self.m31 * self.m22) - - self.m11 * (self.m20 * self.m32 - self.m30 * self.m22) + - self.m12 * (self.m20 * self.m31 - self.m30 * self.m21)), - - -(self.m01 * (self.m22 * self.m33 - self.m32 * self.m23) - - self.m02 * (self.m21 * self.m33 - self.m31 * self.m23) + - self.m03 * (self.m21 * self.m32 - self.m31 * self.m22)), - (self.m00 * (self.m22 * self.m33 - self.m32 * self.m23) - - self.m02 * (self.m20 * self.m33 - self.m30 * self.m23) + - self.m03 * (self.m20 * self.m32 - self.m30 * self.m22)), - -(self.m00 * (self.m21 * self.m33 - self.m31 * self.m23) - - self.m01 * (self.m20 * self.m33 - self.m30 * self.m23) + - self.m03 * (self.m20 * self.m31 - self.m30 * self.m21)), - (self.m00 * (self.m21 * self.m32 - self.m31 * self.m22) - - self.m01 * (self.m20 * self.m32 - self.m30 * self.m22) + - self.m02 * (self.m20 * self.m31 - self.m30 * self.m21)), - - (self.m01 * (self.m12 * self.m33 - self.m32 * self.m13) - - self.m02 * (self.m11 * self.m33 - self.m31 * self.m13) + - self.m03 * (self.m11 * self.m32 - self.m31 * self.m12)), - -(self.m00 * (self.m12 * self.m33 - self.m32 * self.m13) - - self.m02 * (self.m10 * self.m33 - self.m30 * self.m13) + - self.m03 * (self.m10 * self.m32 - self.m30 * self.m12)), - (self.m00 * (self.m11 * self.m33 - self.m31 * self.m13) - - self.m01 * (self.m10 * self.m33 - self.m30 * self.m13) + - self.m03 * (self.m10 * self.m31 - self.m30 * self.m11)), - -(self.m00 * (self.m11 * self.m32 - self.m31 * self.m12) - - self.m01 * (self.m10 * self.m32 - self.m30 * self.m12) + - self.m02 * (self.m10 * self.m31 - self.m30 * self.m11)), - - -(self.m01 * (self.m12 * self.m23 - self.m22 * self.m13) - - self.m02 * (self.m11 * self.m23 - self.m21 * self.m13) + - self.m03 * (self.m11 * self.m22 - self.m21 * self.m12)), - (self.m00 * (self.m12 * self.m23 - self.m22 * self.m13) - - self.m02 * (self.m10 * self.m23 - self.m20 * self.m13) + - self.m03 * (self.m10 * self.m22 - self.m20 * self.m12)), - -(self.m00 * (self.m11 * self.m23 - self.m21 * self.m13) - - self.m01 * (self.m10 * self.m23 - self.m20 * self.m13) + - self.m03 * (self.m10 * self.m21 - self.m20 * self.m11)), - (self.m00 * (self.m11 * self.m22 - self.m21 * self.m12) - - self.m01 * (self.m10 * self.m22 - self.m20 * self.m12) + - self.m02 * (self.m10 * self.m21 - self.m20 * self.m11)), - }; -} - - -fn Matrix2x2! Matrix2x2.inverse(&self) -{ - Real det = self.determinant(); - if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?; - Matrix2x2 adj = self.adjoint(); - return adj.component_mul(1 / det).transpose(); -} - -fn Matrix3x3! Matrix3x3.inverse(&self) -{ - Real det = self.determinant(); - if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?; - Matrix3x3 adj = self.adjoint(); - return adj.component_mul(1 / det).transpose(); -} - -fn Matrix4x4! Matrix4x4.inverse(&self) -{ - Real det = self.determinant(); - if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?; - Matrix4x4 adj = self.adjoint(); - return adj.component_mul(1 / det).transpose(); -} - - -fn Matrix3x3 Matrix3x3.translate(&self, Real[<2>] v) -{ - return self.mul({ - 1, 0, v[0], - 0, 1, v[1], - 0, 0, 1, - }); -} - -fn Matrix4x4 Matrix4x4.translate(&self, Real[<3>] v) -{ - return self.mul({ - 1, 0, 0, v[0], - 0, 1, 0, v[1], - 0, 0, 1, v[2], - 0, 0, 0, 1, - }); -} - -// r in radians -fn Matrix3x3 Matrix3x3.rotate(&self, Real r) -{ - return self.mul({ - math::cos(r), -math::sin(r), 0, - math::sin(r), math::cos(r), 0, - 0, 0, 1, - }); -} - -// r in radians -fn Matrix4x4 Matrix4x4.rotate_z(&self, Real r) -{ - return self.mul({ - math::cos(r), -math::sin(r), 0, 0, - math::sin(r), math::cos(r), 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, - }); -} - -// r in radians -fn Matrix4x4 Matrix4x4.rotate_y(&self, Real r) -{ - return self.mul({ - math::cos(r), 0, -math::sin(r), 0, - 0, 1, 0, 0, - math::sin(r), 0, math::cos(r), 0, - 0, 0, 0, 1, - }); -} - -// r in radians -fn Matrix4x4 Matrix4x4.rotate_x(&self, Real r) -{ - return self.mul({ - 1, 0, 0, 0, - 0, math::cos(r), -math::sin(r), 0, - 0, math::sin(r), math::cos(r), 0, - 0, 0, 0, 1, - }); -} - - -fn Matrix3x3 Matrix3x3.scale(&self, Real[<2>] v) -{ - return self.mul({ - v[0], 0, 0, - 0, v[1], 0, - 0, 0, 1, - }); -} - -fn Real Matrix2x2.trace(&self) => self.m00 + self.m11; -fn Real Matrix3x3.trace(&self) => self.m00 + self.m11 + self.m22; -fn Real Matrix4x4.trace(&self) => self.m00 + self.m11 + self.m22 + self.m33; - -fn Matrix4x4 Matrix4x4.scale(&self, Real[<3>] v) -{ - return self.mul({ - v[0], 0, 0, 0, - 0, v[1], 0, 0, - 0, 0, v[2], 0, - 0, 0, 0, 1, - }); -} - -fn Matrix4x4 ortho(Real left, Real right, Real top, Real bottom, Real near, Real far) -{ - Real width = right - left; - Real height = top - bottom; - Real depth = far - near; - return { - 2 / width, 0, 0, 0, - 0, 2 / height, 0, 0, - 0, 0, -2 / depth, 0, - -(right + left) / width, -(top + bottom) / height, -(far + near) / depth, 1 - }; -} - -// fov in radians -fn Matrix4x4 perspective(Real fov, Real aspect_ratio, Real near, Real far) -{ - Real f = (Real)math::tan(math::PI * 0.5 - 0.5 * fov); - Real range_inv = (Real)1.0 / (near - far); - - return { - f / aspect_ratio, 0, 0, 0, - 0, f, 0, 0, - 0, 0, (near + far) * range_inv, near * far * range_inv * 2, - 0, 0, -1, 0, - }; -} - -const Matrix2x2 IDENTITY2 = { .m = { [0] = 1, [3] = 1 } }; -const Matrix3x3 IDENTITY3 = { .m = { [0] = 1, [4] = 1, [8] = 1 } }; -const Matrix4x4 IDENTITY4 = { .m = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 } }; - -macro matrix_component_mul(mat, val) @private -{ - var $Type = Real[<$typeof(mat.m).len>]; - return ($typeof(*mat)) { .m = val * ($Type)mat.m }; -} - -macro matrix_add(mat, mat2) @private -{ - var $Type = Real[<$typeof(mat.m).len>]; - return ($typeof(*mat)) { .m = ($Type)mat.m + ($Type)mat2.m }; -} - -macro matrix_sub(mat, mat2) @private -{ - var $Type = Real[<$typeof(mat.m).len>]; - return ($typeof(*mat)) { .m = ($Type)mat.m - ($Type)mat2.m }; -} - -macro matrix_look_at($Type, eye, target, up) @private -{ - var vz = (eye - target).normalize(); - var vx = up.cross(vz).normalize(); - var vy = vz.cross(vx); - - return ($Type){ - vx[0], vx[1], vx[2], - (Real)vx.dot(eye), - vy[0], vy[1], vy[2], - (Real)vy.dot(eye), - vz[0], vz[1], vz[2], - (Real)vz.dot(eye), - 0.0, 0.0, 0.0, 1 - }; -} diff --git a/lib7/std/math/math_nolibc/__cos.c3 b/lib7/std/math/math_nolibc/__cos.c3 deleted file mode 100644 index 44df4c5c4..000000000 --- a/lib7/std/math/math_nolibc/__cos.c3 +++ /dev/null @@ -1,31 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD /usr/src/lib/msun/src/k_cos.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -fn double __cos(double x, double y) @extern("__cos") @weak @nostrip -{ - const C1 = 4.16666666666666019037e-02; /* 0x3FA55555, 0x5555554C */ - const C2 = -1.38888888888741095749e-03; /* 0xBF56C16C, 0x16C15177 */ - const C3 = 2.48015872894767294178e-05; /* 0x3EFA01A0, 0x19CB1590 */ - const C4 = -2.75573143513906633035e-07; /* 0xBE927E4F, 0x809C52AD */ - const C5 = 2.08757232129817482790e-09; /* 0x3E21EE9E, 0xBDB4B1C4 */ - const C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */ - - double z = x * x; - double w = z * z; - double r = z * (C1 + z * (C2 + z * C3)) + w * w * (C4 + z * (C5 + z * C6)); - double hz = 0.5 * z; - w = 1.0 - hz; - return w + (((1.0 - w) - hz) + (z * r - x * y)); -} - diff --git a/lib7/std/math/math_nolibc/__cosdf.c3 b/lib7/std/math/math_nolibc/__cosdf.c3 deleted file mode 100644 index 08e519f4a..000000000 --- a/lib7/std/math/math_nolibc/__cosdf.c3 +++ /dev/null @@ -1,32 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD /usr/src/lib/msun/src/k_cosf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - * Debugged and optimized by Bruce D. Evans. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* |cos(x) - c(x)| < 2**-34.1 (~[-5.37e-11, 5.295e-11]). */ - -const double C0 @private = -0x1ffffffd0c5e81.0p-54; /* -0.499999997251031003120 */ -const double C1 @private = 0x155553e1053a42.0p-57; /* 0.0416666233237390631894 */ -const double C2 @private = -0x16c087e80f1e27.0p-62; /* -0.00138867637746099294692 */ -const double C3 @private = 0x199342e0ee5069.0p-68; /* 0.0000243904487962774090654 */ - -fn float __cosdf(double x) @extern("__cosdf") @weak @nostrip -{ - /* Try to optimize for parallel evaluation as in __tandf.c. */ - double z = x * x; - double w = z * z; - double r = C2 + z * C3; - return (float)(((1.0f + z * C0) + w * C1) + (w * z) * r); -} diff --git a/lib7/std/math/math_nolibc/__fmod.c3 b/lib7/std/math/math_nolibc/__fmod.c3 deleted file mode 100644 index 432bd0cf9..000000000 --- a/lib7/std/math/math_nolibc/__fmod.c3 +++ /dev/null @@ -1,154 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -union DoubleInternal -{ - double f; - ulong i; -} - -union FloatInternal -{ - float f; - uint i; -} - -// Based on the musl implementation -fn double fmod(double x, double y) @extern("fmod") @weak @nostrip -{ - DoubleInternal ux = { .f = x }; - DoubleInternal uy = { .f = y }; - int ex = (int)((ux.i >> 52) & 0x7ff); - int ey = (int)((uy.i >> 52) & 0x7ff); - int sx = (int)(ux.i >> 63); - ulong uxi = ux.i; - if (uy.i << 1 == 0 || math::is_nan(y) || ex == 0x7ff) return (x * y)/(x * y); - if (uxi << 1 <= uy.i << 1) - { - if (uxi << 1 == uy.i << 1) return 0 * x; - return x; - } - - if (!ex) - { - for (ulong i = uxi << 12; i >> 63 == 0; ex--, i <<= 1); - uxi <<= -ex + 1; - } - else - { - uxi &= -1UL >> 12; - uxi |= 1UL << 52; - } - if (!ey) - { - for (ulong i = uy.i << 12; i >> 63 == 0; ey--, i <<= 1); - uy.i <<= -ey + 1; - } - else - { - uy.i &= -1UL >> 12; - uy.i |= 1UL << 52; - } - - /* x mod y */ - for (; ex > ey; ex--) - { - ulong i = uxi - uy.i; - if (i >> 63 == 0) - { - if (i == 0) return 0 * x; - uxi = i; - } - uxi <<= 1; - } - ulong i = uxi - uy.i; - if (i >> 63 == 0) - { - if (i == 0) return 0*x; - uxi = i; - } - for (; uxi>>52 == 0; uxi <<= 1, ex--); - - /* scale result */ - if (ex > 0) - { - uxi -= 1UL << 52; - uxi |= (ulong)ex << 52; - } - else - { - uxi >>= -ex + 1; - } - uxi |= (ulong)sx << 63; - ux.i = uxi; - return ux.f; -} - -fn float fmodf(float x, float y) @extern("fmodf") @weak @nostrip -{ - FloatInternal ux = { .f = x }; - FloatInternal uy = { .f = y }; - int ex = (int)((ux.i >> 23) & 0xff); - int ey = (int)((uy.i >> 23) & 0xff); - int sx = (int)(ux.i >> 31); - uint uxi = ux.i; - if (uy.i << 1 == 0 || math::is_nan(y) || ex == 0xff) return (x * y)/(x * y); - if (uxi << 1 <= uy.i << 1) - { - if (uxi << 1 == uy.i << 1) return 0 * x; - return x; - } - - if (!ex) - { - for (uint i = uxi << 9; i >> 31 == 0; ex--, i <<= 1); - uxi <<= -ex + 1; - } - else - { - uxi &= -1U >> 9; - uxi |= 1U << 23; - } - if (!ey) - { - for (uint i = uy.i << 9; i >> 31 == 0; ey--, i <<= 1); - uy.i <<= -ey + 1; - } - else - { - uy.i &= -1U >> 9; - uy.i |= 1U << 23; - } - - /* x mod y */ - for (; ex > ey; ex--) - { - uint i = uxi - uy.i; - if (i >> 31 == 0) - { - if (i == 0) return 0 * x; - uxi = i; - } - uxi <<= 1; - } - uint i = uxi - uy.i; - if (i >> 31 == 0) - { - if (i == 0) return 0*x; - uxi = i; - } - for (; uxi>>23 == 0; uxi <<= 1, ex--); - - /* scale result */ - if (ex > 0) - { - uxi -= 1U << 23; - uxi |= (uint)ex << 23; - } - else - { - uxi >>= -ex + 1; - } - uxi |= (uint)sx << 31; - ux.i = uxi; - return ux.f; -} diff --git a/lib7/std/math/math_nolibc/__sin.c3 b/lib7/std/math/math_nolibc/__sin.c3 deleted file mode 100644 index 2f9e0bf59..000000000 --- a/lib7/std/math/math_nolibc/__sin.c3 +++ /dev/null @@ -1,31 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD /usr/src/lib/msun/src/k_sin.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -fn double __sin(double x, double y, int iy) @extern("__sin") @weak @nostrip -{ - - const S1 = -1.66666666666666324348e-01; /* 0xBFC55555, 0x55555549 */ - const S2 = 8.33333333332248946124e-03; /* 0x3F811111, 0x1110F8A6 */ - const S3 = -1.98412698298579493134e-04; /* 0xBF2A01A0, 0x19C161D5 */ - const S4 = 2.75573137070700676789e-06; /* 0x3EC71DE3, 0x57B1FE7D */ - const S5 = -2.50507602534068634195e-08; /* 0xBE5AE5E6, 0x8A2B9CEB */ - const S6 = 1.58969099521155010221e-10; /* 0x3DE5D93A, 0x5ACFD57C */ - - double z = x * x; - double w = z * z; - double r = S2 + z * (S3 + z * S4) + z * w * (S5 + z * S6); - double v = z * x; - return iy == 0 - ? x + v * (S1 + z * r) - : x - ((z * (0.5 * y - v * r) - y) - v * S1); -} diff --git a/lib7/std/math/math_nolibc/__sindf.c3 b/lib7/std/math/math_nolibc/__sindf.c3 deleted file mode 100644 index caac7314b..000000000 --- a/lib7/std/math/math_nolibc/__sindf.c3 +++ /dev/null @@ -1,31 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD /usr/src/lib/msun/src/k_sinf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - * Optimized by Bruce D. Evans. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -// |sin(x)/x - s(x)| < 2**-37.5 (~[-4.89e-12, 4.824e-12]). -fn float __sindf(double x) @extern("__sindf") @weak @nostrip -{ - const S1F = -0x15555554cbac77.0p-55; /* -0.166666666416265235595 */ - const S2F = 0x111110896efbb2.0p-59; /* 0.0083333293858894631756 */ - const S3F = -0x1a00f9e2cae774.0p-65; /* -0.000198393348360966317347 */ - const S4F = 0x16cd878c3b46a7.0p-71; /* 0.0000027183114939898219064 */ - double z = x * x; - double w = z * z; - double r = S3F + z * S4F; - double s = z * x; - return (float)((x + s * (S1F + z * S2F)) + s * w * r); -} - diff --git a/lib7/std/math/math_nolibc/__tan.c3 b/lib7/std/math/math_nolibc/__tan.c3 deleted file mode 100644 index 44bb8adfd..000000000 --- a/lib7/std/math/math_nolibc/__tan.c3 +++ /dev/null @@ -1,79 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD /usr/src/lib/msun/src/k_tan.c */ -/* - * ==================================================== - * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. - * - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -const double[?] TAN_T = { - 3.33333333333334091986e-01, /* 3FD55555, 55555563 */ - 1.33333333333201242699e-01, /* 3FC11111, 1110FE7A */ - 5.39682539762260521377e-02, /* 3FABA1BA, 1BB341FE */ - 2.18694882948595424599e-02, /* 3F9664F4, 8406D637 */ - 8.86323982359930005737e-03, /* 3F8226E3, E96E8493 */ - 3.59207910759131235356e-03, /* 3F6D6D22, C9560328 */ - 1.45620945432529025516e-03, /* 3F57DBC8, FEE08315 */ - 5.88041240820264096874e-04, /* 3F4344D8, F2F26501 */ - 2.46463134818469906812e-04, /* 3F3026F7, 1A8D1068 */ - 7.81794442939557092300e-05, /* 3F147E88, A03792A6 */ - 7.14072491382608190305e-05, /* 3F12B80F, 32F0A7E9 */ - -1.85586374855275456654e-05, /* BEF375CB, DB605373 */ - 2.59073051863633712884e-05, /* 3EFB2A70, 74BF7AD4 */ -}; - -fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip -{ - const double PIO4 = 7.85398163397448278999e-01; /* 3FE921FB, 54442D18 */ - const double PIO4LO = 3.06161699786838301793e-17; /* 3C81A626, 33145C07 */ - - uint hx = x.high_word(); - bool big = (hx &0x7fffffff) >= 0x3FE59428; // |x| >= 0.6744 - int sign @noinit; - if (big) - { - sign = hx >> 31; - if (sign) - { - x = -x; - y = -y; - } - x = (PIO4 - x) + (PIO4LO - y); - y = 0.0; - } - double z = x * x; - double w = z * z; - /* - * Break x^5*(T[1]+x^2*T[2]+...) into - * x^5(T[1]+x^4*T[3]+...+x^20*T[11]) + - * x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12])) - */ - double r = TAN_T[1] + w * (TAN_T[3] + w * (TAN_T[5] + w * (TAN_T[7] + w * (TAN_T[9] + w * TAN_T[11])))); - double v = z * (TAN_T[2] + w * (TAN_T[4] + w * (TAN_T[6] + w * (TAN_T[8] + w * (TAN_T[10] + w * TAN_T[12]))))); - double s = z * x; - r = y + z * (s * (r + v) + y) + s * TAN_T[0]; - w = x + r; - if (big) - { - s = (double)(1 - 2 * odd); - v = s - 2.0 * (x + (r - w*w/(w + s))); - return sign ? -v : v; - } - if (!odd) return w; - // -1.0/(x+r) has up to 2ulp error, so compute it accurately - - // Clear low word - double w0 = w; - w0.set_low_word(0); - - v = r - (w0 - x); // w0+v = r+x - double a = -1.0 / w; - double a0 = a; - a0.set_low_word(0); - return a0 + a * (1.0 + a0 * w0 + a0 * v); -} diff --git a/lib7/std/math/math_nolibc/__tandf.c3 b/lib7/std/math/math_nolibc/__tandf.c3 deleted file mode 100644 index 8719ba32f..000000000 --- a/lib7/std/math/math_nolibc/__tandf.c3 +++ /dev/null @@ -1,52 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD /usr/src/lib/msun/src/k_tanf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - * Optimized by Bruce D. Evans. - */ -/* - * ==================================================== - * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. - * - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -// |tan(x)/x - t(x)| < 2**-25.5 (~[-2e-08, 2e-08]). -const double[?] TANDF = { - 0x15554d3418c99f.0p-54, /* 0.333331395030791399758 */ - 0x1112fd38999f72.0p-55, /* 0.133392002712976742718 */ - 0x1b54c91d865afe.0p-57, /* 0.0533812378445670393523 */ - 0x191df3908c33ce.0p-58, /* 0.0245283181166547278873 */ - 0x185dadfcecf44e.0p-61, /* 0.00297435743359967304927 */ - 0x1362b9bf971bcd.0p-59, /* 0.00946564784943673166728 */ -}; - -fn float __tandf(double x, int odd) @extern("__tandf") @weak @nostrip -{ - double z = x * x; - /* - * Split up the polynomial into small independent terms to give - * opportunities for parallel evaluation. The chosen splitting is - * micro-optimized for Athlons (XP, X64). It costs 2 multiplications - * relative to Horner's method on sequential machines. - * - * We add the small terms from lowest degree up for efficiency on - * non-sequential machines (the lowest degree terms tend to be ready - * earlier). Apart from this, we don't care about order of - * operations, and don't need to to care since we have precision to - * spare. However, the chosen splitting is good for accuracy too, - * and would give results as accurate as Horner's method if the - * small terms were added from highest degree down. - */ - double r = TANDF[4] + z * TANDF[5]; - double t = TANDF[2] + z * TANDF[3]; - double w = z * z; - double s = z * x; - double u = TANDF[0] + z * TANDF[1]; - r = (x + s * u) + (s * w) * (t + w * r); - return (float)(odd ? -1.0 / r : r); -} diff --git a/lib7/std/math/math_nolibc/acos.c3 b/lib7/std/math/math_nolibc/acos.c3 deleted file mode 100644 index 1245a24ff..000000000 --- a/lib7/std/math/math_nolibc/acos.c3 +++ /dev/null @@ -1,139 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -const PIO2_HI @local = 1.57079632679489655800e+00; /* 0x3FF921FB, 0x54442D18 */ -const PIO2_LO @local = 6.12323399573676603587e-17; /* 0x3C91A626, 0x33145C07 */ -const PS0 @local = 1.66666666666666657415e-01; /* 0x3FC55555, 0x55555555 */ -const PS1 @local = -3.25565818622400915405e-01; /* 0xBFD4D612, 0x03EB6F7D */ -const PS2 @local = 2.01212532134862925881e-01; /* 0x3FC9C155, 0x0E884455 */ -const PS3 @local = -4.00555345006794114027e-02; /* 0xBFA48228, 0xB5688F3B */ -const PS4 @local = 7.91534994289814532176e-04; /* 0x3F49EFE0, 0x7501B288 */ -const PS5 @local = 3.47933107596021167570e-05; /* 0x3F023DE1, 0x0DFDF709 */ -const QS1 @local = -2.40339491173441421878e+00; /* 0xC0033A27, 0x1C8A2D4B */ -const QS2 @local = 2.02094576023350569471e+00; /* 0x40002AE5, 0x9C598AC8 */ -const QS3 @local = -6.88283971605453293030e-01; /* 0xBFE6066C, 0x1B8D0159 */ -const QS4 @local = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */ - -fn double _r(double z) @local -{ - double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5))))); - double q = 1.0 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4))); - return p / q; -} - -fn double _acos(double x) @weak @extern("acos") @nostrip -{ - uint hx = x.high_word(); - uint ix = hx & 0x7fffffff; - switch - { - /* |x| >= 1 or nan */ - case ix >= 0x3ff00000: - uint lx = x.low_word(); - if ((ix - 0x3ff00000 | lx) == 0) - { - /* acos(1)=0, acos(-1)=pi */ - if (hx >> 31) return 2. * PIO2_HI + 0x1p-120f; - return 0.; - } - return double.nan; - /* |x| < 0.5 */ - case ix < 0x3fe00000: - /* |x| < 2**-57 */ - if (ix <= 0x3c600000) return PIO2_HI + 0x1p-120f; - return PIO2_HI - (x - (PIO2_LO - x * _r(x * x))); - /* x < -0.5 */ - case (hx >> 31) != 0: - double z = (1. + x) * 0.5; - double s = math::sqrt(z); - double w = _r(z) * s - PIO2_LO; - return 2. * (PIO2_HI - (s + w)); - /* x > 0.5 */ - default: - double z = (1. - x) * 0.5; - double s = math::sqrt(z); - double df = s; - df.set_low_word(0); - double c = (z - df * df) / (s + df); - double w = _r(z) * s + c; - return 2. * (df + w); - } -} - -/* origin: FreeBSD /usr/src/lib/msun/src/e_acosf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -const float PIO2_HI_F @local = 1.5707962513e+00; /* 0x3fc90fda */ -const float PIO2_LO_F @local = 7.5497894159e-08; /* 0x33a22168 */ -const float PS0_F @local = 1.6666586697e-01; -const float PS1_F @local = -4.2743422091e-02; -const float PS2_F @local = -8.6563630030e-03; -const float QS1_F @local = -7.0662963390e-01; - -fn float _r_f(float z) @local -{ - float p = z * ( PS0_F + z * (PS1_F + z * PS2_F)); - float q = 1.0f + z * QS1_F; - return p / q; -} - -fn float _acosf(float x) @weak @extern("acosf") @nostrip -{ - uint hx = bitcast(x, uint); - uint ix = hx & 0x7fffffff; - switch - { - /* |x| >= 1 or nan */ - case ix >= 0x3f800000: - if (ix == 0x3f800000) - { - if (hx >> 31) return 2.f * PIO2_HI_F + 0x1p-120f; - return 0; - } - return float.nan; - /* |x| < 0.5 */ - case ix < 0x3f000000: - /* |x| < 2**-26 */ - if (ix <= 0x32800000) return PIO2_HI_F + 0x1p-120f; - return PIO2_HI_F - (x - (PIO2_LO_F - x * _r_f(x * x))); - /* x < -0.5 */ - case (hx >> 31) != 0: - float z = (1.f + x) * 0.5f; - float s = math::sqrt(z); - float w = _r_f(z) * s - PIO2_LO_F; - return 2.f * (PIO2_HI_F - (s + w)); - /* x > 0.5 */ - default: - float z = (1.f - x) * 0.5f; - float s = math::sqrt(z); - float df = s; - uint idf = df.word(); - df.set_word(idf & 0xfffff000); - float c = (z - df * df) / (s + df); - float w = _r_f(z) * s + c; - return 2.f * (df + w); - } -} \ No newline at end of file diff --git a/lib7/std/math/math_nolibc/asin.c3 b/lib7/std/math/math_nolibc/asin.c3 deleted file mode 100644 index 281107c82..000000000 --- a/lib7/std/math/math_nolibc/asin.c3 +++ /dev/null @@ -1,132 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -const PIO2_HI @local = 1.57079632679489655800e+00; /* 0x3FF921FB, 0x54442D18 */ -const PIO2_LO @local = 6.12323399573676603587e-17; /* 0x3C91A626, 0x33145C07 */ -const PS0 @local = 1.66666666666666657415e-01; /* 0x3FC55555, 0x55555555 */ -const PS1 @local = -3.25565818622400915405e-01; /* 0xBFD4D612, 0x03EB6F7D */ -const PS2 @local = 2.01212532134862925881e-01; /* 0x3FC9C155, 0x0E884455 */ -const PS3 @local = -4.00555345006794114027e-02; /* 0xBFA48228, 0xB5688F3B */ -const PS4 @local = 7.91534994289814532176e-04; /* 0x3F49EFE0, 0x7501B288 */ -const PS5 @local = 3.47933107596021167570e-05; /* 0x3F023DE1, 0x0DFDF709 */ -const QS1 @local = -2.40339491173441421878e+00; /* 0xC0033A27, 0x1C8A2D4B */ -const QS2 @local = 2.02094576023350569471e+00; /* 0x40002AE5, 0x9C598AC8 */ -const QS3 @local = -6.88283971605453293030e-01; /* 0xBFE6066C, 0x1B8D0159 */ -const QS4 @local = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */ - -fn double _r(double z) @local -{ - double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z * (PS4 + z * PS5))))); - double q = 1.0 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4))); - return p / q; -} - -fn double _asin(double x) @weak @extern("asin") @nostrip -{ - uint hx = x.high_word(); - uint ix = hx & 0x7fffffff; - switch - { - /* |x| >= 1 or nan */ - case ix >= 0x3ff00000: - uint lx = x.low_word(); - if ((ix-0x3ff00000 | lx) == 0) - { - /* asin(1) = +-pi/2 with inexact */ - return x * PIO2_HI + 0x1p-120f; - } - return double.nan; - /* |x| < 0.5 */ - case ix < 0x3fe00000: - /* if 0x1p-1022 <= |x| < 0x1p-26, avoid raising underflow */ - if (ix < 0x3e500000 && ix >= 0x00100000) return x; - return x + x * _r(x * x); - /* 1 > |x| >= 0.5 */ - default: - double z = (1. - math::abs(x)) * 0.5; - double s = math::sqrt(z); - double r = _r(z); - /* if |x| > 0.975 */ - if (ix >= 0x3fef3333) - { - x = PIO2_HI - (2. * (s + s * r) - PIO2_LO); - } - else - { - double f = s; - f.set_low_word(0); - double c = (z - f * f) / (s + f); - x = 0.5 * PIO2_HI - (2. * s * r - (PIO2_LO - 2. * c) - (0.5 * PIO2_HI - 2. * f)); - } - if (hx >> 31 != 0) return -x; - return x; - } -} - -/* origin: FreeBSD /usr/src/lib/msun/src/e_asinf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -const PIO2 @local = 1.570796326794896558e+00; -const float PS0_F @local = 1.6666586697e-01; -const float PS1_F @local = -4.2743422091e-02; -const float PS2_F @local = -8.6563630030e-03; -const float QS1_F @local = -7.0662963390e-01; - -fn float _r_f(float z) @local -{ - float p = z * ( PS0_F + z * (PS1_F + z * PS2_F)); - float q = 1.0f + z * QS1_F; - return p / q; -} - -fn float _asinf(float x) @weak @extern("asinf") @nostrip -{ - uint hx = bitcast(x, uint); - uint ix = hx & 0x7fffffff; - switch - { - /* |x| >= 1 */ - case ix >= 0x3f800000: - if (ix == 0x3f800000) - { - /* asin(+-1) = +-pi/2 with inexact */ - return x * (float)PIO2 + 0x1p-120f; - } - return float.nan; - /* |x| < 0.5 */ - case ix < 0x3f000000: - /* if 0x1p-126 <= |x| < 0x1p-12, avoid raising underflow */ - if (ix < 0x39800000 && ix >= 0x00800000) return x; - return x + x * _r_f(x * x); - /* 1 > |x| >= 0.5 */ - default: - float z = (1.f - math::abs(x)) * 0.5f; - float s = math::sqrt(z); - x = (float)PIO2 - 2.f * (s + s * _r_f(z)); - if (hx >> 31) return -x; - return x; - } -} \ No newline at end of file diff --git a/lib7/std/math/math_nolibc/atan.c3 b/lib7/std/math/math_nolibc/atan.c3 deleted file mode 100644 index b2db3713c..000000000 --- a/lib7/std/math/math_nolibc/atan.c3 +++ /dev/null @@ -1,366 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD /usr/src/lib/msun/src/s_atan.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -const double[?] ATANHI @private = { - 4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */ - 7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */ - 9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */ - 1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */ -}; - -const double[?] ATANLO @private = { - 2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */ - 3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */ - 1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */ - 6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */ -}; - -const double[?] AT @private = { - 3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */ - -1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */ - 1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */ - -1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */ - 9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */ - -7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */ - 6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */ - -5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */ - 4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */ - -3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */ - 1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */ -}; - -fn double _atan(double x) @weak @extern("atan") @nostrip -{ - int id @noinit; - uint ix = x.high_word(); - uint sign = ix >> 31; - ix &= 0x7fffffff; - switch - { - case ix >= 0x44100000: - /* if |x| >= 2^66 */ - if (math::is_nan(x)) return x; - double z = ATANHI[3] + 0x1p-120f; - return sign ? -z : z; - case ix < 0x3fdc0000: - /* |x| < 0.4375 */ - if (ix < 0x3e400000) - { - /* |x| < 2^-27 */ - if (ix < 0x00100000) - { - /* raise underflow for subnormal x */ - (float)@volatile_load(x); - } - return x; - } - id = -1; - case ix < 0x3ff30000: - /* |x| < 1.1875 */ - x = math::abs(x); - if (ix < 0x3fe60000) - { - /* 7/16 <= |x| < 11/16 */ - id = 0; - x = (2 * x - 1) / (2 + x); - } - else - { /* 11/16 <= |x| < 19/16 */ - id = 1; - x = (x - 1) / (x + 1); - } - case ix < 0x40038000: - x = math::abs(x); - /* |x| < 2.4375 */ - id = 2; - x = (x - 1.5) / (1 + 1.5 * x); - default: - /* 2.4375 <= |x| < 2^66 */ - id = 3; - x = -1 / math::abs(x); - } - /* end of argument reduction */ - double z = x * x; - double w = z * z; - /* break sum from i=0 to 10 AT[i]z**(i+1) into odd and even poly */ - double s1 = z * (AT[0] + w * (AT[2] + w * (AT[4] + w * (AT[6] + w * (AT[8] + w * AT[10]))))); - double s2 = w * (AT[1] + w * (AT[3] + w * (AT[5] + w * (AT[7] + w * AT[9])))); - if (id < 0) return x - x * (s1 + s2); - z = ATANHI[id] - (x * (s1 + s2) - ATANLO[id] - x); - return sign ? -z : z; -} - -/* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -const float[?] ATANHIF @private = { - 4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */ - 7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */ - 9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */ - 1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */ -}; - -const float[?] ATANLOF @private = { - 5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */ - 3.7748947079e-08, /* atan(1.0)lo 0x33222168 */ - 3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */ - 7.5497894159e-08, /* atan(inf)lo 0x33a22168 */ -}; - -const float[?] ATF @private = { - 3.3333328366e-01, - -1.9999158382e-01, - 1.4253635705e-01, - -1.0648017377e-01, - 6.1687607318e-02, -}; - -fn float _atanf(float x) @weak @extern("atanf") @nostrip -{ - int id @noinit; - uint ix = x.word(); - uint sign = ix >> 31; - ix &= 0x7fffffff; - if (ix >= 0x4c800000) - { - /* if |x| >= 2**26 */ - if (math::is_nan(x)) return x; - float z = ATANHIF[3] + 0x1p-120f; - return sign ? -z : z; - } - switch - { - case ix < 0x3ee00000: - /* |x| < 0.4375 */ - if (ix < 0x39800000) - { - /* |x| < 2**-12 */ - if (ix < 0x00800000) - { - /* raise underflow for subnormal x */ - float f = @volatile_load(x); - f = f * f; - } - return x; - } - id = -1; - case ix < 0x3f980000: - /* |x| < 1.1875 */ - x = math::abs(x); - if (ix < 0x3f300000) - { - /* 7/16 <= |x| < 11/16 */ - id = 0; - x = (2.0f * x - 1.0f) / (2.0f + x); - break; - } - /* 11/16 <= |x| < 19/16 */ - id = 1; - x = (x - 1.0f) / (x + 1.0f); - case ix < 0x401c0000: - x = math::abs(x); - /* |x| < 2.4375 */ - id = 2; - x = (x - 1.5f) / (1.0f + 1.5f * x); - default: - /* 2.4375 <= |x| < 2**26 */ - x = math::abs(x); - id = 3; - x = -1.0f / x; - } - /* end of argument reduction */ - float z = x * x; - float w = z * z; - /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */ - float s1 = z * (ATF[0] + w * (ATF[2] + w * ATF[4])); - float s2 = w * (ATF[1] + w * ATF[3]); - if (id < 0) return x - x * (s1 + s2); - z = ATANHIF[id] - ((x * (s1 + s2) - ATANLOF[id]) - x); - return sign ? -z : z; -} - -/* origin: FreeBSD /usr/src/lib/msun/src/e_atan2.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - * - */ - -const PI_LO @private = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */ - -fn double _atan2(double y, double x) @weak @extern("atan2") @nostrip -{ - if (math::is_nan(x) || math::is_nan(y)) return x + y; - - uint lx = x.low_word(); - uint ix = x.high_word(); - uint ly = y.low_word(); - uint iy = y.high_word(); - - // x = 1.0 - if ((ix - 0x3ff00000) | lx == 0) return _atan(y); - // 2*sign(x) + sign(y) - uint m = ((iy >> 31) & 1) | ((ix >> 30) & 2); - ix &= 0x7fffffff; - iy &= 0x7fffffff; - - // when y = 0 - if (iy | ly == 0) - { - switch (m) - { - case 0: - case 1: return y; /* atan(+-0,+anything)=+-0 */ - case 2: return math::PI; /* atan(+0,-anything) = pi */ - case 3: return -math::PI; /* atan(-0,-anything) =-pi */ - } - } - // when x = 0 - if (ix | lx == 0) return m & 1 ? -math::PI_2 : math::PI_2; - /* when x is INF */ - if (ix == 0x7ff00000) - { - if (iy == 0x7ff00000) - { - switch (m) - { - case 0: return math::PI_4; /* atan(+INF,+INF) */ - case 1: return -math::PI_4; /* atan(-INF,+INF) */ - case 2: return math::PI_4 + math::PI_2; /* atan(+INF,-INF) */ - case 3: return - (math::PI_4 + math::PI_2); /* atan(-INF,-INF) */ - } - unreachable(); - } - switch (m) - { - case 0: return 0.0; /* atan(+...,+INF) */ - case 1: return -0.0; /* atan(-...,+INF) */ - case 2: return math::PI; /* atan(+...,-INF) */ - case 3: return -math::PI; /* atan(-...,-INF) */ - } - unreachable(); - } - /* |y/x| > 0x1p64 */ - if (ix + (64 << 20) < iy || iy == 0x7ff00000) return m & 1 ? -math::PI_2 : math::PI_2; - - /* z = atan(|y/x|) without spurious underflow */ - double z = ((m & 2) && iy + (64 << 20) < ix) ? 0 : _atan(math::abs(y/x)); - switch (m) - { - case 0: return z; /* atan(+,+) */ - case 1: return -z; /* atan(-,+) */ - case 2: return math::PI - (z - PI_LO); /* atan(+,-) */ - default: return (z - PI_LO) - math::PI; /* atan(-,-) */ - } -} - -/* origin: FreeBSD /usr/src/lib/msun/src/e_atan2f.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -const float PI_F @private = 3.1415927410e+00; /* 0x40490fdb */ -const float PI_LO_F @private = -8.7422776573e-08; /* 0xb3bbbd2e */ - -fn float _atan2f(float y, float x) @weak @extern("atan2f") @nostrip -{ - if (math::is_nan(x) || math::is_nan(y)) return x + y; - uint ix = x.word(); - uint iy = y.word(); - /* x=1.0 */ - if (ix == 0x3f800000) return _atanf(y); - /* 2*sign(x)+sign(y) */ - uint m = ((iy >> 31) & 1) | ((ix >> 30) & 2); - ix &= 0x7fffffff; - iy &= 0x7fffffff; - - /* when y = 0 */ - if (iy == 0) - { - switch (m) - { - case 0: - case 1: return y; /* atan(+-0,+anything)=+-0 */ - case 2: return PI_F; /* atan(+0,-anything) = pi */ - case 3: return -PI_F; /* atan(-0,-anything) =-pi */ - } - unreachable(); - } - /* when x = 0 */ - if (ix == 0) return m & 1 ? -(float)math::PI_2 : (float)math::PI_2; - /* when x is INF */ - if (ix == 0x7f800000) - { - if (iy == 0x7f800000) - { - switch (m) - { - case 0: return (float)math::PI_4; /* atan(+INF,+INF) */ - case 1: return (float)-math::PI_4; /* atan(-INF,+INF) */ - case 2: return (float)(math::PI_4 + math::PI_2); /*atan(+INF,-INF)*/ - case 3: return -(float)(math::PI_4 + math::PI_2); /*atan(-INF,-INF)*/ - } - unreachable(); - } - switch (m) - { - case 0: return 0.0f; /* atan(+...,+INF) */ - case 1: return -0.0f; /* atan(-...,+INF) */ - case 2: return PI_F; /* atan(+...,-INF) */ - case 3: return -PI_F; /* atan(-...,-INF) */ - } - unreachable(); - } - /* |y/x| > 0x1p26 */ - if (ix + 26 << 23 < iy || iy == 0x7f800000) return m & 1 ? -(float)math::PI_2 : (float)math::PI_2; - - /* z = atan(|y/x|) with correct underflow */ - /*|y/x| < 0x1p-26, x < 0 */ - float z = (m & 2 && iy + 26 << 23 < ix) ? 0.0f : _atanf(math::abs(y / x)); - switch (m) - { - case 0: return z; /* atan(+,+) */ - case 1: return -z; /* atan(-,+) */ - case 2: return PI_F - (z - PI_LO_F); /* atan(+,-) */ - default: return (z - PI_LO_F) - PI_F; /* atan(-,-) */ - } -} diff --git a/lib7/std/math/math_nolibc/atanh.c3 b/lib7/std/math/math_nolibc/atanh.c3 deleted file mode 100644 index 5f7383569..000000000 --- a/lib7/std/math/math_nolibc/atanh.c3 +++ /dev/null @@ -1,95 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD usr/src/lib/msun/src/e_atanh.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -fn double _atanh(double x) @weak @extern("atanh") @nostrip -{ - double t @noinit; - uint hx = x.high_word(); - uint ix = hx & 0x7fffffff; - uint sign = hx >> 31; - switch - { - /* |x| >= 1 or nan */ - case ix >= 0x3ff00000: - uint lx = x.low_word(); - if ((ix - 0x3ff00000 | lx) == 0) - { - return sign ? -double.inf : double.inf; - } - return double.nan; - /* x<2**-28 */ - case ix < 0x3e300000 && (1e300 + x) > 0.: - return x; - } - x.set_high_word(ix); - /* |x| < 0.5 */ - if (ix < 0x3fe00000) - { - t = x + x; - t = 0.5 * _log1p(t + t * x / (1. - x)); - } - else - { - t = 0.5 * _log1p((x + x) / (1. - x)); - } - return sign ? -t : t; -} - -/* origin: FreeBSD usr/src/lib/msun/src/e_atanhf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -fn float _atanhf(float x) @weak @extern("atanhf") @nostrip -{ - float t @noinit; - uint hx = bitcast(x, uint); - uint ix = hx & 0x7fffffff; - uint sign = hx >> 31; - switch - { - /* |x| >= 1 or nan */ - case ix >= 0x3f800000: - if (ix == 0x3f800000) - { - return sign ? -float.inf : float.inf; - } - return float.nan; - /* x<2**-28 */ - case ix < 0x31800000 && (1e30 + x) > 0.f: - return x; - } - x.set_word(ix); - /* |x| < 0.5 */ - if (ix < 0x3f000000) - { - t = x + x; - t = 0.5f * _log1pf(t + t * x / (1.f - x)); - } - else - { - t = 0.5f * _log1pf((x + x) / (1.f - x)); - } - return sign ? -t : t; -} diff --git a/lib7/std/math/math_nolibc/ceil.c3 b/lib7/std/math/math_nolibc/ceil.c3 deleted file mode 100644 index 0fd5ea500..000000000 --- a/lib7/std/math/math_nolibc/ceil.c3 +++ /dev/null @@ -1,42 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn double _ceil(double x) @weak @extern("ceil") @nostrip -{ - ulong ui = bitcast(x, ulong); - int e = (int)((ui >> 52) & 0x7ff); - if (e >= 0x3ff + 52 || x == 0) return x; - // y = int(x) - x, where int(x) is an integer neighbor of x - double y = ui >> 63 ? (x - TOINT) + TOINT - x : (x + TOINT) - TOINT - x; - // special case because of non-nearest rounding modes - if (e <= 0x3ff - 1) - { - force_eval_add(y, 0); - return ui >> 63 ? -0.0 : 1; - } - return y < 0 ? x + y + 1 : x + y; -} - - -fn float _ceilf(float x) @weak @extern("ceilf") @nostrip -{ - uint u = bitcast(x, uint); - int e = (int)((u >> 23) & 0xff) - 0x7f; - switch - { - case e >= 23: - return x; - case e >= 0: - uint m = 0x007fffff >> e; - if (u & m == 0) return x; - force_eval_add(x, 0x1p120f); - if (u >> 31 == 0) u += m; - u &= ~m; - case u >> 31 != 0: - force_eval_add(x, 0x1p120f); - return -0.0f; - case u << 1 != 0: - return 1; - } - return bitcast(u, float); -} - diff --git a/lib7/std/math/math_nolibc/cos.c3 b/lib7/std/math/math_nolibc/cos.c3 deleted file mode 100644 index 7dcb16e10..000000000 --- a/lib7/std/math/math_nolibc/cos.c3 +++ /dev/null @@ -1,85 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn float _cosf(float x) @extern("cosf") @weak @nostrip -{ - uint ix = x.word(); - uint sign = ix >> 31; - ix &= 0x7fffffff; - - switch - { - case (ix <= 0x3f490fda): - // |x| < 2**-12 - if (ix < 0x39800000) - { - /* raise inexact if x != 0 */ - force_eval_add(x, 0x1p120f); - return 1.0f; - } - return __cosdf(x); - // |x| ~<= 5*pi/4 - case (ix <= 0x407b53d1): - // |x| ~> 3*pi/4 - if (ix > 0x4016cbe3) return -__cosdf(sign ? x + S2PI2 : x - S2PI2); - return sign ? __sindf(x + S1PI2) : __sindf(S1PI2 - x); - // |x| ~<= 9*pi/4 - case (ix <= 0x40e231d5): - if (ix > 0x40afeddf) return __cosdf(sign ? x + S4PI2 : x - S4PI2); - return sign ? __sindf((double)(-x) - S3PI2) : __sindf(x - S3PI2); - } - - // general argument reduction needed - double y @noinit; - switch (__rem_pio2f(x, &y) & 3) - { - case 0: return __cosdf(y); - case 1: return __sindf(-y); - case 2: return -__cosdf(y); - default: return __sindf(y); - } -} - -/* origin: FreeBSD /usr/src/lib/msun/src/s_cos.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -fn double _cos(double x) @extern("cos") @weak @nostrip -{ - // High word of x. - uint ix = x.high_word() & 0x7fffffff; - - switch - { - // |x| ~< pi/4 - case ix <= 0x3fe921fb: - if (ix < 0x3e46a09e) - { - // |x| < 2**-27 * sqrt(2) - // raise inexact if x!=0 - force_eval_add(x, 0x1p120f); - return 1.0; - } - return __cos(x, 0); - case ix >= 0x7ff00000: - // cos(Inf or NaN) is NaN - return x - x; - default: - // argument reduction - double[2] y @noinit; - switch (__rem_pio2(x, &y) & 3) - { - case 0: return __cos(y[0], y[1]); - case 1: return -__sin(y[0], y[1], 1); - case 2: return -__cos(y[0], y[1]); - default: return __sin(y[0], y[1], 1); - } - } -} diff --git a/lib7/std/math/math_nolibc/exp.c3 b/lib7/std/math/math_nolibc/exp.c3 deleted file mode 100644 index ce19a4d4e..000000000 --- a/lib7/std/math/math_nolibc/exp.c3 +++ /dev/null @@ -1,60 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -const double EXP_LN2_HI = 6.93147180369123816490e-01; -const double EXP_LN2_LO = 1.90821492927058770002e-10; -const double EXP_INV_LN2 = 1.44269504088896338700e+00; -const double EXP_P1 = 1.66666666666666019037e-01; -const double EXP_P2 = -2.77777777770155933842e-03; -const double EXP_P3 = 6.61375632143793436117e-05; -const double EXP_P4 = -1.65339022054652515390e-06; -const double EXP_P5 = 4.13813679705723846039e-08; -/*--------------------------------------------*/ -const float EXPF_LN2_HI = 6.9314575195e-01f; -const float EXPF_LN2_LO = 1.4286067653e-06f; -const float EXPF_INV_LN2 = 1.4426950216e+00f; -const float EXPF_P1 = 1.6666667163e-01f; -const float EXPF_P2 = -2.7777778450e-03f; -const float EXPF_P3 = 6.6137559770e-05f; -const float EXPF_P4 = -1.6533901999e-06f; - -fn double exp(double x) @extern("exp") -{ - if (x != x) return x; - if (x == double.inf) return double.inf; - if (x == -double.inf) return 0.0; // IEEE 754 spec - // Overflow threshold for exp (approx +709.78 for double) - if (x > 709.782712893384) return double.inf; - // Underflow threshold for exp (approx -745.13 for double) - if (x < -745.133219101941) return 0.0; - - double px = x * EXP_INV_LN2; - double k = _floor(px + 0.5); - double r = x - k * EXP_LN2_HI - k * EXP_LN2_LO; - - double r2 = r * r; - double p = r2 * (EXP_P1 + r2 * (EXP_P2 + r2 * (EXP_P3 + r2 * (EXP_P4 + r2 * EXP_P5)))); - double exp_r = 1.0 + r + r * p; - - return ldexp(exp_r, (int)k); -} - -fn float expf(float x) @extern("expf") -{ - if (x != x) return x; - if (x == float.inf) return float.inf; - if (x == -float.inf) return 0.0f; // IEEE 754 spec - // Overflow threshold (approx +88.72 for float) - if (x > 88.7228f) return float.inf; - // Underflow threshold (approx -103.97 for float) - if (x < -103.972084f) return 0.0f; - - float px = x * EXPF_INV_LN2; - float k = _floorf(px + 0.5f); - float r = x - k * EXPF_LN2_HI - k * EXPF_LN2_LO; - - float r2 = r * r; - float p = r2 * (EXPF_P1 + r2 * (EXPF_P2 + r2 * (EXPF_P3 + r2 * EXPF_P4))); - float exp_r = 1.0f + r + r * p; - - return ldexpf(exp_r, (int)k); -} diff --git a/lib7/std/math/math_nolibc/exp2.c3 b/lib7/std/math/math_nolibc/exp2.c3 deleted file mode 100644 index df14507bc..000000000 --- a/lib7/std/math/math_nolibc/exp2.c3 +++ /dev/null @@ -1,141 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -macro uint _top12f(float x) @private => bitcast(x, uint) >> 20; - - -fn float _exp2f(float x) @extern("exp2f") @weak @nostrip -{ - double xd = x; - uint abstop = _top12f(x) & 0x7ff; - if (abstop >= _top12f(128.0f)) /* @unlikely */ - { - switch - { - // |x| >= 128 or x is nan. - case bitcast(x, uint) == bitcast(-float.inf, uint): - return 0; - case abstop >= _top12f(float.inf): - return x + x; - case x > 0: - return __math_oflowf(0); - case x <= -150.0f: - return __math_uflowf(0); - } - } - const SHIFT = __EXP2F_DATA.shift_scaled; - const uint N = 1U << EXP2F_TABLE_BITS; - // x = k/N + r with r in [-1/(2N), 1/(2N)] and int k. - double kd = xd + SHIFT; - ulong ki = bitcast(kd, ulong); - kd -= SHIFT; /* k/N for int k. */ - double r = xd - kd; - - // exp2(x) = 2^(k/N) * 2^r ~= s * (C0*r^3 + C1*r^2 + C2*r + 1) - ulong t = __EXP2F_DATA.tab[ki % N]; - t += ki << (52 - EXP2F_TABLE_BITS); - double s = bitcast(t, double); - double z = __EXP2F_DATA.poly[0] * r + __EXP2F_DATA.poly[1]; - double r2 = r * r; - double y = __EXP2F_DATA.poly[2] * r + 1; - y = z * r2 + y; - y = y * s; - return (float)y; -} - -fn double _exp2_specialcase(double tmp, ulong sbits, ulong ki) @private -{ - if (ki & 0x80000000 == 0) - { - // k > 0, the exponent of scale might have overflowed by 1. - sbits -= 1u64 << 52; - double scale = bitcast(sbits, double); - double y = 2 * (scale + scale * tmp); - return y; - } - // k < 0, need special care in the subnormal range. - sbits += 1022u64 << 52; - double scale = bitcast(sbits, double); - double y = scale + scale * tmp; - if (y >= 1.0) - { - return 0x1p-1022 * y; - } - // Round y to the right precision before scaling it into the subnormal - // range to avoid double rounding that can cause 0.5+E/2 ulp error where - // E is the worst-case ulp error outside the subnormal range. So this - // is only useful if the goal is better than 1 ulp worst-case error. - double lo = scale - y + scale * tmp; - double hi = 1.0 + y; - lo = 1.0 - hi + y + lo; - y = hi + lo - 1.0; - /* Avoid -0.0 with downward rounding. */ - if (WANT_ROUNDING && y == 0.0) y = 0.0; - /* The underflow exception needs to be signaled explicitly. */ - return __math_xflow(0, 0x1p-1022); -} - - -macro uint _top12d(double x) @private -{ - return (uint)(bitcast(x, ulong) >> 52); -} - -fn double _exp2(double x) @extern("exp2") @weak @nostrip -{ - uint abstop = _top12d(x) & 0x7ff; - ulong u = bitcast(x, ulong); - if (abstop - _top12d(0x1p-54) >= _top12d(512.0) - _top12d(0x1p-54)) /* @unlikely */ - { - if (abstop - _top12d(0x1p-54) >= 0x80000000) - { - // Avoid spurious underflow for tiny x. - // Note: 0 is common input. - return WANT_ROUNDING ? 1.0 + x : 1.0; - } - if (abstop >= _top12d(1024.0)) - { - switch - { - case u == bitcast(-double.inf, ulong): - return 0.0; - case abstop >= _top12d(double.inf): - return 1.0 + x; - case !(u >> 63): - return __math_oflow(0); - case u >= bitcast(-1075.0, ulong): - return __math_uflow(0); - } - } - // Large x is special cased below. - if (2 * u > 2 * bitcast(928.0, ulong)) abstop = 0; - } - const SHIFT = __EXP2_DATA.exp2_shift; - // exp2(x) = 2^(k/N) * 2^r, with 2^r in [2^(-1/2N),2^(1/2N)]. - // x = k/N + r, with int k and r in [-1/2N, 1/2N]. - double kd = x + SHIFT; - ulong ki = bitcast(kd, ulong); /* k. */ - kd -= SHIFT; /* k/N for int k. */ - double r = x - kd; - /* 2^(k/N) ~= scale * (1 + tail). */ - ulong idx = 2 * (ki % EXP_DATA_WIDTH); - ulong top = ki << (52 - EXP_TABLE_BITS); - double tail = __EXP2_DATA.tab[idx]; - /* This is only a valid scale when -1023*N < k < 1024*N. */ - ulong sbits = __EXP2_DATA.tab[idx + 1] + top; - /* exp2(x) = 2^(k/N) * 2^r ~= scale + scale * (tail + 2^r - 1). */ - /* Evaluation is optimized assuming superscalar pipelined execution. */ - double r2 = r * r; - const C1 = __EXP2_DATA.exp2_poly[0]; - const C2 = __EXP2_DATA.exp2_poly[1]; - const C3 = __EXP2_DATA.exp2_poly[2]; - const C4 = __EXP2_DATA.exp2_poly[3]; - const C5 = __EXP2_DATA.exp2_poly[4]; - /* Without fma the worst case error is 0.5/N ulp larger. */ - /* Worst case error is less than 0.5+0.86/N+(abs poly error * 2^53) ulp. */ - double tmp = tail + r * C1 + r2 * (C2 + r * C3) + r2 * r2 * (C4 + r * C5); - if (abstop == 0 /* @unlikely */) return _exp2_specialcase(tmp, sbits, ki); - double scale = bitcast(sbits, double); - /* Note: tmp == 0 or |tmp| > 2^-65 and scale > 2^-928, so there - is no spurious underflow here even without fma. */ - return scale + scale * tmp; -} diff --git a/lib7/std/math/math_nolibc/fabs.c3 b/lib7/std/math/math_nolibc/fabs.c3 deleted file mode 100644 index 38c96e8fe..000000000 --- a/lib7/std/math/math_nolibc/fabs.c3 +++ /dev/null @@ -1,15 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn double _fabs(double x) @weak @extern("fabs") @nostrip -{ - ulong ix = bitcast(x, ulong); - ix &= ~(1ul << 63); - return bitcast(ix, double); -} - -fn float _fabsf(float x) @weak @extern("fabsf") @nostrip -{ - uint ix = bitcast(x, uint); - ix &= 0x7fffffff; - return bitcast(ix, float); -} diff --git a/lib7/std/math/math_nolibc/floor.c3 b/lib7/std/math/math_nolibc/floor.c3 deleted file mode 100644 index 274948fe1..000000000 --- a/lib7/std/math/math_nolibc/floor.c3 +++ /dev/null @@ -1,42 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn double _floor(double x) @weak @extern("floor") @nostrip -{ - ulong ui = bitcast(x, ulong); - int e = (int)((ui >> 52) & 0x7ff); - if (e >= 0x3ff + 52 || x == 0) return x; - // y = int(x) - x, where int(x) is an integer neighbor of x - double y = ui >> 63 ? (x - TOINT) + TOINT - x : (x + TOINT) - TOINT - x; - // special case because of non-nearest rounding modes - if (e <= 0x3ff - 1) - { - force_eval_add(y, 0); - return ui >> 63 ? -1 : 0; - } - return y > 0 ? x + y - 1 : x + y; -} - - -fn float _floorf(float x) @weak @extern("floorf") @nostrip -{ - uint u = bitcast(x, uint); - int e = (int)((u >> 23) & 0xff) - 0x7f; - switch - { - case e >= 23: - return x; - case e >= 0: - uint m = 0x007fffff >> e; - if (u & m == 0) return x; - force_eval_add(x, 0x1p120f); - if (u >> 31) u += m; - u &= ~m; - case u >> 31 == 0: - force_eval_add(x, 0x1p120f); - u = 0; - case (u << 1 != 0): - return -1; - } - return bitcast(u, float); -} - diff --git a/lib7/std/math/math_nolibc/frexp.c3 b/lib7/std/math/math_nolibc/frexp.c3 deleted file mode 100644 index 20702f869..000000000 --- a/lib7/std/math/math_nolibc/frexp.c3 +++ /dev/null @@ -1,59 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn double frexp(double x, int* exp) @extern("frexp") -{ - uint hx = x.high_word(); - uint ix = hx & 0x7fffffff; - uint lx = x.low_word(); - - if (ix >= 0x7ff00000 || (ix | lx) == 0) - { - *exp = 0; - return x; - } - - // exponent extraction and normalization - int e = (int)((ix >> 20) & 0x7ff); - if (e == 0) - { - // subnormal number - x *= 0x1p64; - hx = x.high_word(); - e = (int)((hx >> 20) & 0x7ff) - 64; - } - *exp = e - 1022; - - // set exponent to -1 (fraction in [0.5, 1)) - hx = (hx & 0x800fffff) | 0x3fe00000; - { - ulong rep = ((ulong)hx << 32) | lx; - return bitcast(rep, double); - } -} - -fn float frexpf(float x, int* exp) @extern("frexpf") -{ - uint ix = x.word(); - uint hx = ix & 0x7fffffff; - - if (hx >= 0x7f800000 || hx == 0) - { - *exp = 0; - return x; - } - - // exponent extraction and normalization - int e = (int)((hx >> 23) & 0xff); - if (e == 0) - { - // subnormal number - x *= 0x1p64f; - ix = x.word(); - e = (int)((ix >> 23) & 0xff) - 64; - } - *exp = e - 126; - - // set exponent to -1 (fraction in [0.5, 1)) - ix = (ix & 0x807fffff) | 0x3f000000; - return bitcast(ix, float); -} diff --git a/lib7/std/math/math_nolibc/ldexp.c3 b/lib7/std/math/math_nolibc/ldexp.c3 deleted file mode 100644 index 3a40a9a46..000000000 --- a/lib7/std/math/math_nolibc/ldexp.c3 +++ /dev/null @@ -1,67 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn double ldexp(double x, int exp) @extern("ldexp") -{ - uint hx = x.high_word(); - int hexp = (int)((hx & 0x7ff00000) >> 20); - - - if (hexp == 0x7ff) return x; - if (hexp == 0) - { - // subnormal number handling - x *= 0x1p64; - hx = x.high_word(); - hexp = (int)((hx & 0x7ff00000) >> 20) - 64; - } - - // new exponent calculation - hexp += exp; - - if (hexp > 0x7fe) return x * double.inf; - if (hexp < 1) - { - x *= 0x1p-1022; - hexp += 1022; - if (hexp < 1) x *= 0x1p-1022; - return x; - } - - // set new exponent - hx = ((ulong)hx & 0x800fffff) | ((ulong)hexp << 20); - { - ulong rep = ((ulong)hx << 32) | x.low_word(); - return bitcast(rep, double); - } -} - -fn float ldexpf(float x, int exp) @extern("ldexpf") -{ - uint ix = x.word(); - int hexp = (int)((ix & 0x7f800000) >> 23); - - if (hexp == 0xff) return x; - if (hexp == 0) - { - // subnormal number handling - x *= 0x1p64f; - ix = x.word(); - hexp = (int)((ix & 0x7f800000) >> 23) - 64; - } - - // new exponent calculation - hexp += exp; - - if (hexp > 0xfe) return x * float.inf; - if (hexp < 1) - { - x *= 0x1p-126f; - hexp += 126; - if (hexp < 1) x *= 0x1p-126f; - return x; - } - - // set new exponent - ix = (ix & 0x807fffff) | (hexp << 23); - return bitcast(ix, float); -} diff --git a/lib7/std/math/math_nolibc/log.c3 b/lib7/std/math/math_nolibc/log.c3 deleted file mode 100644 index 77301348f..000000000 --- a/lib7/std/math/math_nolibc/log.c3 +++ /dev/null @@ -1,86 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -const double LOG_LN2_HI = 6.93147180369123816490e-01; -const double LOG_LN2_LO = 1.90821492927058770002e-10; -const double LOG_L1 = 6.666666666666735130e-01; -const double LOG_L2 = 3.999999999940941908e-01; -const double LOG_L3 = 2.857142874366239149e-01; -const double LOG_L4 = 2.222219843214978396e-01; -const double LOG_L5 = 1.818357216161805012e-01; -const double LOG_L6 = 1.531383769920937332e-01; -/*--------------------------------------------*/ -const float LOGF_LN2_HI = 6.9313812256e-01f; -const float LOGF_LN2_LO = 9.0580006145e-06f; -const float LOGF_L1 = 6.6666662693e-01f; -const float LOGF_L2 = 4.0000972152e-01f; -const float LOGF_L3 = 2.8498786688e-01f; -const float LOGF_L4 = 2.4279078841e-01f; - -const double SQRT2 = 1.41421356237309504880; -const float SQRT2F = 1.41421356237309504880f; - -fn double log(double x) @extern("log") -{ - if (x != x) return x; - if (x < 0.0) return double.nan; - if (x == 0.0) return -double.inf; - if (x == double.inf) return double.inf; - - int k; - double f = frexp(x, &k); - if (f < SQRT2 * 0.5) - { - f *= 2.0; - k--; - } - - // polynomial approximation of log(1 + f), with f in [0, sqrt(2) - 1] - f -= 1.0; - double s = f / (2.0 + f); - double z = s * s; - double w = z * z; - - // even-part polynomial terms (t1) and odd-part polynomial terms (t2) - double t1 = w * (LOG_L1 + w * (LOG_L3 + w * LOG_L5)); - double t2 = z * (LOG_L2 + w * (LOG_L4 + w * LOG_L6)); - double r = t1 + t2; - - double hfsq = 0.5 * f * f; - - return k * LOG_LN2_HI - ((hfsq - (s * (hfsq + r) + k * LOG_LN2_LO)) - f); -} - -fn float logf(float x) @extern("logf") -{ - if (x != x) return x; - if (x < 0.0f) return float.nan; - if (x == 0.0f) return -float.inf; - if (x == float.inf) return float.inf; - - int k; - float f = frexpf(x, &k); - if (f < SQRT2F * 0.5f) - { - f *= 2.0f; - k--; - } - - // polynomial approximation for log(1 + f) - f -= 1.0f; - float s = f / (2.0f + f); - float z = s * s; - float w = z * z; - - /* - logf uses fewer terms in its polynomial approximation - compared to the double precision version because - single-precision floating point doesn't benefit from the additional terms. - LOGF_L1, ... ,LOGF_L4 provide sufficient accuracy for 32-bit float calculations. - */ - float t1 = w * (LOGF_L1 + w * LOGF_L3); - float t2 = z * (LOGF_L2 + w * LOGF_L4); - float r = t1 + t2; - - float hfsq = 0.5f * f * f; - return k * LOGF_LN2_HI - ((hfsq - (s * (hfsq + r) + k * LOGF_LN2_LO)) - f); -} \ No newline at end of file diff --git a/lib7/std/math/math_nolibc/log1p.c3 b/lib7/std/math/math_nolibc/log1p.c3 deleted file mode 100644 index 9fe0984a5..000000000 --- a/lib7/std/math/math_nolibc/log1p.c3 +++ /dev/null @@ -1,228 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD /usr/src/lib/msun/src/s_log1p.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -/* origin: musl libc /src/math/log1p.c */ -/* - * ==================================================== - * Copyright (c) 2005-2020 Rich Felker, et al. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER 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. - * ==================================================== - */ - -const LN2_HI @local = 6.93147180369123816490e-01; /* 3fe62e42 fee00000 */ -const LN2_LO @local = 1.90821492927058770002e-10; /* 3dea39ef 35793c76 */ -const LG1 @local = 6.666666666666735130e-01; /* 3FE55555 55555593 */ -const LG2 @local = 3.999999999940941908e-01; /* 3FD99999 9997FA04 */ -const LG3 @local = 2.857142874366239149e-01; /* 3FD24924 94229359 */ -const LG4 @local = 2.222219843214978396e-01; /* 3FCC71C5 1D8E78AF */ -const LG5 @local = 1.818357216161805012e-01; /* 3FC74664 96CB03DE */ -const LG6 @local = 1.531383769920937332e-01; /* 3FC39A09 D078C69F */ -const LG7 @local = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ - -fn double _log1p(double x) @weak @extern("log1p") @nostrip -{ - uint hx = x.high_word(); - int k = 1; - double c @noinit; - double f @noinit; - switch - { - /* 1+x < sqrt(2)+ */ - case hx < 0x3fda827a || hx >> 31 != 0: - switch - { - /* x <= -1.0 */ - case hx >= 0xbff00000: - if (x == -1) return -double.inf; - return double.nan; - /* |x| < 2**-53 */ - case hx << 1 < 0x3ca00000 << 1: - /* underflow if subnormal */ - if ((hx & 0x7ff00000) == 0) - { - (float)@volatile_load(x); - } - return x; - /* sqrt(2)/2- <= 1+x < sqrt(2)+ */ - case hx <= 0xbfd2bec4: - k = 0; - c = 0; - f = x; - } - case hx >= 0x7ff00000: - return x; - } - if (k) - { - double u = 1 + x; - uint hu = u.high_word(); - hu += 0x3ff00000 - 0x3fe6a09e; - k = (int)(hu >> 20) - 0x3ff; - /* correction term ~ log(1+x)-log(u), avoid underflow in c/u */ - if (k < 54) - { - c = (k >= 2) ? 1. - (u - x) : x - (u - 1.); - c /= u; - } - else - { - c = 0; - } - /* reduce u into [sqrt(2)/2, sqrt(2)] */ - hu = (hu & 0x000fffff) + 0x3fe6a09e; - u = bitcast(((ulong)hu << 32) | (bitcast(u, ulong) & 0xffffffff) , double); - f = u - 1.; - } - double hfsq = 0.5 * f * f; - double s = f / (2. + f); - double z = s * s; - double w = z * z; - double t1 = w * (LG2 + w * (LG4 + w * LG6)); - double t2 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7))); - double r = t1 + t2; - double dk = k; - return s * (hfsq + r) + (dk * LN2_LO + c) - hfsq + f + dk * LN2_HI; -} - -/* origin: FreeBSD /usr/src/lib/msun/src/s_log1pf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -/* origin: musl libc /src/math/log1pf.c */ -/* - * ==================================================== - * Copyright (c) 2005-2020 Rich Felker, et al. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER 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. - * ==================================================== - */ - -const float LN2_HI_F @local = 6.9313812256e-01; /* 0x3f317180 */ -const float LN2_LO_F @local = 9.0580006145e-06; /* 0x3717f7d1 */ -/* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ -const float LG1_F @local = 0xaaaaaa.0p-24; /* 0.66666662693 */ -const float LG2_F @local = 0xccce13.0p-25; /* 0.40000972152 */ -const float LG3_F @local = 0x91e9ee.0p-25; /* 0.28498786688 */ -const float LG4_F @local = 0xf89e26.0p-26; /* 0.24279078841 */ - -fn float _log1pf(float x) @weak @extern("log1pf") @nostrip -{ - uint ix = x.word(); - int k = 1; - float c @noinit; - float f @noinit; - switch - { - /* 1+x < sqrt(2)+ */ - case ix < 0x3ed413d0 || ix >> 31 != 0: - switch - { - /* x <= -1.0 */ - case ix >= 0xbf800000: - if (x == -1) return -float.inf; - return float.nan; - /* |x| < 2**-24 */ - case ix << 1 < 0x33800000 << 1: - /* underflow if subnormal */ - if ((ix & 0x7f800000) == 0) - { - float v = @volatile_load(x); - v = v * v; - } - return x; - /* sqrt(2)/2- <= 1+x < sqrt(2)+ */ - case ix <= 0xbe95f619: - k = 0; - c = 0; - f = x; - } - case ix >= 0x7f800000: - return x; - } - if (k) - { - float u = 1 + x; - uint iu = u.word(); - iu += 0x3f800000 - 0x3f3504f3; - k = (int)(iu >> 23) - 0x7f; - /* correction term ~ log(1+x)-log(u), avoid underflow in c/u */ - if (k < 25) - { - c = (k >= 2) ? 1.f - (u - x) : x - (u - 1.f); - c /= u; - } - else - { - c = 0; - } - /* reduce u into [sqrt(2)/2, sqrt(2)] */ - iu = (iu & 0x007fffff) + 0x3f3504f3; - f = bitcast(iu, float) - 1.f; - } - float hfsq = 0.5f * f * f; - float s = f / (2.f + f); - float z = s * s; - float w = z * z; - float t1 = w * (LG2_F + w * LG4_F); - float t2 = z * (LG1_F + w * LG3_F); - float r = t1 + t2; - float dk = k; - return s * (hfsq + r) + (dk * LN2_LO_F + c) - hfsq + f + dk * LN2_HI_F; -} \ No newline at end of file diff --git a/lib7/std/math/math_nolibc/math_nolibc.c3 b/lib7/std/math/math_nolibc/math_nolibc.c3 deleted file mode 100644 index 72f195cec..000000000 --- a/lib7/std/math/math_nolibc/math_nolibc.c3 +++ /dev/null @@ -1,256 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -const double TOINT = 1 / math::DOUBLE_EPSILON; -const double TOINT15 = 1.5 / math::DOUBLE_EPSILON; -const float TOINTF = (float)(1 / math::FLOAT_EPSILON); -const double S1PI2 @private = math::PI_2; /* 0x3FF921FB, 0x54442D18 */ -const double S2PI2 @private = math::PI; /* 0x400921FB, 0x54442D18 */ -const double S3PI2 @private = math::PI + math::PI_2; /* 0x4012D97C, 0x7F3321D2 */ -const double S4PI2 @private = math::PI + math::PI; /* 0x401921FB, 0x54442D18 */ - -// Shared between expf, exp2f and powf. -const EXP2F_TABLE_BITS @private = 5; -const EXP2F_POLY_ORDER @private = 3; -struct Exp2fData @private -{ - ulong[1 << EXP2F_TABLE_BITS] tab; - double shift_scaled; - double[EXP2F_POLY_ORDER] poly; - double shift; - double invln2_scaled; - double[EXP2F_POLY_ORDER] poly_scaled; -} - -const Exp2fData __EXP2F_DATA @private = { - .tab = { - 0x3ff0000000000000, 0x3fefd9b0d3158574, 0x3fefb5586cf9890f, 0x3fef9301d0125b51, - 0x3fef72b83c7d517b, 0x3fef54873168b9aa, 0x3fef387a6e756238, 0x3fef1e9df51fdee1, - 0x3fef06fe0a31b715, 0x3feef1a7373aa9cb, 0x3feedea64c123422, 0x3feece086061892d, - 0x3feebfdad5362a27, 0x3feeb42b569d4f82, 0x3feeab07dd485429, 0x3feea47eb03a5585, - 0x3feea09e667f3bcd, 0x3fee9f75e8ec5f74, 0x3feea11473eb0187, 0x3feea589994cce13, - 0x3feeace5422aa0db, 0x3feeb737b0cdc5e5, 0x3feec49182a3f090, 0x3feed503b23e255d, - 0x3feee89f995ad3ad, 0x3feeff76f2fb5e47, 0x3fef199bdd85529c, 0x3fef3720dcef9069, - 0x3fef5818dcfba487, 0x3fef7c97337b9b5f, 0x3fefa4afa2a490da, 0x3fefd0765b6e4540, - }, - .shift_scaled = 0x1.8p+52 / (1 << 5), - .poly = { - 0x1.c6af84b912394p-5, 0x1.ebfce50fac4f3p-3, 0x1.62e42ff0c52d6p-1, - }, - .shift = 0x1.8p+52, - .invln2_scaled = 0x1.71547652b82fep+0 * (1 << 5), - .poly_scaled = { - 0x1.c6af84b912394p-5 / (1 << 5) / (1 << 5) / (1 << 5), - 0x1.ebfce50fac4f3p-3 / (1 << 5) / (1 << 5), - 0x1.62e42ff0c52d6p-1 / (1 << 5), - }, -}; - -const EXP_TABLE_BITS = 7; -const EXP_POLY_ORDER = 5; -const EXP2_POLY_ORDER = 5; -const EXP_DATA_WIDTH = 1 << EXP_TABLE_BITS; -struct Exp2Data @private -{ - double invln2N; - double shift; - double negln2hiN; - double negln2loN; - double[4] poly; // Last four coefficients. - double exp2_shift; - double[EXP2_POLY_ORDER] exp2_poly; - ulong[2 * EXP_DATA_WIDTH] tab; -} - -/* - * Shared data between exp, exp2 and pow. - * - * Copyright (c) 2018, Arm Limited. - * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception - */ - - -const Exp2Data __EXP2_DATA = { - // N/ln2 - .invln2N = 0x1.71547652b82fep0 * EXP_DATA_WIDTH, - // -ln2/N - .negln2hiN = -0x1.62e42fefa0000p-8, - .negln2loN = -0x1.cf79abc9e3b3ap-47, - .shift = 0x1.8p52, - // exp polynomial coefficients. - .poly = { - // abs error: 1.555*2^-66 - // ulp error: 0.509 (0.511 without fma) - // if |x| < ln2/256+eps - // abs error if |x| < ln2/256+0x1p-15: 1.09*2^-65 - // abs error if |x| < ln2/128: 1.7145*2^-56 - 0x1.ffffffffffdbdp-2, - 0x1.555555555543cp-3, - 0x1.55555cf172b91p-5, - 0x1.1111167a4d017p-7, - }, - .exp2_shift = 0x1.8p52 / EXP_DATA_WIDTH, - // exp2 polynomial coefficients. - .exp2_poly = { - // abs error: 1.2195*2^-65 - // ulp error: 0.507 (0.511 without fma) - // if |x| < 1/256 - // abs error if |x| < 1/128: 1.9941*2^-56 - 0x1.62e42fefa39efp-1, - 0x1.ebfbdff82c424p-3, - 0x1.c6b08d70cf4b5p-5, - 0x1.3b2abd24650ccp-7, - 0x1.5d7e09b4e3a84p-10, - }, - // 2^(k/N) ~= H[k]*(1 + T[k]) for int k in [0,N) - // tab[2*k] = asuint64(T[k]) - // tab[2*k+1] = asuint64(H[k]) - (k << 52)/N - .tab = { - 0x0, 0x3ff0000000000000, - 0x3c9b3b4f1a88bf6e, 0x3feff63da9fb3335, - 0xbc7160139cd8dc5d, 0x3fefec9a3e778061, - 0xbc905e7a108766d1, 0x3fefe315e86e7f85, - 0x3c8cd2523567f613, 0x3fefd9b0d3158574, - 0xbc8bce8023f98efa, 0x3fefd06b29ddf6de, - 0x3c60f74e61e6c861, 0x3fefc74518759bc8, - 0x3c90a3e45b33d399, 0x3fefbe3ecac6f383, - 0x3c979aa65d837b6d, 0x3fefb5586cf9890f, - 0x3c8eb51a92fdeffc, 0x3fefac922b7247f7, - 0x3c3ebe3d702f9cd1, 0x3fefa3ec32d3d1a2, - 0xbc6a033489906e0b, 0x3fef9b66affed31b, - 0xbc9556522a2fbd0e, 0x3fef9301d0125b51, - 0xbc5080ef8c4eea55, 0x3fef8abdc06c31cc, - 0xbc91c923b9d5f416, 0x3fef829aaea92de0, - 0x3c80d3e3e95c55af, 0x3fef7a98c8a58e51, - 0xbc801b15eaa59348, 0x3fef72b83c7d517b, - 0xbc8f1ff055de323d, 0x3fef6af9388c8dea, - 0x3c8b898c3f1353bf, 0x3fef635beb6fcb75, - 0xbc96d99c7611eb26, 0x3fef5be084045cd4, - 0x3c9aecf73e3a2f60, 0x3fef54873168b9aa, - 0xbc8fe782cb86389d, 0x3fef4d5022fcd91d, - 0x3c8a6f4144a6c38d, 0x3fef463b88628cd6, - 0x3c807a05b0e4047d, 0x3fef3f49917ddc96, - 0x3c968efde3a8a894, 0x3fef387a6e756238, - 0x3c875e18f274487d, 0x3fef31ce4fb2a63f, - 0x3c80472b981fe7f2, 0x3fef2b4565e27cdd, - 0xbc96b87b3f71085e, 0x3fef24dfe1f56381, - 0x3c82f7e16d09ab31, 0x3fef1e9df51fdee1, - 0xbc3d219b1a6fbffa, 0x3fef187fd0dad990, - 0x3c8b3782720c0ab4, 0x3fef1285a6e4030b, - 0x3c6e149289cecb8f, 0x3fef0cafa93e2f56, - 0x3c834d754db0abb6, 0x3fef06fe0a31b715, - 0x3c864201e2ac744c, 0x3fef0170fc4cd831, - 0x3c8fdd395dd3f84a, 0x3feefc08b26416ff, - 0xbc86a3803b8e5b04, 0x3feef6c55f929ff1, - 0xbc924aedcc4b5068, 0x3feef1a7373aa9cb, - 0xbc9907f81b512d8e, 0x3feeecae6d05d866, - 0xbc71d1e83e9436d2, 0x3feee7db34e59ff7, - 0xbc991919b3ce1b15, 0x3feee32dc313a8e5, - 0x3c859f48a72a4c6d, 0x3feedea64c123422, - 0xbc9312607a28698a, 0x3feeda4504ac801c, - 0xbc58a78f4817895b, 0x3feed60a21f72e2a, - 0xbc7c2c9b67499a1b, 0x3feed1f5d950a897, - 0x3c4363ed60c2ac11, 0x3feece086061892d, - 0x3c9666093b0664ef, 0x3feeca41ed1d0057, - 0x3c6ecce1daa10379, 0x3feec6a2b5c13cd0, - 0x3c93ff8e3f0f1230, 0x3feec32af0d7d3de, - 0x3c7690cebb7aafb0, 0x3feebfdad5362a27, - 0x3c931dbdeb54e077, 0x3feebcb299fddd0d, - 0xbc8f94340071a38e, 0x3feeb9b2769d2ca7, - 0xbc87deccdc93a349, 0x3feeb6daa2cf6642, - 0xbc78dec6bd0f385f, 0x3feeb42b569d4f82, - 0xbc861246ec7b5cf6, 0x3feeb1a4ca5d920f, - 0x3c93350518fdd78e, 0x3feeaf4736b527da, - 0x3c7b98b72f8a9b05, 0x3feead12d497c7fd, - 0x3c9063e1e21c5409, 0x3feeab07dd485429, - 0x3c34c7855019c6ea, 0x3feea9268a5946b7, - 0x3c9432e62b64c035, 0x3feea76f15ad2148, - 0xbc8ce44a6199769f, 0x3feea5e1b976dc09, - 0xbc8c33c53bef4da8, 0x3feea47eb03a5585, - 0xbc845378892be9ae, 0x3feea34634ccc320, - 0xbc93cedd78565858, 0x3feea23882552225, - 0x3c5710aa807e1964, 0x3feea155d44ca973, - 0xbc93b3efbf5e2228, 0x3feea09e667f3bcd, - 0xbc6a12ad8734b982, 0x3feea012750bdabf, - 0xbc6367efb86da9ee, 0x3fee9fb23c651a2f, - 0xbc80dc3d54e08851, 0x3fee9f7df9519484, - 0xbc781f647e5a3ecf, 0x3fee9f75e8ec5f74, - 0xbc86ee4ac08b7db0, 0x3fee9f9a48a58174, - 0xbc8619321e55e68a, 0x3fee9feb564267c9, - 0x3c909ccb5e09d4d3, 0x3feea0694fde5d3f, - 0xbc7b32dcb94da51d, 0x3feea11473eb0187, - 0x3c94ecfd5467c06b, 0x3feea1ed0130c132, - 0x3c65ebe1abd66c55, 0x3feea2f336cf4e62, - 0xbc88a1c52fb3cf42, 0x3feea427543e1a12, - 0xbc9369b6f13b3734, 0x3feea589994cce13, - 0xbc805e843a19ff1e, 0x3feea71a4623c7ad, - 0xbc94d450d872576e, 0x3feea8d99b4492ed, - 0x3c90ad675b0e8a00, 0x3feeaac7d98a6699, - 0x3c8db72fc1f0eab4, 0x3feeace5422aa0db, - 0xbc65b6609cc5e7ff, 0x3feeaf3216b5448c, - 0x3c7bf68359f35f44, 0x3feeb1ae99157736, - 0xbc93091fa71e3d83, 0x3feeb45b0b91ffc6, - 0xbc5da9b88b6c1e29, 0x3feeb737b0cdc5e5, - 0xbc6c23f97c90b959, 0x3feeba44cbc8520f, - 0xbc92434322f4f9aa, 0x3feebd829fde4e50, - 0xbc85ca6cd7668e4b, 0x3feec0f170ca07ba, - 0x3c71affc2b91ce27, 0x3feec49182a3f090, - 0x3c6dd235e10a73bb, 0x3feec86319e32323, - 0xbc87c50422622263, 0x3feecc667b5de565, - 0x3c8b1c86e3e231d5, 0x3feed09bec4a2d33, - 0xbc91bbd1d3bcbb15, 0x3feed503b23e255d, - 0x3c90cc319cee31d2, 0x3feed99e1330b358, - 0x3c8469846e735ab3, 0x3feede6b5579fdbf, - 0xbc82dfcd978e9db4, 0x3feee36bbfd3f37a, - 0x3c8c1a7792cb3387, 0x3feee89f995ad3ad, - 0xbc907b8f4ad1d9fa, 0x3feeee07298db666, - 0xbc55c3d956dcaeba, 0x3feef3a2b84f15fb, - 0xbc90a40e3da6f640, 0x3feef9728de5593a, - 0xbc68d6f438ad9334, 0x3feeff76f2fb5e47, - 0xbc91eee26b588a35, 0x3fef05b030a1064a, - 0x3c74ffd70a5fddcd, 0x3fef0c1e904bc1d2, - 0xbc91bdfbfa9298ac, 0x3fef12c25bd71e09, - 0x3c736eae30af0cb3, 0x3fef199bdd85529c, - 0x3c8ee3325c9ffd94, 0x3fef20ab5fffd07a, - 0x3c84e08fd10959ac, 0x3fef27f12e57d14b, - 0x3c63cdaf384e1a67, 0x3fef2f6d9406e7b5, - 0x3c676b2c6c921968, 0x3fef3720dcef9069, - 0xbc808a1883ccb5d2, 0x3fef3f0b555dc3fa, - 0xbc8fad5d3ffffa6f, 0x3fef472d4a07897c, - 0xbc900dae3875a949, 0x3fef4f87080d89f2, - 0x3c74a385a63d07a7, 0x3fef5818dcfba487, - 0xbc82919e2040220f, 0x3fef60e316c98398, - 0x3c8e5a50d5c192ac, 0x3fef69e603db3285, - 0x3c843a59ac016b4b, 0x3fef7321f301b460, - 0xbc82d52107b43e1f, 0x3fef7c97337b9b5f, - 0xbc892ab93b470dc9, 0x3fef864614f5a129, - 0x3c74b604603a88d3, 0x3fef902ee78b3ff6, - 0x3c83c5ec519d7271, 0x3fef9a51fbc74c83, - 0xbc8ff7128fd391f0, 0x3fefa4afa2a490da, - 0xbc8dae98e223747d, 0x3fefaf482d8e67f1, - 0x3c8ec3bc41aa2008, 0x3fefba1bee615a27, - 0x3c842b94c3a9eb32, 0x3fefc52b376bba97, - 0x3c8a64a931d185ee, 0x3fefd0765b6e4540, - 0xbc8e37bae43be3ed, 0x3fefdbfdad9cbe14, - 0x3c77893b4d91cd9d, 0x3fefe7c1819e90d8, - 0x3c5305c14160cc89, 0x3feff3c22b8f71f1, - } -}; - -const bool WANT_ROUNDING = true; -macro float __math_uflowf(uint sign) => __math_xflow(sign, 0x1p97f); -macro double __math_uflow(ulong sign) => __math_xflow(sign, 0x1p769); -macro float __math_oflowf(uint sign) => __math_xflow(sign, 0x1p-95f); -macro double __math_oflow(ulong sign) => __math_xflow(sign, 0x1p-767); - -macro __math_xflow(sign, v) -{ - $typeof(v) temp @noinit; - @volatile_store(temp, (sign ? -v : v) * v); - return temp; -} - -macro force_eval_add(x, v) -{ - $typeof(x) temp @noinit; - @volatile_store(temp, x + v); -} \ No newline at end of file diff --git a/lib7/std/math/math_nolibc/pow.c3 b/lib7/std/math/math_nolibc/pow.c3 deleted file mode 100644 index a039e5998..000000000 --- a/lib7/std/math/math_nolibc/pow.c3 +++ /dev/null @@ -1,109 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn double pow(double x, double y) @extern("pow") -{ - if (x != x || y != y) return double.nan; - - if (y == double.inf) - { - if (x == 1.0 || x == -1.0) return 1.0; - return (_fabs(x) < 1.0) ? 0.0 : double.inf; - } - if (y == -double.inf) - { - if (x == 1.0 || x == -1.0) return 1.0; - return (_fabs(x) < 1.0) ? double.inf : 0.0; - } - if (x == double.inf) - { - return (y < 0.0) ? 0.0 : double.inf; - } - if (x == -double.inf) - { - if (y != _floor(y)) return -double.nan; - if (y < 0.0) return 0.0; - return ((int)y & 1) ? -double.inf : double.inf; - } - - if (y == 0.0) return 1.0; - - if (x == 0.0) - { - if (y < 0.0) return double.inf; - if (y > 0.0) return 0.0; - return 1.0; - } - - if (y == 1.0) return x; - if (x == 1.0) return 1.0; - - if (x < 0.0) - { - if (y != _floor(y)) return double.nan; - return ((int)y & 1) ? -pow(-x, y) : pow(-x, y); - } - - double result = exp(y * log(x)); - - if (result == double.inf || result == -double.inf) - { - return (y < 0.0) ? 0.0 : double.inf; - } - if (result == 0.0) return 0.0; - - return result; -} - -fn float powf(float x, float y) @extern("powf") -{ - if (x != x || y != y) return float.nan; - - if (y == float.inf) - { - if (x == 1.0f || x == -1.0f) return 1.0f; - return (_fabsf(x) < 1.0f) ? 0.0f : float.inf; - } - if (y == -float.inf) - { - if (x == 1.0f || x == -1.0f) return 1.0f; - return (_fabsf(x) < 1.0f) ? float.inf : 0.0f; - } - if (x == float.inf) - { - return (y < 0.0f) ? 0.0f : float.inf; - } - if (x == -float.inf) - { - if (y != _floorf(y)) return float.nan; - if (y < 0.0f) return 0.0f; - return ((int)y & 1) ? -float.inf : float.inf; - } - - if (y == 0.0f) return 1.0f; - - if (x == 0.0f) - { - if (y < 0.0f) return float.inf; - if (y > 0.0f) return 0.0f; - return 1.0f; - } - - if (y == 1.0f) return x; - if (x == 1.0f) return 1.0f; - - if (x < 0.0f) - { - if (y != _floorf(y)) return float.nan; - return ((int)y & 1) ? -powf(-x, y) : powf(-x, y); - } - - float result = expf(y * logf(x)); - - if (result == float.inf || result == -float.inf) - { - return (y < 0.0f) ? 0.0f : float.inf; - } - if (result == 0.0f) return 0.0f; - - return result; -} \ No newline at end of file diff --git a/lib7/std/math/math_nolibc/rempi.c3 b/lib7/std/math/math_nolibc/rempi.c3 deleted file mode 100644 index 36ec31241..000000000 --- a/lib7/std/math/math_nolibc/rempi.c3 +++ /dev/null @@ -1,534 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); -import std::math; - -/* origin: FreeBSD /usr/src/lib/msun/src/e_rem_pio2f.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - * Debugged and optimized by Bruce D. Evans. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* __rem_pio2f(x,y) - * - * return the remainder of x rem pi/2 in *y - * use double precision for everything except passing x - * use __rem_pio2_large() for large x - */ - -/* - * invpio2: 53 bits of 2/pi - * pio2_1: first 25 bits of pi/2 - * pio2_1t: pi/2 - pio2_1 - */ -fn int __rem_pio2f(float x, double *y) -{ - const double PIO4 = 0x1.921fb6p-1; - const double INVPIO2 = 6.36619772367581382433e-01; /* 0x3FE45F30, 0x6DC9C883 */ - const double PIO2_1 = 1.57079631090164184570e+00; /* 0x3FF921FB, 0x50000000 */ - const double PIO2_1T = 1.58932547735281966916e-08; /* 0x3E5110b4, 0x611A6263 */ - - uint ux = bitcast(x, uint); - uint ix = ux & 0x7fffffff; - /* 25+53 bit pi is good enough for medium size */ - if (ix < 0x4dc90fdb) - { - // |x| ~< 2^28*(pi/2), medium size - // Use a specialized rint() to get f. - uint f = (uint)(((double)x * INVPIO2 + TOINT15) - TOINT15); - int n = (int)f; - *y = x - f * PIO2_1 - f * PIO2_1T; - /* Matters with directed rounding. */ - if (*y < -PIO4) // likely false - { - n--; - f--; - *y = x - f * PIO2_1 - f * PIO2_1T; - } - else if (*y > PIO4) // likely false - { - n++; - f++; - *y = x - f * PIO2_1 - f * PIO2_1T; - } - return n; - } - if (ix >= 0x7f800000) - { - // x is inf or NaN - *y = x - (double)x; - return 0; - } - /* scale x into [2^23, 2^24-1] */ - int sign = ux >> 31; - int e0 = (ix >> 23) - (0x7f + 23); /* e0 = ilogb(|x|)-23, positive */ - ux = ix - e0 << 23; - double tx = bitcast(ux, float); - double ty @noinit; - int n = __rem_pio2_large(&tx,&ty, e0, 1, 0); - if (sign) - { - *y = (float)-ty; - return -n; - } - *y = (float)ty; - return n; -} - -/* origin: FreeBSD /usr/src/lib/msun/src/k_rem_pio2.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -const int[?] INIT_JK = {3,4,4,6}; /* initial value for jk */ - -const int[?] IPIO2 = { -0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, -0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, -0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, -0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41, -0x3991D6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8, -0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF, -0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5, -0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08, -0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3, -0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880, -0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, }; - -const double[?] PIO2 = { - 1.57079625129699707031e+00, /* 0x3FF921FB, 0x40000000 */ - 7.54978941586159635335e-08, /* 0x3E74442D, 0x00000000 */ - 5.39030252995776476554e-15, /* 0x3CF84698, 0x80000000 */ - 3.28200341580791294123e-22, /* 0x3B78CC51, 0x60000000 */ - 1.27065575308067607349e-29, /* 0x39F01B83, 0x80000000 */ - 1.22933308981111328932e-36, /* 0x387A2520, 0x40000000 */ - 2.73370053816464559624e-44, /* 0x36E38222, 0x80000000 */ - 2.16741683877804819444e-51, /* 0x3569F31D, 0x00000000 */ -}; - -fn int __rem_pio2_large(double* x, double* y, int e0, int nx, int prec) -{ - double[20] f @noinit; - double[20] fq @noinit; - double[20] q @noinit; - int[20] iq @noinit; - double fw @noinit; - /* initialize jk*/ - int jk = INIT_JK[prec]; - int jp = jk; - - /* determine jx,jv,q0, note that 3>q0 */ - int jx = nx - 1; - int jv = (e0 - 3) / 24; - if (jv < 0) jv = 0; - int q0 = e0 - 24 * (jv + 1); - - // set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] - int j = jv - jx; - int m = jx + jk; - for (int i = 0; i <= m; i++, j++) - { - f[i] = j < 0 ? 0.0 : (double)IPIO2[j]; - } - - // compute q[0],q[1],...q[jk] - for (int i = 0; i <= jk; i++) - { - for (j = 0, fw = 0.0; j <= jx; j++) fw += x[j] * f[jx + i - j]; - q[i] = fw; - } - - int jz = jk; - double z @noinit; - int ih = 0; - int n @noinit; - while (1) - { - /* distill q[] into iq[] reversingly */ - z = q[jz]; - j = jz; - for (int i = 0; j > 0; i++, j--) - { - fw = (double)(int)(0x1p-24 * z); - iq[i] = (int)(z - 0x1p24 * fw); - z = q[j - 1] + fw; - } - // compute n - z = nolibc::_scalbn(z, q0); /* actual value of z */ - z -= 8.0 * nolibc::_floor(z * 0.125); /* trim off integer >= 8 */ - n = (int)z; - z -= (double)n; - switch - { - case q0 > 0: - // need iq[jz-1] to determine n - int i = iq[jz - 1] >> (24 - q0); - n += i; - iq[jz - 1] -= i << (24 - q0); - ih = iq[jz - 1] >> (23 - q0); - case q0 == 0: - ih = iq[jz - 1] >> 23; - case z >= 0.5: - ih = 2; - } - if (ih > 0) - { - /* q > 0.5 */ - n += 1; - int carry = 0; - for (int i = 0; i < jz; i++) - { - /* compute 1-q */ - j = iq[i]; - if (carry == 0) - { - if (j != 0) - { - carry = 1; - iq[i] = 0x1000000 - j; - } - } - else - { - iq[i] = 0xffffff - j; - } - } - if (q0 > 0) - { - /* rare case: chance is 1 in 12 */ - switch (q0) - { - case 1: iq[jz - 1] &= 0x7fffff; - case 2: iq[jz - 1] &= 0x3fffff; - } - } - if (ih == 2) - { - z = 1.0 - z; - if (carry != 0) z -= nolibc::_scalbn(1.0, q0); - } - } - - // check if recomputation is needed - if (z == 0.0) - { - j = 0; - for (int i = jz - 1; i >= jk; i--) j |= iq[i]; - if (j == 0) - { - /* need recomputation */ - int k = 1; - for (; iq[jk - k] == 0; k++); /* k = no. of terms needed */ - - for (int i = jz + 1; i <= jz + k; i++) - { - /* add q[jz+1] to q[jz+k] */ - f[jx + i] = (double)IPIO2[jv + i]; - for (j = 0, fw = 0.0; j <= jx; j++) fw += x[j] * f[jx + i - j]; - q[i] = fw; - } - jz += k; - continue; - } - } - break; - } - - /* chop off zero terms */ - if (z == 0.0) - { - jz -= 1; - q0 -= 24; - while (iq[jz] == 0) - { - jz--; - q0 -= 24; - } - } - else - { - /* break z into 24-bit if necessary */ - z = _scalbn(z,-q0); - if (z >= 0x1p24) - { - fw = (double)(int)(0x1p-24 * z); - iq[jz] = (int)(z - 0x1p24 * fw); - jz += 1; - q0 += 24; - iq[jz] = (int)fw; - } - else - { - iq[jz] = (int)z; - } - } - - /* convert integer "bit" chunk to floating-point value */ - fw = nolibc::_scalbn(1.0 , q0); - for (int i = jz; i >= 0; i --) - { - q[i] = fw * (double)iq[i]; - fw *= 0x1p-24; - } - - /* compute PIO2[0,...,jp]*q[jz,...,0] */ - for (int i = jz; i >= 0; i--) - { - for (fw = 0.0, int k = 0; k <= jp && k <= jz-i; k++) fw += PIO2[k] * q[i + k]; - fq[jz - i] = fw; - } - - /* compress fq[] into y[] */ - switch (prec) - { - case 0: - fw = 0.0; - for (int i = jz; i >= 0; i--) fw += fq[i]; - y[0] = ih == 0 ? fw : -fw; - case 1: - case 2: - fw = 0.0; - for (int i = jz; i >= 0; i--) fw += fq[i]; - // TODO: drop excess precision here once double_t is used - fw = (double)fw; - y[0] = ih == 0 ? fw : -fw; - fw = fq[0]-fw; - for (int i = 1; i <= jz; i++) fw += fq[i]; - y[1] = ih == 0 ? fw : -fw; - case 3: /* painful */ - for (int i = jz; i > 0; i--) - { - fw = fq[i - 1] + fq[i]; - fq[i] += fq[i - 1] - fw; - fq[i - 1] = fw; - } - for (int i = jz; i > 1; i--) - { - fw = fq[i-1] + fq[i]; - fq[i] += fq[i - 1] - fw; - fq[i - 1] = fw; - } - for (fw = 0.0, int i = jz; i >= 2; i--) fw += fq[i]; - if (ih == 0) - { - y[0] = fq[0]; - y[1] = fq[1]; - y[2] = fw; - } - else - { - y[0] = -fq[0]; - y[1] = -fq[1]; - y[2] = -fw; - } - } - return n & 7; -} - -/* origin: FreeBSD /usr/src/lib/msun/src/e_rem_pio2.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - * - * Optimized by Bruce D. Evans. - */ -/* - * invpio2: 53 bits of 2/pi - * pio2_1: first 33 bit of pi/2 - * pio2_1t: pi/2 - pio2_1 - * pio2_2: second 33 bit of pi/2 - * pio2_2t: pi/2 - (pio2_1+pio2_2) - * pio2_3: third 33 bit of pi/2 - * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3) - */ - -<* caller must handle the case when reduction is not needed: |x| ~<= pi/4 *> -fn int __rem_pio2(double x, double *y) -{ - const double PIO4 = 0x1.921fb54442d18p-1; - const double INVPIO2 = 6.36619772367581382433e-01; /* 0x3FE45F30, 0x6DC9C883 */ - const double PIO2_1 = 1.57079632673412561417e+00; /* 0x3FF921FB, 0x54400000 */ - const double PIO2_1T = 6.07710050650619224932e-11; /* 0x3DD0B461, 0x1A626331 */ - const double PIO2_2 = 6.07710050630396597660e-11; /* 0x3DD0B461, 0x1A600000 */ - const double PIO2_2T = 2.02226624879595063154e-21; /* 0x3BA3198A, 0x2E037073 */ - const double PIO2_3 = 2.02226624871116645580e-21; /* 0x3BA3198A, 0x2E000000 */ - const double PIO2_3T = 8.47842766036889956997e-32; /* 0x397B839A, 0x252049C1 */ - - ulong u = bitcast(x, ulong); - int sign = (int)(u >> 63); - uint ix = (uint)(u >> 32 & 0x7fffffff); - switch - { - // |x| ~<= 5pi/4 - case ix <= 0x400f6a7a: - // |x| ~= pi/2 or 2pi/2 - if ((ix & 0xfffff) == 0x921fb) - { - // cancellation -- use medium case - break; - } - // |x| ~<= 3pi/4 - if (ix <= 0x4002d97c) - { - if (!sign) - { - double z = x - PIO2_1; /* one round good to 85 bits */ - y[0] = z - PIO2_1T; - y[1] = (z - y[0]) - PIO2_1T; - return 1; - } - double z = x + PIO2_1; - y[0] = z + PIO2_1T; - y[1] = (z - y[0]) + PIO2_1T; - return -1; - } - if (!sign) - { - double z = x - 2 * PIO2_1; - y[0] = z - 2 * PIO2_1T; - y[1] = (z - y[0]) - 2 * PIO2_1T; - return 2; - } - double z = x + 2 * PIO2_1; - y[0] = z + 2 * PIO2_1T; - y[1] = (z - y[0]) + 2 * PIO2_1T; - return -2; - // |x| ~<= 9pi/4 - case ix <= 0x401c463b: - // |x| ~<= 7pi/4 - if (ix <= 0x4015fdbc) - { - // |x| ~= 3pi/2 - if (ix == 0x4012d97c) break; // Use medium - - if (!sign) - { - double z = x - 3 * PIO2_1; - y[0] = z - 3 * PIO2_1T; - y[1] = (z - y[0]) - 3 * PIO2_1T; - return 3; - } - double z = x + 3 * PIO2_1; - y[0] = z + 3 * PIO2_1T; - y[1] = (z - y[0]) + 3 * PIO2_1T; - return -3; - } - // |x| ~= 4pi/2 - if (ix == 0x401921fb) break; // Use medium - if (!sign) - { - double z = x - 4 * PIO2_1; - y[0] = z - 4 * PIO2_1T; - y[1] = (z - y[0]) - 4 * PIO2_1T; - return 4; - } - double z = x + 4*PIO2_1; - y[0] = z + 4 * PIO2_1T; - y[1] = (z - y[0]) + 4 * PIO2_1T; - return -4; - case ix < 0x413921fb: - break; // medium - case ix >= 0x7ff00000: - // x is inf or NaN - y[0] = y[1] = x - x; - return 0; - default: - // all other (large) arguments - // set z = scalbn(|x|,-ilogb(x)+23) - u = bitcast(x, ulong); - u &= ((ulong)-1) >> 12; - u |= (ulong)(0x3ff + 23) << 52; - double z = bitcast(u, double); - double[3] tx; - int i; - for (i = 0; i < 2; i++) - { - tx[i] = (double)(int)z; - z = (z - tx[i]) * 0x1p24; - } - tx[i] = z; - // skip zero terms, first term is non-zero - while (tx[i] == 0.0) i--; - double[2] ty; - int n = __rem_pio2_large(&tx, &ty, (int)(ix >> 20) - (0x3ff + 23), i + 1, 1); - if (sign) - { - y[0] = -ty[0]; - y[1] = -ty[1]; - return -n; - } - y[0] = ty[0]; - y[1] = ty[1]; - return n; - } - - // |x| ~< 2^20*(pi/2), medium size - // rint(x/(pi/2)) - double fnn = (double)x * INVPIO2 + TOINT15 - TOINT15; - int n = (int)fnn; - double r = x - fnn * PIO2_1; - double w = fnn * PIO2_1T; // 1st round, good to 85 bits - // Matters with directed rounding. - if (r - w < -PIO4) /*@unlikely*/ - { - n--; - fnn--; - r = x - fnn * PIO2_1; - w = fnn * PIO2_1T; - } - else if (r - w > PIO4) /*@unlikely*/ - { - n++; - fnn++; - r = x - fnn * PIO2_1; - w = fnn * PIO2_1T; - } - y[0] = r - w; - u = bitcast(y[0], ulong); - int ey = (int)(u >> 52 & 0x7ff); - int ex = (int)(ix >> 20); - if (ex - ey > 16) - { - // 2nd round, good to 118 bits - double t = r; - w = fnn * PIO2_2; - r = t - w; - w = fnn * PIO2_2T - ((t - r) - w); - y[0] = r - w; - u = bitcast(y[0], ulong); - ey = (int)(u >> 52 & 0x7ff); - if (ex - ey > 49) - { - // 3rd round, good to 151 bits, covers all cases - t = r; - w = fnn * PIO2_3; - r = t - w; - w = fnn * PIO2_3T - ((t - r) - w); - y[0] = r - w; - } - } - y[1] = (r - y[0]) - w; - return n; - - -} diff --git a/lib7/std/math/math_nolibc/round.c3 b/lib7/std/math/math_nolibc/round.c3 deleted file mode 100644 index 31ea82e58..000000000 --- a/lib7/std/math/math_nolibc/round.c3 +++ /dev/null @@ -1,53 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn double _round(double x) @extern("round") @weak @nostrip -{ - ulong u = bitcast(x, ulong); - int e = (int)((u >> 52) & 0x7ff); - if (e >= 0x3ff + 52) return x; - if (u >> 63) x = -x; - if (e < 0x3ff - 1) - { - /* raise inexact if x!=0 */ - force_eval_add(x, TOINT); - return 0 * x; - } - double y = (x + TOINT) - TOINT - x; - switch - { - case y > 0.5: - y = y + x - 1; - case y <= -0.5: - y = y + x + 1; - default: - y = y + x; - } - if (u >> 63) y = -y; - return y; -} - -fn float _roundf(float x) @extern("roundf") @weak @nostrip -{ - uint u = bitcast(x, uint); - int e = (u >> 23) & 0xff; - if (e >= 0x7f + 23) return x; - if (u >> 31) x = -x; - if (e < 0x7f - 1) - { - force_eval_add(x, TOINTF); - return 0 * x; - } - float y = (x + TOINTF) - TOINTF - x; - switch - { - case y > 0.5f: - y = y + x - 1; - case y <= -0.5f: - y = y + x + 1; - default: - y = y + x; - } - if (u >> 31) y = -y; - return y; -} - diff --git a/lib7/std/math/math_nolibc/scalbn.c3 b/lib7/std/math/math_nolibc/scalbn.c3 deleted file mode 100644 index ccd251c06..000000000 --- a/lib7/std/math/math_nolibc/scalbn.c3 +++ /dev/null @@ -1,27 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn double _scalbn(double x, int n) @weak @extern("scalbn") @nostrip -{ - switch - { - case n > 1023: - x *= 0x1p1023; - n -= 1023; - if (n > 1023) - { - x *= 0x1p1023; - n -= 1023; - if (n > 1023) n = 1023; - } - case n < -1022: - x *= 0x1p-1022 * 0x1p53; - n += 1022 - 53; - if (n < -1022) - { - x *= 0x1p-1022 * 0x1p53; - n += 1022 - 53; - if (n < -1022) n = -1022; - } - } - return x * bitcast(((ulong)(0x3ff + n)) << 52, double); -} diff --git a/lib7/std/math/math_nolibc/sin.c3 b/lib7/std/math/math_nolibc/sin.c3 deleted file mode 100644 index d6cf7aece..000000000 --- a/lib7/std/math/math_nolibc/sin.c3 +++ /dev/null @@ -1,117 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD /usr/src/lib/msun/src/s_sinf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - * Optimized by Bruce D. Evans. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -fn float _sinf(float x) @weak @extern("sinf") @nostrip -{ - uint ix = x.word(); - int sign = ix >> 31; - ix &= 0x7fffffff; - switch - { - case ix <= 0x3f490fda: - // |x| ~<= pi/4 - if (ix < 0x39800000) - { - /* |x| < 2**-12 */ - // raise inexact if x!=0 and underflow if subnormal - if (ix < 0x00800000) - { - @volatile_load(x) / 0x1p120f; - } - else - { - @volatile_load(x) + 0x1p120f; - } - return x; - } - return __sindf(x); - case ix <= 0x407b53d1: - // |x| ~<= 5*pi/4 - if (ix <= 0x4016cbe3) - { - // |x| ~<= 3pi/4 - if (sign) return -__cosdf(x + S1PI2); - return __cosdf(x - S1PI2); - } - return __sindf(sign ? -(x + S2PI2) : -(x - S2PI2)); - case ix <= 0x40e231d5: - // |x| ~<= 9*pi/4 - if (ix <= 0x40afeddf) - { - // |x| ~<= 7*pi/4 - return sign ? __cosdf(x + S3PI2) : -__cosdf(x - S3PI2); - } - return __sindf(sign ? x + S4PI2 : x - S4PI2); - case ix >= 0x7f800000: - // sin(Inf or NaN) is NaN - return x - x; - } - /* general argument reduction needed */ - double y @noinit; - switch (__rem_pio2f(x, &y) & 3) - { - case 0: return __sindf(y); - case 1: return __cosdf(y); - case 2: return __sindf(-y); - default: return -__cosdf(y); - } -} - -/* origin: FreeBSD /usr/src/lib/msun/src/s_sin.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -fn double sin(double x) @extern("sin") @weak @nostrip -{ - // High word of x. - uint ix = x.high_word() & 0x7fffffff; - switch - { - // |x| ~< pi/4 - case ix <= 0x3fe921fb: - // |x| < 2**-26 - if (ix < 0x3e500000) - { - // raise inexact if x != 0 and underflow if subnormal - force_eval_add(x, ix < 0x00100000 ? -0x1p120f : 0x1p120f); - return x; - } - return __sin(x, 0.0, 0); - // sin(Inf or NaN) is NaN - case ix >= 0x7ff00000: - return x - x; - default: - // argument reduction needed - double[2] y; - switch (__rem_pio2(x, &y) & 3) - { - case 0: return __sin(y[0], y[1], 1); - case 1: return __cos(y[0], y[1]); - case 2: return -__sin(y[0], y[1], 1); - default: return -__cos(y[0], y[1]); - } - } -} diff --git a/lib7/std/math/math_nolibc/sincos.c3 b/lib7/std/math/math_nolibc/sincos.c3 deleted file mode 100644 index 15b744ea9..000000000 --- a/lib7/std/math/math_nolibc/sincos.c3 +++ /dev/null @@ -1,154 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD /usr/src/lib/msun/src/s_sinf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - * Optimized by Bruce D. Evans. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -fn void sincosf(float x, float *sin, float *cos) @extern("__sincosf") @weak @nostrip -{ - uint ix = x.word(); - uint sign = ix >> 31; - ix &= 0x7fffffff; - - switch - { - // |x| ~<= pi/4 - case ix <= 0x3f490fda: - // |x| < 2**-12 - if (ix < 0x39800000) - { - // raise inexact if x!=0 and underflow if subnormal - float z; - @volatile_store(z, ix < 0x00100000 ? @volatile_load(x) / 0x1p120f : @volatile_load(x) + 0x1p120f); - *sin = x; - *cos = 1.0f; - return; - } - *sin = __sindf(x); - *cos = __cosdf(x); - return; - // |x| ~<= 5*pi/4 - case ix <= 0x407b53d1: - // |x| ~<= 3pi/4 - if (ix <= 0x4016cbe3) - { - if (sign) - { - *sin = -__cosdf(x + S1PI2); - *cos = __sindf(x + S1PI2); - return; - } - *sin = __cosdf(S1PI2 - x); - *cos = __sindf(S1PI2 - x); - return; - } - /* -sin(x+c) is not correct if x+c could be 0: -0 vs +0 */ - *sin = -__sindf(sign ? x + S2PI2 : x - S2PI2); - *cos = -__cosdf(sign ? x + S2PI2 : x - S2PI2); - return; - case ix <= 0x40e231d5: - // |x| ~<= 9*pi/4 - if (ix <= 0x40afeddf) - { - // |x| ~<= 7*pi/4 - if (sign) - { - *sin = __cosdf(x + S3PI2); - *cos = -__sindf(x + S3PI2); - return; - } - *sin = -__cosdf(x - S3PI2); - *cos = __sindf(x - S3PI2); - return; - } - *sin = __sindf(sign ? x + S4PI2 : x - S4PI2); - *cos = __cosdf(sign ? x + S4PI2 : x - S4PI2); - case ix >= 0x7f800000: - // sin(Inf or NaN) is NaN - *sin = *cos = x - x; - default: - // general argument reduction needed - double y @noinit; - uint n = __rem_pio2f(x, &y); - float s = __sindf(y); - float c = __cosdf(y); - switch (n & 3) - { - case 0: - *sin = s; - *cos = c; - case 1: - *sin = c; - *cos = -s; - case 2: - *sin = -s; - *cos = -c; - case 3: - default: - *sin = -c; - *cos = s; - } - } - -} - -fn void sincos(double x, double *sin, double *cos) @extern("__sincos") @weak @nostrip -{ - // High word of x. - uint ix = x.high_word() & 0x7fffffff; - - // |x| ~< pi/4 - switch - { - // |x| ~< pi/4 - case ix <= 0x3fe921fb: - if (ix < 0x3e46a09e) - { - // raise inexact if x!=0 and underflow if subnormal - force_eval_add(ix < 0x00100000 ? x/0x1p120f : x+0x1p120f, 0); - *sin = x; - *cos = 1.0; - return; - } - // if |x| < 2**-27 * sqrt(2) - *sin = __sin(x, 0.0, 0); - *cos = __cos(x, 0.0); - // sincos(Inf or NaN) is NaN - case ix >= 0x7ff00000: - *sin = *cos = x - x; - default: - // argument reduction needed - double[2] y; - int n = __rem_pio2(x, &y); - double s = __sin(y[0], y[1], 1); - double c = __cos(y[0], y[1]); - switch (n & 3) - { - case 0: - *sin = s; - *cos = c; - case 1: - *sin = c; - *cos = -s; - case 2: - *sin = -s; - *cos = -c; - case 3: - default: - *sin = -c; - *cos = s; - } - } -} diff --git a/lib7/std/math/math_nolibc/tan.c3 b/lib7/std/math/math_nolibc/tan.c3 deleted file mode 100644 index 056329492..000000000 --- a/lib7/std/math/math_nolibc/tan.c3 +++ /dev/null @@ -1,100 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -/* origin: FreeBSD /usr/src/lib/msun/src/s_tan.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -fn double tan(double x) @extern("tan") @weak @nostrip -{ - uint ix = x.high_word(); - ix &= 0x7fffffff; - - // |x| ~< pi/4 - switch - { - case ix <= 0x3fe921fb: - if (ix < 0x3e400000) - { - // |x| < 2**-27 - /* raise inexact if x!=0 and underflow if subnormal */ - force_eval_add(ix < 0x00100000 ? x / 0x1p120f : x + 0x1p120f, 0); - return x; - } - return __tan(x, 0.0, 0); - // tan(Inf or NaN) is NaN - case ix >= 0x7ff00000: - return x - x; - default: - // argument reduction - double[2] y; - uint n = __rem_pio2(x, &y); - return __tan(y[0], y[1], n & 1); - - } -} - -/* origin: FreeBSD /usr/src/lib/msun/src/s_tanf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - * Optimized by Bruce D. Evans. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -fn float tanf(float x) @extern("tanf") @weak @nostrip -{ - uint ix = x.word(); - uint sign = ix >> 31; - ix &= 0x7fffffff; - - switch - { - // |x| ~<= pi/4 - case ix <= 0x3f490fda: - // |x| < 2**-12 - if (ix < 0x39800000) - { - /* raise inexact if x!=0 and underflow if subnormal */ - force_eval_add(ix < 0x00800000 ? x / 0x1p120f : x + 0x1p120f, 0); - return x; - } - return __tandf(x, 0); - // |x| ~<= 5*pi/4 - case ix <= 0x407b53d1: - // |x| ~<= 3pi/4 - return ix <= 0x4016cbe3 - ? __tandf(sign ? x + S1PI2 : x - S1PI2, 1) - : __tandf(sign ? x + S2PI2 : x - S2PI2, 0); - // |x| ~<= 9*pi/4 - case ix <= 0x40e231d5: - // |x| ~<= 7*pi/4 - return ix <= 0x40afeddf - ? __tandf(sign ? x + S3PI2 : x - S3PI2, 1) - : __tandf(sign ? x + S4PI2 : x - S4PI2, 0); - // tan(Inf or NaN) is NaN - case ix >= 0x7f800000: - return x - x; - default: - // argument reduction - double y; - uint n = __rem_pio2f(x, &y); - return __tandf(y, n & 1); - } -} - diff --git a/lib7/std/math/math_nolibc/trig.c3 b/lib7/std/math/math_nolibc/trig.c3 deleted file mode 100644 index d3b0d7279..000000000 --- a/lib7/std/math/math_nolibc/trig.c3 +++ /dev/null @@ -1,8 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn double sincos_broken(double x) @extern("sincos") @weak @nostrip -{ - unreachable("'sinccos' not supported"); -} - - diff --git a/lib7/std/math/math_nolibc/trunc.c3 b/lib7/std/math/math_nolibc/trunc.c3 deleted file mode 100644 index c9fc4077f..000000000 --- a/lib7/std/math/math_nolibc/trunc.c3 +++ /dev/null @@ -1,28 +0,0 @@ -module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH)); - -fn double _trunc(double x) @weak @extern("trunc") @nostrip -{ - ulong i = bitcast(x, ulong); - int e = (int)((i >> 52) & 0x7ff) - 0x3ff + 12; - if (e >= 52 + 12) return x; - if (e < 12) e = 1; - ulong m = ((ulong)-1) >> e; - if (i & m == 0) return x; - force_eval_add(x, 0x1p120f); - i &= ~m; - return bitcast(i, double); -} - -fn float _truncf(float x) @weak @extern("truncf") @nostrip -{ - uint i = bitcast(x, uint); - int e = (int)((i >> 23) & 0xff) - 0x7f + 9; - if (e >= 23 + 9) return x; - if (e < 9) e = 1; - uint m = ((uint)-1) >> e; - if (i & m == 0) return x; - force_eval_add(x, 0x1p120f); - i &= ~m; - return bitcast(i, float); -} - diff --git a/lib7/std/math/math_quaternion.c3 b/lib7/std/math/math_quaternion.c3 deleted file mode 100644 index 18a7940ef..000000000 --- a/lib7/std/math/math_quaternion.c3 +++ /dev/null @@ -1,85 +0,0 @@ -module std::math::quaternion{Real}; -import std::math::vector; -union Quaternion -{ - struct - { - Real i, j, k, l; - } - Real[<4>] v; -} - -const Quaternion IDENTITY = { 0, 0, 0, 1 }; - -macro Quaternion Quaternion.add(Quaternion a, Quaternion b) => { .v = a.v + b.v }; -macro Quaternion Quaternion.add_each(Quaternion a, Real b) => { .v = a.v + b }; -macro Quaternion Quaternion.sub(Quaternion a, Quaternion b) => { .v = a.v - b.v }; -macro Quaternion Quaternion.sub_each(Quaternion a, Real b) => { .v = a.v - b }; -macro Quaternion Quaternion.scale(Quaternion a, Real s) => { .v = a.v * s }; -macro Quaternion Quaternion.normalize(Quaternion q) => { .v = q.v.normalize() }; -macro Real Quaternion.length(Quaternion q) => q.v.length(); -macro Quaternion Quaternion.lerp(Quaternion q1, Quaternion q2, Real amount) => { .v = q1.v.lerp(q2.v, amount) }; -macro Matrix4f Quaternion.to_matrixf(Quaternion* q) => into_matrix(q, Matrix4f); -macro Matrix4 Quaternion.to_matrix(Quaternion* q) => into_matrix(q, Matrix4); -fn Quaternion Quaternion.nlerp(Quaternion q1, Quaternion q2, Real amount) => { .v = q1.v.lerp(q2.v, amount).normalize() }; - -fn Quaternion Quaternion.invert(q) -{ - Real length_sq = q.v.dot(q.v); - if (length_sq <= 0) return q; - Real inv_length = 1 / length_sq; - return { q.v[0] * -inv_length, q.v[1] * -inv_length, q.v[2] * -inv_length, q.v[3] * inv_length }; -} - -fn Quaternion Quaternion.slerp(q1, Quaternion q2, Real amount) -{ - Quaternion result = {}; - - Real[<4>] q2v = q2.v; - Real cos_half_theta = q1.v.dot(q2v); - - if (cos_half_theta < 0) - { - q2v = -q2v; - cos_half_theta = -cos_half_theta; - } - - if (cos_half_theta >= 1) return q1; - - Real[<4>] q1v = q1.v; - if (cos_half_theta > 0.95f) return { .v = q1v.lerp(q2v, amount) }; - - Real half_theta = math::cos(cos_half_theta); - Real sin_half_theta = math::sqrt(1 - cos_half_theta * cos_half_theta); - if (math::abs(sin_half_theta) < 0.001f) - { - return { .v = (q1v + q2v) * 0.5f }; - } - Real ratio_a = math::sin((1 - amount) * half_theta) / sin_half_theta; - Real ratio_b = math::sin(amount * half_theta) / sin_half_theta; - return { .v = q1v * ratio_a + q2v * ratio_b }; -} - -fn Quaternion Quaternion.mul(a, Quaternion b) -{ - return { a.i * b.l + a.l * b.i + a.j * b.k - a.k * b.j, - a.j * b.l + a.l * b.j + a.k * b.i - a.i * b.k, - a.k * b.l + a.l * b.k + a.i * b.j - a.j * b.i, - a.l * b.l - a.i * b.i - a.j * a.j - a.k * a.k }; -} - -macro into_matrix(Quaternion* q, $Type) @private -{ - Quaternion rotation = q.normalize(); - var x = rotation.i; - var y = rotation.j; - var z = rotation.k; - var w = rotation.l; - - return ($Type) { - 1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0, - 2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0, - 2*x*z - 2*y*w, 2*y*z + 2*x*w , 1 - 2*x*x - 2*y*y, 0, - 0.0, 0.0, 0.0, 1.0, - }; -} \ No newline at end of file diff --git a/lib7/std/math/math_random.c3 b/lib7/std/math/math_random.c3 deleted file mode 100644 index b457e407d..000000000 --- a/lib7/std/math/math_random.c3 +++ /dev/null @@ -1,184 +0,0 @@ -<* - Randoms: - General usage - - 1. Create a Random (see std/math/random for various alternatives), or pick DefaultRandom - 2. Define it `DefaultRandom my_random;` - 3. Seed it: `random::seed(&my_random, some_seed);` or `rand::seed_entropy(&my_random);` - 4. Use it with the functions in random: `float random_float = random::next_float(&my_random);` - - For just a simple integer between 0 and n (not including n), you can use `rand(n)`. -*> -module std::math::random; - -<* - @require is_random(random) -*> -macro void seed(random, seed) -{ - random.set_seed(@as_char_view(seed)); -} - -<* - Seed the random with some best effort entropy. - - @require is_random(random) -*> -macro void seed_entropy(random) -{ - random.set_seed(&&entropy()); -} - -<* - Get the next value between 0 and range (not including range). - - @require is_random(random) - @require range > 0 -*> -macro int next(random, uint range) -{ - if (range == 1) return 0; - uint mask = ~0U; - range--; - mask >>= range.clz(); - uint x @noinit; - do - { - x = random.next_int() & mask; - } - while (x > range); - return x; -} - -<* - Get a random in the range [min, max], both included. - - @require is_random(random) - @require max >= min -*> -macro int next_in_range(random, int min, int max) -{ - return next(random, max - min + 1) + min; -} - -def DefaultRandom = Sfc64Random; - -tlocal Sfc64Random default_random @private; -tlocal bool default_random_initialized @private = false; - -<* -Seed the default random function. -*> -fn void srand(ulong seed) @builtin -{ - default_random.set_seed(@as_char_view(seed)); - default_random_initialized = true; -} - -<* -Get a default random value between 0 and range (not including range) -*> -fn int rand(int range) @builtin -{ - init_default_random(); - return next(&default_random, range); -} - -<* -Get a random in the range, both included. - @require max >= min -*> -fn int rand_in_range(int min, int max) @builtin -{ - init_default_random(); - return next_in_range(&default_random, min, max); -} - -fn double rnd() @builtin -{ - init_default_random(); - ulong val = default_random.next_long() & (1UL << 53 - 1); - return val * 0x1.0p-53; -} - -<* - Get 'true' or 'false' - - @require is_random(random) -*> -macro bool next_bool(random) -{ - return (bool)(random.next_byte() & 1); -} - -<* - Get a float between 0 and 1.0, not including 1.0. - - @require is_random(random) -*> -macro float next_float(random) -{ - uint val = random.next_int() & (1 << 24 - 1); - return val / (float)(1 << 24); -} - -<* - Get a double between 0 and 1.0, not including 1.0. - - @require is_random(random) -*> -macro double next_double(random) -{ - ulong val = random.next_long() & (1UL << 53 - 1); - return val * 0x1.0p-53; -} - -// True if the value is a Random. -macro bool is_random(random) => $assignable(random, Random); - -macro uint128 @long_to_int128(#function) => (uint128)#function << 64 + #function; -macro ulong @int_to_long(#function) => (ulong)#function << 32 + #function; -macro uint @short_to_int(#function) => (uint)#function << 16 + #function; -macro ushort @char_to_short(#function) => (ushort)#function << 8 + #function; - -macro @random_value_to_bytes(#function, char[] bytes) -{ - var $byte_size = $sizeof(#function()); - usz len = bytes.len; - // Same size or smaller? Then just copy. - while (len > 0) - { - var value = #function(); - if (len <= $byte_size) - { - bytes[..] = ((char*)&value)[:len]; - return; - } - bytes[:$byte_size] = ((char*)&value)[:$byte_size]; - len -= $byte_size; - bytes = bytes[$byte_size..]; - } - unreachable(); -} - -// This is the interface to implement for Random implementations, usually -// it is not invoked directly. -interface Random -{ - fn void set_seed(char[] input); - fn char next_byte(); - fn ushort next_short(); - fn uint next_int(); - fn ulong next_long(); - fn uint128 next_int128(); - fn void next_bytes(char[] buffer); -} - - -macro init_default_random() @private -{ - if (!default_random_initialized) - { - seed_entropy(&default_random); - default_random_initialized = true; - } -} diff --git a/lib7/std/math/math_vector.c3 b/lib7/std/math/math_vector.c3 deleted file mode 100644 index 1d686a8cc..000000000 --- a/lib7/std/math/math_vector.c3 +++ /dev/null @@ -1,245 +0,0 @@ -module std::math::vector; -import std::math; - -def Vec2f = float[<2>]; -def Vec3f = float[<3>]; -def Vec4f = float[<4>]; - -def Vec2 = double[<2>]; -def Vec3 = double[<3>]; -def Vec4 = double[<4>]; - -macro Vec2f.length_sq(self) => self.dot(self); -macro Vec3f.length_sq(self) => self.dot(self); -macro Vec4f.length_sq(self) => self.dot(self); -macro Vec2.length_sq(self) => self.dot(self); -macro Vec3.length_sq(self) => self.dot(self); -macro Vec4.length_sq(self) => self.dot(self); - -macro Vec2f.distance_sq(self, Vec2f v2) => (self - v2).length_sq(); -macro Vec3f.distance_sq(self, Vec3f v2) => (self - v2).length_sq(); -macro Vec4f.distance_sq(self, Vec4f v2) => (self - v2).length_sq(); -macro Vec2.distance_sq(self, Vec2 v2) => (self - v2).length_sq(); -macro Vec3.distance_sq(self, Vec3 v2) => (self - v2).length_sq(); -macro Vec4.distance_sq(self, Vec4 v2) => (self - v2).length_sq(); - -macro Vec2f.transform(self, Matrix4f mat) => transform2(self, mat); -macro Vec2f.rotate(self, float angle) => rotate(self, angle); -macro Vec2f.angle(self, Vec2f v2) => math::atan2(v2.y, v2.x) - math::atan2(self.y, self.x); - -macro Vec2.transform(self, Matrix4 mat) => transform2(self, mat); -macro Vec2.rotate(self, double angle) => rotate(self, angle); -macro Vec2.angle(self, Vec2 v2) => math::atan2(v2.y, v2.x) - math::atan2(self.y, self.x); - -macro Vec2f.clamp_mag(self, float min, float max) => clamp_magnitude(self, min, max); -macro Vec3f.clamp_mag(self, float min, float max) => clamp_magnitude(self, min, max); -macro Vec4f.clamp_mag(self, float min, float max) => clamp_magnitude(self, min, max); -macro Vec2.clamp_mag(self, double min, double max) => clamp_magnitude(self, min, max); -macro Vec3.clamp_mag(self, double min, double max) => clamp_magnitude(self, min, max); -macro Vec4.clamp_mag(self, double min, double max) => clamp_magnitude(self, min, max); - -fn Vec2f Vec2f.towards(self, Vec2f target, float max_distance) => towards(self, target, max_distance); -fn Vec3f Vec3f.towards(self, Vec3f target, float max_distance) => towards(self, target, max_distance); -fn Vec4f Vec4f.towards(self, Vec4f target, float max_distance) => towards(self, target, max_distance); -fn Vec2 Vec2.towards(self, Vec2 target, double max_distance) => towards(self, target, max_distance); -fn Vec3 Vec3.towards(self, Vec3 target, double max_distance) => towards(self, target, max_distance); -fn Vec4 Vec4.towards(self, Vec4 target, double max_distance) => towards(self, target, max_distance); - -fn Vec3f Vec3f.cross(self, Vec3f v2) => cross3(self, v2); -fn Vec3 Vec3.cross(self, Vec3 v2) => cross3(self, v2); - -fn Vec3f Vec3f.perpendicular(self) => perpendicular3(self); -fn Vec3 Vec3.perpendicular(self) => perpendicular3(self); - -fn Vec3f Vec3f.barycenter(self, Vec3f a, Vec3f b, Vec3f c) => barycenter3(self, a, b, c); -fn Vec3 Vec3.barycenter(self, Vec3 a, Vec3 b, Vec3 c) => barycenter3(self, a, b, c); - -fn Vec3f Vec3f.transform(self, Matrix4f mat) => transform3(self, mat); -fn Vec3 Vec3.transform(self, Matrix4 mat) => transform3(self, mat); - -fn float Vec3f.angle(self, Vec3f v2) => angle3(self, v2); -fn double Vec3.angle(self, Vec3 v2) => angle3(self, v2); - -fn Vec3f Vec3f.refract(self, Vec3f n, float r) => refract3(self, n, r); -fn Vec3 Vec3.refract(self, Vec3 n, double r) => refract3(self, n, r); - -fn void ortho_normalize(Vec3f* v1, Vec3f* v2) => ortho_normalize3(v1, v2); -fn void ortho_normalized(Vec3* v1, Vec3* v2) => ortho_normalize3(v1, v2); - -fn Matrix4f matrix4f_look_at(Vec3f eye, Vec3f target, Vec3f up) @deprecated => matrix::look_at{float}(eye, target, up); -fn Matrix4 matrix4_look_at(Vec3 eye, Vec3 target, Vec3 up) @deprecated => matrix::look_at{double}(eye, target, up); - -fn Vec3f Vec3f.rotate_quat(self, Quaternionf q) => rotate_by_quat3(self, q); -fn Vec3 Vec3.rotate_quat(self, Quaternion q) => rotate_by_quat3(self, q); - -fn Vec3f Vec3f.rotate_axis(self, Vec3f axis, float angle) => rotate_axis_angle(self, axis, angle); -fn Vec3 Vec3.rotate_axis(self, Vec3 axis, double angle) => rotate_axis_angle(self, axis, angle); - -fn Vec3f Vec3f.unproject(self, Matrix4f projection, Matrix4f view) => unproject3(self, projection, view); -fn Vec3 Vec3.unproject(self, Matrix4 projection, Matrix4 view) => unproject3(self, projection, view); - -macro towards(v, target, max_distance) @private -{ - var delta = target - v; - var square = delta.length_sq(); - - if (square == 0 || (max_distance >= 0 && (square <= max_distance * max_distance))) return target; - - var dist = math::sqrt(square); - - return v + delta * max_distance / dist; -} - -macro clamp_magnitude(v, min, max) @private -{ - var length = v.dot(v); - if (length > 0) - { - length = math::sqrt(length); - - if (length < min) return v * (min / length); - if (length > max) return v * (max / length); - } - return v; -} - -macro rotate(v, angle) @private -{ - var c = math::cos(angle); - var s = math::sin(angle); - return $typeof(v) { v[0] * c - v[1] * s, v[0] * s + v[1] * c }; -} - -macro perpendicular3(v) @private -{ - var min = math::abs(v[0]); - $typeof(v) cardinal_axis = { 1, 0, 0 }; - - if (var vy = math::abs(v[1]), vy < min) - { - min = vy; - cardinal_axis = { 0, 1, 0 }; - } - - if (var vz = math::abs(v[2]), vz < min) - { - cardinal_axis = { 0, 0, 1 }; - } - - return cross3(v, cardinal_axis); -} - -macro cross3(v1, v2) @private -{ - var a = v1.yzx * v2.zxy; - var b = v1.zxy * v2.yzx; - return a - b; -} - -macro transform2(v, mat) @private -{ - return $typeof(v) { - mat.m00 * v[0] + mat.m10 * v[1] + mat.m30 , - mat.m01 * v[0] + mat.m11 * v[1] + mat.m31 }; -} - -macro transform3(v, mat) @private -{ - return ($typeof(v)){ - mat.m00 * v[0] + mat.m10 * v[1] + mat.m20 * v[2] + mat.m30, - mat.m01 * v[0] + mat.m11 * v[1] + mat.m21 * v[2] + mat.m31, - mat.m02 * v[0] + mat.m12 * v[1] + mat.m22 * v[2] + mat.m32 - }; -} - - -macro angle3(v1, v2) @private -{ - var len = v1.cross(v2).length(); - var dot = v1.dot(v2); - return math::atan2(len, dot); -} - -macro void ortho_normalize3(v1, v2) @private -{ - var v1n = *v1 = v1.normalize(); - var vn1 = v1n.cross(*v2).normalize(); - *v2 = v1n.cross(vn1); -} - -macro rotate_by_quat3(v, q) @private -{ - return ($typeof(v)){ - v[0] * (q.i * q.i + q.l * q.l - q.j * q.j - q.k * q.k) - + v[1] * (2 * q.i * q.j - 2 * q.l * q.k) - + v[2] * (2 * q.i * q.k - 2 * q.l * q.j), - v[0] * (2 * q.l * q.k + 2 * q.i * q.j) - + v[1] * (q.l * q.l - q.i * q.i + q.j * q.j - q.k * q.k) - + v[2] * (-2 * q.l * q.i + 2 * q.j * q.k), - v[0] * (-2 * q.l * q.j + 2 * q.i * q.k) - + v[1] * (2 * q.l * q.i + 2 * q.j * q.k) - + v[2] * (q.l * q.l - q.i * q.i - q.j * q.j + q.k * q.k) - }; -} - -macro rotate_axis_angle(v, axis, angle) @private -{ - axis = axis.normalize(); - - angle /= 2; - var w = axis * math::sin(angle); - var wv = w.cross(v); - var wwv = w.cross(wv); - wv *= math::cos(($typeof(v))angle) * 2; - wwv *= 2; - - return v + wv + wwv; -} - -macro unproject3(v, m1, m2) @private -{ -return v; -/* - var view_proj = m1.mul(m2); - var invert = view_proj.invert(); - // Create quaternion from source point - $if @typeid(v[0]) == float.typeid: - Quaternionf quat = { v.x, v.y, v.z, 1 }; - $else - Quaternion quat = { v.x, v.y, v.z, 1 }; - $endif - - // Multiply quat point by unproject matrix - var qtransformed = quat.transform(invert); - - // Normalized world points in vectors - return { - qtransformed.i / qtransformed.l, - qtransformed.j / qtransformed.l, - qtransformed.k / qtransformed.l - };*/ -} - -macro barycenter3(p, a, b, c) @private -{ - var v0 = b - a; - var v1 = c - a; - var v2 = p - a; - var d00 = v0.dot(v0); - var d01 = v0.dot(v1); - var d11 = v1.dot(v1); - var d20 = v2.dot(v0); - var d21 = v2.dot(v1); - var denom = d00 * d11 - d01 * d01; - var y = (d11 * d20 - d01 * d21) / denom; - var z = (d00 * d21 - d01 * d20) / denom; - return ($typeof(p)){ 1 - y - z, y, z }; -} - -macro refract3(v, n, r) @private -{ - var dot = v.dot(n); - var d = 1 - r * r * (1 - dot * dot); - - return d < 0 ? v : r * v - (r * dot + math::sqrt(d)) * n; -} diff --git a/lib7/std/math/random/math.lcg.c3 b/lib7/std/math/random/math.lcg.c3 deleted file mode 100644 index 79e0fe2b0..000000000 --- a/lib7/std/math/random/math.lcg.c3 +++ /dev/null @@ -1,112 +0,0 @@ -module std::math::random; - -// Move ODD_PHI into a shared module -const ODD_PHI128 @local = 0x9e3779b97f4a7c15f39cc0605cedc835; -const ODD_PHI64 @local = 0x9e3779b97f4a7c15; -const ODD_PHI32 @local = 0x9e3779b9; -const ODD_PHI16 @local = 0x9e37; - -const MUL_LCG128 @local = 0xdb36357734e34abb0050d0761fcdfc15; -const MUL_LCG64 @local = 0xd1342543de82ef95; -const MUL_LCG32 @local = 0x915f77f5; -const MUL_LCG16 @local = 0x915d; // TODO: Find good constant - - -// Lcg128_64 -distinct Lcg128Random (Random) = uint128; - -fn void Lcg128Random.set_seed(&self, char[] input) @dynamic -{ - *self = (Lcg128Random)random::make_seed(uint128, input); -} - - -fn ulong Lcg128Random.next_long(&self) @dynamic -{ - uint128* s = (uint128*)self; - ulong result = (ulong)(*s >> 64); - *s = *s * MUL_LCG128 + ODD_PHI128; - return result; -} - -<* - @require bytes.len > 0 -*> -fn void Lcg128Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_long, bytes); -fn uint128 Lcg128Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn uint Lcg128Random.next_int(&self) @dynamic => (uint)self.next_long(); -fn ushort Lcg128Random.next_short(&self) @dynamic => (ushort)self.next_long(); -fn char Lcg128Random.next_byte(&self) @dynamic => (char)self.next_long(); - - -// -------------------------------- Lcg64_32 -------------------------------- -distinct Lcg64Random (Random) = ulong; - -fn void Lcg64Random.set_seed(&self, char[] seed) @dynamic -{ - *self = (Lcg64Random)random::make_seed(ulong, seed); -} - - -fn uint Lcg64Random.next_int(&self) @dynamic -{ - ulong* s = (ulong*)self; - uint result = (uint)(*s >> 32); - *s = *s * MUL_LCG64 + ODD_PHI64; - return result; -} - -<* - @require bytes.len > 0 -*> -fn void Lcg64Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_int, bytes); -fn uint128 Lcg64Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Lcg64Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn ushort Lcg64Random.next_short(&self) @dynamic => (ushort)self.next_int(); -fn char Lcg64Random.next_byte(&self) @dynamic => (char)self.next_int(); - -// -------------------------------- Lcg32_16 -------------------------------- - -distinct Lcg32Random (Random) = uint; - -fn void Lcg32Random.set_seed(&self, char[] seed) @dynamic -{ - *self = (Lcg32Random)random::make_seed(uint, seed); -} - -fn ushort Lcg32Random.next_short(&self) @dynamic -{ - uint* s = (uint*)self; - ushort result = (ushort)(*s >> 16); - *s = *s * MUL_LCG32 + ODD_PHI32; - return result; -} - -fn void Lcg32Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_short, bytes); -fn uint128 Lcg32Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Lcg32Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn uint Lcg32Random.next_int(&self) @dynamic => @short_to_int(self.next_short()); -fn char Lcg32Random.next_byte(&self) @dynamic => (char)self.next_short(); - - -// -------------------------------- Lcg16_8 -------------------------------- -distinct Lcg16Random (Random) = ushort; - -fn void Lcg16Random.set_seed(&self, char[] seed) @dynamic -{ - *self = (Lcg16Random)random::make_seed(ushort, seed); -} - -fn char Lcg16Random.next_byte(&self) @dynamic -{ - ushort* s = (ushort*)self; - char result = (char)(*s >> 8); - *s = *s * MUL_LCG16 + ODD_PHI16; - return result; -} - -fn void Lcg16Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_byte, bytes); -fn uint128 Lcg16Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Lcg16Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn uint Lcg16Random.next_int(&self) @dynamic => @short_to_int(self.next_short()); -fn ushort Lcg16Random.next_short(&self) @dynamic => @char_to_short(self.next_byte()); diff --git a/lib7/std/math/random/math.mcg.c3 b/lib7/std/math/random/math.mcg.c3 deleted file mode 100644 index 4c57787c4..000000000 --- a/lib7/std/math/random/math.mcg.c3 +++ /dev/null @@ -1,119 +0,0 @@ -module std::math::random; - -const MUL_MCG128 @local = 0x9e3779b97f4a7c15f39cc0605cedc835; -const MUL_MCG64 @local = 0xf1357aea2e62a9c5; -const MUL_MCG32 @local = 0x93d765dd; -const MUL_MCG16 @local = 0x93d5; // TODO: Find good constant - -// Mcg128_64 - -distinct Mcg128Random (Random) = uint128; - -fn void Mcg128Random.set_seed(&self, char[] seed) @dynamic -{ - *self = (Mcg128Random)(random::make_seed(uint128, seed) | 1); -} - -<* - @require bytes.len > 0 -*> -fn void Mcg128Random.next_bytes(&self, char[] bytes) @dynamic -{ - @random_value_to_bytes(self.next_long, bytes); -} - -fn ulong Mcg128Random.next_long(&self) @dynamic -{ - uint128* s = (uint128*)self; - ulong result = (ulong)(*s >> 64); - *s *= MUL_MCG128; - return result; -} - -fn uint128 Mcg128Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn uint Mcg128Random.next_int(&self) @dynamic => (uint)self.next_long(); -fn ushort Mcg128Random.next_short(&self) @dynamic => (ushort)self.next_long(); -fn char Mcg128Random.next_byte(&self) @dynamic => (char)self.next_long(); - - - - -// Mcg64RandomState - -distinct Mcg64Random (Random) = ulong; - -fn void Mcg64Random.set_seed(&self, char[] seed) @dynamic -{ - *self = (Mcg64Random)random::make_seed(ulong, seed) | 1; -} - -<* - @require bytes.len > 0 -*> -fn void Mcg64Random.next_bytes(&self, char[] bytes) @dynamic -{ - @random_value_to_bytes(self.next_int, bytes); -} - -fn uint Mcg64Random.next_int(&self) @dynamic -{ - ulong* s = (ulong*)self; - uint result = (uint)(*s >> 32); - *s *= MUL_MCG64; - return result; -} - -fn uint128 Mcg64Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Mcg64Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn ushort Mcg64Random.next_short(&self) @dynamic => (ushort)self.next_int(); -fn char Mcg64Random.next_byte(&self) @dynamic => (char)self.next_int(); - - -// Mcg32Random - -distinct Mcg32Random (Random) = uint; - -fn void Mcg32Random.set_seed(&self, char[] seed) @dynamic -{ - *self = (Mcg32Random)random::make_seed(uint, seed) | 1; -} - -fn ushort Mcg32Random.next_short(&self) @dynamic -{ - uint* s = (uint*)self; - ushort result = (ushort)(*s >> 16); - *s *= MUL_MCG32; - return result; -} - -<* - @require bytes.len > 0 -*> -fn void Mcg32Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_short, bytes); -fn uint128 Mcg32Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Mcg32Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn uint Mcg32Random.next_int(&self) @dynamic => @short_to_int(self.next_short()); -fn char Mcg32Random.next_byte(&self) @dynamic => (char)self.next_short(); - -// -------------------------------- Mcg16RandomState -------------------------------- - -distinct Mcg16Random (Random) = ushort; - -fn void Mcg16Random.set_seed(&self, char[] seed) @dynamic -{ - *self = (Mcg16Random)random::make_seed(ushort, seed) | 1; -} - -fn char Mcg16Random.next_byte(&self) @dynamic -{ - ushort* s = (ushort*)self; - char result = (char)(*s >> 8); - *s *= MUL_MCG16; - return result; -} - -fn void Mcg16Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_byte, bytes); -fn uint128 Mcg16Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Mcg16Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn uint Mcg16Random.next_int(&self) @dynamic => @short_to_int(self.next_short()); -fn ushort Mcg16Random.next_short(&self) @dynamic => @char_to_short(self.next_byte()); diff --git a/lib7/std/math/random/math.msws.c3 b/lib7/std/math/random/math.msws.c3 deleted file mode 100644 index 61cdc7266..000000000 --- a/lib7/std/math/random/math.msws.c3 +++ /dev/null @@ -1,190 +0,0 @@ -module std::math::random; - -// Move ODD_PHI into a shared module -const ODD_PHI128 @local = 0x9e3779b97f4a7c15f39cc0605cedc835; -const ODD_PHI64 @local = 0x9e3779b97f4a7c15; -const ODD_PHI32 @local = 0x9e3779b9; -const ODD_PHI16 @local = 0x9e37; -const ODD_PHI8 @local = 0x9f; - -// Msws128 -struct Msws128Random (Random) -{ - uint128 state0, state1; - uint128 weyl0, weyl1; -} - -fn void Msws128Random.set_seed(&self, char[] input) @dynamic -{ - *self = bitcast(random::make_seed(uint128[4], input), Msws128Random); -} - - -fn uint128 Msws128Random.next_int128(&self) @dynamic -{ - uint128 s0 = self.state0; - self.state0 = self.state0 * self.state0 + self.weyl0; - self.state0 = self.state0.rotr(64); - self.weyl0 += ODD_PHI128; - - self.state1 = self.state1 * self.state1 + self.weyl1; - uint128 s1 = self.state1; - self.state1 = self.state1.rotr(64); - self.weyl1 -= ODD_PHI128; - - return s0 + s1; -} - -<* - @require bytes.len > 0 -*> -fn void Msws128Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_int128, bytes); -fn ulong Msws128Random.next_long(&self) @dynamic => (ulong)self.next_int128(); -fn uint Msws128Random.next_int(&self) @dynamic => (uint)self.next_int128(); -fn ushort Msws128Random.next_short(&self) @dynamic => (ushort)self.next_int128(); -fn char Msws128Random.next_byte(&self) @dynamic => (char)self.next_int128(); - - -// Msws64 -struct Msws64Random (Random) -{ - ulong state0, state1; - ulong weyl0, weyl1; -} - -fn void Msws64Random.set_seed(&self, char[] input) @dynamic -{ - *self = bitcast(random::make_seed(ulong[4], input), Msws64Random); -} - -fn ulong Msws64Random.next_long(&self) @dynamic -{ - ulong s0 = self.state0; - self.state0 = self.state0 * self.state0 + self.weyl0; - self.state0 = self.state0.rotr(32); - self.weyl0 += ODD_PHI64; - - self.state1 = self.state1 * self.state1 + self.weyl1; - ulong s1 = self.state1; - self.state1 = self.state1.rotr(32); - self.weyl1 -= ODD_PHI64; - - return s0 + s1; -} - -<* - @require bytes.len > 0 -*> -fn void Msws64Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_long, bytes); -fn uint128 Msws64Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn uint Msws64Random.next_int(&self) @dynamic => (uint)self.next_long(); -fn ushort Msws64Random.next_short(&self) @dynamic => (ushort)self.next_long(); -fn char Msws64Random.next_byte(&self) @dynamic => (char)self.next_long(); - -// Msws32 -struct Msws32Random (Random) -{ - uint state0, state1; - uint weyl0, weyl1; -} - -fn void Msws32Random.set_seed(&self, char[] input) @dynamic -{ - *self = bitcast(random::make_seed(uint[4], input), Msws32Random); -} - - -fn uint Msws32Random.next_int(&self) @dynamic -{ - uint s0 = self.state0; - self.state0 = self.state0 * self.state0 + self.weyl0; - self.state0 = self.state0.rotr(16); - self.weyl0 += ODD_PHI32; - - self.state1 = self.state1 * self.state1 + self.weyl1; - uint s1 = self.state1; - self.state1 = self.state1.rotr(16); - self.weyl1 -= ODD_PHI32; - - return s0 + s1; -} - -<* - @require bytes.len > 0 -*> -fn void Msws32Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_int, bytes); -fn uint128 Msws32Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Msws32Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn ushort Msws32Random.next_short(&self) @dynamic => (ushort)self.next_int(); -fn char Msws32Random.next_byte(&self) @dynamic => (char)self.next_int(); - -// Msws16 - -struct Msws16Random (Random) -{ - ushort state0, state1; - ushort weyl0, weyl1; -} - -fn void Msws16Random.set_seed(&self, char[] input) @dynamic -{ - *self = bitcast(random::make_seed(ushort[4], input), Msws16Random); -} - - -fn ushort Msws16Random.next_short(&self) @dynamic -{ - ushort s0 = self.state0; - self.state0 = self.state0 * self.state0 + self.weyl0; - self.state0 = self.state0.rotr(8); - self.weyl0 += ODD_PHI16; - - self.state1 = self.state1 * self.state1 + self.weyl1; - ushort s1 = self.state1; - self.state1 = self.state1.rotr(8); - self.weyl1 -= ODD_PHI16; - - return s0 + s1; -} - -<* - @require bytes.len > 0 -*> -fn void Msws16Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_short, bytes); -fn uint128 Msws16Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Msws16Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn uint Msws16Random.next_int(&self) @dynamic => @short_to_int(self.next_short()); -fn char Msws16Random.next_byte(&self) @dynamic => (char)self.next_short(); - -// Msws8 -struct Msws8Random (Random) -{ - char state0, state1; - char weyl0, weyl1; -} - -fn void Msws8Random.set_seed(&self, char[] input) @dynamic -{ - *self = bitcast(random::make_seed(char[4], input), Msws8Random); -} - -fn char Msws8Random.next_byte(&self) @dynamic -{ - char s0 = self.state0; - self.state0 = self.state0 * self.state0 + self.weyl0; - self.state0 = self.state0.rotr(4); - self.weyl0 += ODD_PHI8; - - self.state1 = self.state1 * self.state1 + self.weyl1; - char s1 = self.state1; - self.state1 = self.state1.rotr(4); - self.weyl1 -= ODD_PHI8; - - return s0 + s1; -} - -fn void Msws8Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_byte, bytes); -fn uint128 Msws8Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Msws8Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn uint Msws8Random.next_int(&self) @dynamic => @short_to_int(self.next_short()); -fn ushort Msws8Random.next_short(&self) @dynamic => @char_to_short(self.next_byte()); diff --git a/lib7/std/math/random/math.pcg.c3 b/lib7/std/math/random/math.pcg.c3 deleted file mode 100644 index 53d56c477..000000000 --- a/lib7/std/math/random/math.pcg.c3 +++ /dev/null @@ -1,125 +0,0 @@ -module std::math::random; - -// Move ODD_PHI into a shared module -const ODD_PHI128 @local = 0x9e3779b97f4a7c15f39cc0605cedc835; -const ODD_PHI64 @local = 0x9e3779b97f4a7c15; -const ODD_PHI32 @local = 0x9e3779b9; -const ODD_PHI16 @local = 0x9e37; - -const MUL_LCG128 @local = 0xdb36357734e34abb0050d0761fcdfc15; -const MUL_LCG64 @local = 0xd1342543de82ef95; -const MUL_LCG32 @local = 0x915f77f5; -const MUL_LCG16 @local = 0x915d; // TODO: Find good constant - - -// -------------------------------- Pcg128_64 -------------------------------- - - -distinct Pcg128Random (Random) = uint128; - -fn void Pcg128Random.set_seed(&self, char[] input) @dynamic -{ - *self = (Pcg128Random)random::make_seed(uint128, input); -} - - -fn ulong Pcg128Random.next_long(&self) @dynamic -{ - const ROT_SHIFT = 64 - 6; - uint128* s = (uint128*)self; - uint128 xor = *s ^ *s >> ((128 - ROT_SHIFT) / 2); - char rot = (char)(*s >> (128 - 6)); - *s = *s * MUL_LCG128 + ODD_PHI128; - return ((ulong)(xor >> ROT_SHIFT)).rotr(rot); -} - -<* - @require bytes.len > 0 -*> -fn void Pcg128Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_long, bytes); -fn uint128 Pcg128Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn uint Pcg128Random.next_int(&self) @dynamic => (uint)self.next_long(); -fn ushort Pcg128Random.next_short(&self) @dynamic => (ushort)self.next_long(); -fn char Pcg128Random.next_byte(&self) @dynamic => (char)self.next_long(); - -// -------------------------------- Pcg64_32 -------------------------------- - -distinct Pcg64Random (Random) = ulong; - -fn void Pcg64Random.set_seed(&self, char[] input) @dynamic -{ - *self = (Pcg64Random)random::make_seed(ulong, input); -} - -fn uint Pcg64Random.next_int(&self) @dynamic -{ - const ROT_SHIFT = 32 - 5; - ulong* s = (ulong*)self; - ulong xor = *s ^ *s >> ((64 - ROT_SHIFT) / 2); - char rot = (char)(*s >> (64 - 5)); - *s = *s * MUL_LCG64 + ODD_PHI64; - return ((uint)(xor >> ROT_SHIFT)).rotr(rot); -} - -<* - @require bytes.len > 0 -*> -fn void Pcg64Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_int, bytes); -fn uint128 Pcg64Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Pcg64Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn ushort Pcg64Random.next_short(&self) @dynamic => (ushort)self.next_int(); -fn char Pcg64Random.next_byte(&self) @dynamic => (char)self.next_int(); - - -// -------------------------------- Pcg32_16 -------------------------------- - -distinct Pcg32Random (Random) = uint; - -fn void Pcg32Random.set_seed(&self, char[] input) @dynamic -{ - *self = (Pcg32Random)random::make_seed(uint, input); -} - -fn ushort Pcg32Random.next_short(&self) @dynamic -{ - const ROT_SHIFT = 16 - 4; - uint* s = (uint*)self; - uint xor = *s ^ *s >> ((32 - ROT_SHIFT) / 2); - char rot = (char)(*s >> (32 - 4)); - *s = *s * MUL_LCG32 + ODD_PHI32; - return ((ushort)(xor >> ROT_SHIFT)).rotr(rot); -} - -<* - @require bytes.len > 0 -*> -fn void Pcg32Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_short, bytes); -fn uint128 Pcg32Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Pcg32Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn uint Pcg32Random.next_int(&self) @dynamic => @short_to_int(self.next_short()); -fn char Pcg32Random.next_byte(&self) @dynamic => (char)self.next_short(); - -// -------------------------------- Pcg16_8 -------------------------------- - -distinct Pcg16Random (Random) = ushort; - -fn void Pcg16Random.set_seed(&self, char[] input) @dynamic -{ - *self = (Pcg16Random)random::make_seed(ushort, input); -} - -fn char Pcg16Random.next_byte(&self) @dynamic -{ - const ROT_SHIFT = 8 - 3; - ushort* s = (ushort*)self; - ushort xor = *s ^ *s >> ((16 - ROT_SHIFT) / 2); - char rot = (char)(*s >> (16 - 3)); - *s = *s * MUL_LCG16 + ODD_PHI16; - return ((char)(xor >> ROT_SHIFT)).rotr(rot); -} - -fn void Pcg16Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_byte, bytes); -fn uint128 Pcg16Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Pcg16Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn uint Pcg16Random.next_int(&self) @dynamic => @short_to_int(self.next_short()); -fn ushort Pcg16Random.next_short(&self) @dynamic => @char_to_short(self.next_byte()); diff --git a/lib7/std/math/random/math.seeder.c3 b/lib7/std/math/random/math.seeder.c3 deleted file mode 100644 index 84de920bf..000000000 --- a/lib7/std/math/random/math.seeder.c3 +++ /dev/null @@ -1,110 +0,0 @@ -module std::math::random; -import std::hash::fnv32a, std::time; - -const ODD_PHI64 @local = 0x9e3779b97f4a7c15; -const MUL_MCG64 @local = 0xf1357aea2e62a9c5; -const MUL_LCG64 @local = 0xd1342543de82ef95; - -macro make_seed($Type, char[] input) -{ - $Type return_value; - seeder(input, @as_char_view(return_value)); - return return_value; -} - -// TODO: Make endian independent -<* - @param [in] input - @param [inout] out_buffer -*> -fn void seeder(char[] input, char[] out_buffer) -{ - $if env::BIG_ENDIAN: - $echo("Please note that the seeding function behaves differently on BE architectures."); - $endif - // Init words - usz out_chars = out_buffer.len; - @pool() - { - ulong[] words = mem::temp_alloc_array(ulong, (out_chars + 7) / 8); - words[..] = ODD_PHI64; - usz words_len_2 = words.len * 2; - - // Add word at a time - for (usz i = 0; i < input.len / 8; i++) - { - usz j = i % words.len; - words[j] -= bitcast(*(char[8]*)&input[i * 8], ulong) * MUL_LCG64; - words[j] ^= words[j] >> 25; - } - - // Add rest of the bytes - usz remaining = input.len - input.len / 8 * 8; - if (remaining) - { - ulong rest = MUL_MCG64; - mem::copy(&rest, &input[^remaining], remaining); - words[^1] -= rest * MUL_LCG64; - words[^1] ^= words[^1] >> 25; - } - - // Mix between words - for (isz i = words_len_2 - 1; i >= 0; i--) - { - isz j = i % words.len; - words[j] -= words[(i + 1) % words.len] * MUL_LCG64; - words[j] ^= words[j] >> 25; - } - - // Mix within words - for (usz i = 0; i < words_len_2; i++) - { - usz j = i % words.len; - words[j] *= MUL_MCG64; - words[j] ^= words[j] >> 25; - } - out_buffer[..] = ((char*)words.ptr)[:out_chars]; - }; -} - -macro uint hash(value) @local -{ - return fnv32a::encode(&&bitcast(value, char[$sizeof(value)])); -} - -fn char[8 * 4] entropy() @if(!env::WASM_NOLIBC) -{ - - void* addr = malloc(1); - free(addr); - static uint random_int; - random_int += 0xedf19156; - uint[8] entropy_data = { - hash($$TIME), - hash(addr), - hash(&addr), - hash(&entropy), - random_int, - hash(clock::now()), - hash(&DString.init), - hash(allocator::heap()) - }; - return bitcast(entropy_data, char[8 * 4]); -} - -fn char[8 * 4] entropy() @if(env::WASM_NOLIBC) -{ - static uint random_int; - random_int += 0xedf19156; - uint[8] entropy_data = { - hash($$TIME), - hash(&entropy), - random_int, - hash($$TIME), - hash(&entropy), - random_int, - hash($$TIME), - hash(&entropy), - }; - return bitcast(entropy_data, char[8 * 4]); -} \ No newline at end of file diff --git a/lib7/std/math/random/math.sfc.c3 b/lib7/std/math/random/math.sfc.c3 deleted file mode 100644 index acf05822e..000000000 --- a/lib7/std/math/random/math.sfc.c3 +++ /dev/null @@ -1,154 +0,0 @@ -module std::math::random; - -// Move ODD_PHI into a shared module -const ODD_PHI128 @local = 0x9e3779b97f4a7c15f39cc0605cedc835; -const ODD_PHI64 @local = 0x9e3779b97f4a7c15; -const ODD_PHI32 @local = 0x9e3779b9; -const ODD_PHI16 @local = 0x9e37; -const ODD_PHI8 @local = 0x9f; - - -// Sfc128 - -distinct Sfc128Random (Random) = uint128[4]; - -fn void Sfc128Random.set_seed(&self, char[] input) @dynamic -{ - *self = (Sfc128Random)random::make_seed(uint128[4], input); -} - -fn uint128 Sfc128Random.next_int128(&self) @dynamic // TODO: Find good constant -{ - uint128* s = (uint128[4]*)self; - uint128 result = s[0] + s[1] + s[3]; - s[0] = s[1] ^ s[1] >> 11; - s[1] = s[2] + s[2] << 3; - s[2] = s[2].rotr(40) + result; - s[3] += ODD_PHI128; - return result; -} - -<* - @require bytes.len > 0 -*> -fn void Sfc128Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_int128, bytes); -fn ulong Sfc128Random.next_long(&self) @dynamic => (uint)self.next_int128(); -fn uint Sfc128Random.next_int(&self) @dynamic => (uint)self.next_int128(); -fn ushort Sfc128Random.next_short(&self) @dynamic => (ushort)self.next_int128(); -fn char Sfc128Random.next_byte(&self) @dynamic => (char)self.next_int128(); - - -// -------------------------------- Sfc64 -------------------------------- - -distinct Sfc64Random (Random) = ulong[4]; - -fn void Sfc64Random.set_seed(&self, char[] input) @dynamic -{ - *self = (Sfc64Random)random::make_seed(ulong[4], input); -} - -fn ulong Sfc64Random.next_long(&self) @dynamic -{ - ulong* s = (ulong[4]*)self; - ulong result = s[0] + s[1] + s[3]; - s[0] = s[1] ^ s[1] >> 11; - s[1] = s[2] + s[2] << 3; - s[2] = s[2].rotr(40) + result; - s[3] += ODD_PHI64; - return result; -} - -<* - @require bytes.len > 0 -*> -fn void Sfc64Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_long, bytes); -fn uint128 Sfc64Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn uint Sfc64Random.next_int(&self) @dynamic => (uint)self.next_long(); -fn ushort Sfc64Random.next_short(&self) @dynamic => (ushort)self.next_long(); -fn char Sfc64Random.next_byte(&self) @dynamic => (char)self.next_long(); - -// -------------------------------- Sfc32 -------------------------------- - -distinct Sfc32Random (Random) = uint[4]; - -fn void Sfc32Random.set_seed(&self, char[] input) @dynamic -{ - *self = (Sfc32Random)random::make_seed(uint[4], input); -} - -fn uint Sfc32Random.next_int(&sfc) @dynamic -{ - uint* s = (uint[4]*)sfc; - uint result = s[0] + s[1] + s[3]; - s[0] = s[1] ^ s[1] >> 9; - s[1] = s[2] + s[2] << 3; - s[2] = s[2].rotr(11) + result; - s[3] += ODD_PHI32; - return result; -} - -<* - @require bytes.len > 0 -*> -fn void Sfc32Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_int, bytes); -fn uint128 Sfc32Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Sfc32Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn ushort Sfc32Random.next_short(&self) @dynamic => (ushort)self.next_int(); -fn char Sfc32Random.next_byte(&self) @dynamic => (char)self.next_int(); - -// -------------------------------- Sfc16 -------------------------------- - -distinct Sfc16Random (Random) = ushort[4]; - -fn void Sfc16Random.set_seed(&self, char[] input) @dynamic -{ - *self = (Sfc16Random)random::make_seed(ushort[4], input); -} - - -fn ushort Sfc16Random.next_short(&seed) @dynamic -{ - ushort* s = (ushort[4]*)seed; - ushort result = s[0] + s[1] + s[3]; - s[0] = s[1] ^ s[1] >> 2; - s[1] = s[2] + s[2] << 3; - s[2] = s[2].rotr(12) + result; - s[3] += ODD_PHI16; - return result; -} - -<* - @require bytes.len > 0 -*> -fn void Sfc16Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_short, bytes); -fn uint128 Sfc16Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Sfc16Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn uint Sfc16Random.next_int(&self) @dynamic => @short_to_int(self.next_short()); -fn char Sfc16Random.next_byte(&self) @dynamic => (char)self.next_short(); - -// -------------------------------- Sfc8 -------------------------------- - - -distinct Sfc8Random (Random) = char[4]; - -fn void Sfc8Random.set_seed(&self, char[] input) @dynamic -{ - *self = (Sfc8Random)random::make_seed(char[4], input); -} - -fn char Sfc8Random.next_byte(&self) @dynamic // TODO: Find better constants -{ - char* s = (char[4]*)self; - char result = s[0] + s[1] + s[3]; - s[0] = s[1] ^ s[1] >> 1; - s[1] = s[2] + s[2] << 2; - s[2] = s[2].rotr(3) + result; - s[3] += ODD_PHI8; - return result; -} - -fn void Sfc8Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_byte, bytes); -fn uint128 Sfc8Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong Sfc8Random.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn uint Sfc8Random.next_int(&self) @dynamic => @short_to_int(self.next_short()); -fn ushort Sfc8Random.next_short(&self) @dynamic => @char_to_short(self.next_byte()); \ No newline at end of file diff --git a/lib7/std/math/random/math.simple_random.c3 b/lib7/std/math/random/math.simple_random.c3 deleted file mode 100644 index 07a43b3fe..000000000 --- a/lib7/std/math/random/math.simple_random.c3 +++ /dev/null @@ -1,34 +0,0 @@ -module std::math::random; - -distinct SimpleRandom (Random) = ulong; - - -fn void SimpleRandom.set_seed(&self, char[] seed) @dynamic -{ - char[8] full; - foreach (i, c : seed) - { - full[i % 8] ^= c; - } - *self = (SimpleRandom)(bitcast(full, ulong) ^ SIMPLE_RANDOM_MULTIPLIER) & SIMPLE_RANDOM_MASK; -} - -fn uint SimpleRandom.next_int(&self) @dynamic -{ - ulong nextseed = ((ulong)*self * SIMPLE_RANDOM_MULTIPLIER + SIMPLE_RANDOM_ADDEND) & SIMPLE_RANDOM_MASK; - *self = (SimpleRandom)nextseed; - return (uint)(nextseed >> (48 - 32)); -} - -<* - @require bytes.len > 0 -*> -fn void SimpleRandom.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_int, bytes); -fn uint128 SimpleRandom.next_int128(&self) @dynamic => @long_to_int128(self.next_long()); -fn ulong SimpleRandom.next_long(&self) @dynamic => @int_to_long(self.next_int()); -fn ushort SimpleRandom.next_short(&self) @dynamic => (ushort)self.next_int(); -fn char SimpleRandom.next_byte(&self) @dynamic => (char)self.next_int(); - -const long SIMPLE_RANDOM_MULTIPLIER @local = 0x5DEECE66D; -const long SIMPLE_RANDOM_ADDEND @local = 0xB; -const long SIMPLE_RANDOM_MASK @local = (1u64 << 48) - 1; diff --git a/lib7/std/math/uuid.c3 b/lib7/std/math/uuid.c3 deleted file mode 100644 index f5648bc27..000000000 --- a/lib7/std/math/uuid.c3 +++ /dev/null @@ -1,41 +0,0 @@ -module std::math::uuid; -import std::math::random @public; -import std::io; - -distinct Uuid (Printable) = char[16]; - -<* -Generate a version 4 UUID from the default random. -*> -fn Uuid generate() -{ - random::init_default_random(); - return generate_from_random(&random::default_random); -} - -<* -Generate a version 4 UUID from the given random. -*> -fn Uuid generate_from_random(Random random) -{ - Uuid uuid; - random.next_bytes(&(char[16])uuid); - uuid[6] = (uuid[6] & 0b0000_1111) | 0b0100_0000; - uuid[8] = (uuid[8] & 0b0011_1111) | 0b1000_0000; - return uuid; -} - -fn usz! Uuid.to_format(&self, Formatter* formatter) @dynamic -{ - return formatter.printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", - (*self)[0], (*self)[1], (*self)[2], (*self)[3], - (*self)[4], (*self)[5], - (*self)[6], (*self)[7], - (*self)[8], (*self)[9], - (*self)[10], (*self)[11], (*self)[12], (*self)[13], (*self)[14], (*self)[15]); -} - -fn String Uuid.to_string(&self, Allocator allocator) -{ - return string::format(allocator, "%s", *self); -} \ No newline at end of file diff --git a/lib7/std/net/inetaddr.c3 b/lib7/std/net/inetaddr.c3 deleted file mode 100644 index 1f82bb2ad..000000000 --- a/lib7/std/net/inetaddr.c3 +++ /dev/null @@ -1,264 +0,0 @@ -module std::net; -import std::io; -import std::ascii; - -enum IpProtocol : char (AIFamily ai_family) -{ - UNSPECIFIED = os::AF_UNSPEC, - IPV4 = os::AF_INET, - IPV6 = os::AF_INET6, -} - -struct InetAddress (Printable) -{ - bool is_ipv6; - union - { - bitstruct ipv6 : char[16] @bigendian - { - ushort a : 0..15; - ushort b : 16..31; - ushort c : 32..47; - ushort d : 48..63; - ushort e : 64..79; - ushort f : 80..95; - ushort g : 96..111; - ushort h : 112..127; - } - bitstruct ip6 : char[16] @bigendian - { - uint128 val : 0..127; - } - UShortBE[8] ipv6arr; - bitstruct ipv4 : char[16] @bigendian - { - char a : 96..103; - char b : 104..111; - char c : 112..119; - char d : 120..127; - } - bitstruct ip4 : char[16] @bigendian - { - uint val : 96..127; - } - } -} - - -fn usz! InetAddress.to_format(InetAddress* addr, Formatter* formatter) @dynamic -{ - if (addr.is_ipv6) - { - return formatter.printf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", - addr.ipv6.a, addr.ipv6.b, addr.ipv6.c, addr.ipv6.d, - addr.ipv6.e, addr.ipv6.f, addr.ipv6.g, addr.ipv6.h)!; - } - return formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!; -} - -fn String InetAddress.to_string(&self, Allocator allocator) -{ - return string::format(allocator, "%s", *self); -} - -fn String InetAddress.to_tstring(&self) -{ - return string::format(tmem(), "%s", *self); -} - -fn InetAddress! ipv6_from_str(String s) -{ - uint sections = 0; - if (s.len < 2) return NetError.INVALID_IP_STRING?; - foreach (c : s) if (c == ':') sections++; - int zero_segment_len = s[0] == ':' || s[^1] == ':' ? 9 - sections : 8 - sections; - if (zero_segment_len == 7 && s.len == 2) return { .is_ipv6 = true }; - if (zero_segment_len > 7) return NetError.INVALID_IP_STRING?; - usz index = 0; - bool last_was_colon, found_zero; - int current = -1; - InetAddress addr = { .is_ipv6 = true }; - foreach (i, c : s) - { - if (c == ':') - { - if (!last_was_colon) - { - if (current == -1) - { - last_was_colon = true; - continue; - } - if (current < 0 || current > 65535) return NetError.INVALID_IP_STRING?; - addr.ipv6arr[index++].val = (ushort)current; - current = -1; - last_was_colon = true; - continue; - } - assert(current == -1); - // Check that this is the first :: - if (found_zero) return NetError.INVALID_IP_STRING?; - // Also check that the zeroed section is at least 2 - if (zero_segment_len < 2) return NetError.INVALID_IP_STRING?; - // Skip (will be zero by default - index += zero_segment_len; - found_zero = true; - last_was_colon = false; - continue; - } - last_was_colon = false; - if (index > 7 || !c.is_xdigit()) return NetError.INVALID_IP_STRING?; - if (current < 0) current = 0; - current = current * 16 + (c <= '9' ? c - '0' : (c | 32) - 'a' + 10); - } - // Ends with :: - if (index == 8 && current == -1) return addr; - - // Ends with number - if (index != 7 || current < 0 || current > 65535) return NetError.INVALID_IP_STRING?; - addr.ipv6arr[7].val = (ushort)current; - return addr; -} - -fn InetAddress! ipv4_from_str(String s) -{ - InetAddress addr; - int element; - int current = -1; - foreach (c : s) - { - if (c == '.') - { - if (current < 0) return NetError.INVALID_IP_STRING?; - if (current > 255) return NetError.INVALID_IP_STRING?; - switch (element) - { - case 0: addr.ipv4.a = (char)current; - case 1: addr.ipv4.b = (char)current; - case 2: addr.ipv4.c = (char)current; - default: return NetError.INVALID_IP_STRING?; - } - current = -1; - element++; - continue; - } - if (element > 3 || c < '0' || c > '9') return NetError.INVALID_IP_STRING?; - if (current < 0) - { - current = c - '0'; - continue; - } - current = current * 10 + c - '0'; - } - if (element != 3 || current < 0 || current > 255) return NetError.INVALID_IP_STRING?; - addr.ipv4.d = (char)current; - return addr; -} - -fn bool InetAddress.is_loopback(InetAddress* addr) -{ - if (addr.is_ipv6) - { - return addr.ip6.val == 1; - } - return addr.ipv4.a == 127; -} - -fn bool InetAddress.is_any_local(InetAddress* addr) -{ - if (addr.is_ipv6) - { - return addr.ip6.val == 0; - } - return addr.ip4.val == 0; -} - -fn bool InetAddress.is_link_local(InetAddress* addr) -{ - if (addr.is_ipv6) - { - return addr.ipv6.a == 0xfa && (addr.ipv6.b & 0xc0) == 0x80; - } - return addr.ipv4.a == 169 && addr.ipv4.b == 254; -} - -fn bool InetAddress.is_site_local(InetAddress* addr) -{ - if (addr.is_ipv6) - { - return addr.ipv6.a == 0xfe && (addr.ipv6.b & 0xc0) == 0xc0; - } - // 10/8 172.16/12 192.168/16 - switch - { - case addr.ipv4.a == 10: - case addr.ipv4.a == 172 && (addr.ipv4.b & 0xF0) == 16: - case addr.ipv4.a == 192 && addr.ipv4.b == 168: - return true; - default: - return false; - } -} - -fn bool InetAddress.is_multicast(InetAddress* addr) -{ - if (addr.is_ipv6) - { - return addr.ipv6.a == 0xff; - } - return addr.ip4.val & 0xf0000000 == 0xe0000000; -} - -fn bool InetAddress.is_multicast_global(InetAddress* addr) -{ - if (addr.is_ipv6) - { - return addr.ipv6.a == 0xff && (addr.ipv6.b & 0x0f) == 0x0e; - } - return addr.ipv4.a >= 224 && addr.ipv4.a <= 238 - && !(addr.ipv4.a == 224 && addr.ipv4.b == 0 && addr.ipv4.c == 0); -} - -fn bool InetAddress.is_multicast_node_local(InetAddress* addr) -{ - if (!addr.is_ipv6) return false; - return addr.ipv6.a == 0xff && addr.ipv6.b & 0x0f == 0x01; -} - -fn bool InetAddress.is_multicast_site_local(InetAddress* addr) -{ - if (addr.is_ipv6) - { - return addr.ipv6.a == 0xff && addr.ipv6.b & 0x0f == 0x05; - } - return addr.ipv4.a == 239 && addr.ipv4.b == 255; -} - -fn bool InetAddress.is_multicast_org_local(InetAddress* addr) -{ - if (addr.is_ipv6) - { - return addr.ipv6.a == 0xff && addr.ipv6.b & 0x0f == 0x08; - } - return addr.ipv4.a == 239 && addr.ipv4.b >= 192 && addr.ipv4.b <= 195; -} - -fn bool InetAddress.is_multicast_link_local(InetAddress* addr) -{ - if (addr.is_ipv6) - { - return addr.ipv6.a == 0xff && (addr.ipv6.b & 0x0f) == 0x02; - } - return addr.ipv4.a == 224 && addr.ipv4.b == 0 && addr.ipv4.c == 0; -} - -fn AddrInfo*! addrinfo(String host, uint port, AIFamily ai_family, AISockType ai_socktype) @if(os::SUPPORTS_INET) => @pool() -{ - ZString zhost = host.zstr_tcopy(); - DString str = dstring::temp_with_capacity(32); - str.appendf("%d", port); - AddrInfo hints = { .ai_family = ai_family, .ai_socktype = ai_socktype }; - AddrInfo* ai; - if (os::getaddrinfo(zhost, str.zstr_view(), &hints, &ai)) return NetError.ADDRINFO_FAILED?; - return ai; -} diff --git a/lib7/std/net/net.c3 b/lib7/std/net/net.c3 deleted file mode 100644 index 7a5f600d5..000000000 --- a/lib7/std/net/net.c3 +++ /dev/null @@ -1,66 +0,0 @@ -module std::net; -import std::io; - -fault NetError -{ - INVALID_URL, - URL_TOO_LONG, - INVALID_SOCKET, - GENERAL_ERROR, - INVALID_IP_STRING, - ADDRINFO_FAILED, - CONNECT_FAILED, - LISTEN_FAILED, - ACCEPT_FAILED, - WRITE_FAILED, - READ_FAILED, - SOCKOPT_FAILED, - - SOCKETS_NOT_INITIALIZED, - STILL_PROCESSING_CALLBACK, - BAD_SOCKET_DESCRIPTOR, - NOT_A_SOCKET, - CONNECTION_REFUSED, - CONNECTION_TIMED_OUT, - ADDRESS_IN_USE, - CONNECTION_ALREADY_IN_PROGRESS, - ALREADY_CONNECTED, - NETWORK_UNREACHABLE, - OPERATION_NOT_SUPPORTED_ON_SOCKET, - CONNECTION_RESET, -} - -fn uint! ipv4toint(String s) -{ - uint out; - int element; - int current = -1; - foreach (c : s) - { - if (c == '.') - { - if (current < 0) return NetError.INVALID_IP_STRING?; - out = out << 8 + current; - current = -1; - element++; - continue; - } - if (element > 3 || c < '0' || c > '9') return NetError.INVALID_IP_STRING?; - if (current < 0) - { - current = c - '0'; - continue; - } - current = current * 10 + c - '0'; - } - if (element != 3 || current < 0) return NetError.INVALID_IP_STRING?; - out = out << 8 + current; - return out; -} - -fn String! int_to_ipv4(uint val, Allocator allocator) -{ - char[3 * 4 + 3 + 1] buffer; - String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)!; - return res.copy(allocator); -} diff --git a/lib7/std/net/os/common.c3 b/lib7/std/net/os/common.c3 deleted file mode 100644 index 650304b72..000000000 --- a/lib7/std/net/os/common.c3 +++ /dev/null @@ -1,168 +0,0 @@ -module std::net::os; -const bool SUPPORTS_INET = env::LIBC && (env::WIN32 || env::DARWIN || env::LINUX); - -distinct AIFamily = CInt; -distinct AIProtocol = CInt; -distinct AISockType = CInt; -distinct AIFlags = CInt; - -def Socklen_t = CUInt @if(!env::WIN32); -def Socklen_t = usz @if(env::WIN32); - -distinct SockAddrPtr = void*; - -struct AddrInfo -{ - AIFlags ai_flags; - AIFamily ai_family; - AISockType ai_socktype; - AIProtocol ai_protocol; - Socklen_t ai_addrlen; - struct @if(env::WIN32 || env::DARWIN) - { - ZString ai_canonname; - SockAddrPtr ai_addr; - } - struct @if(env::LINUX) - { - SockAddrPtr ai_addr; - ZString ai_canonname; - } - AddrInfo* ai_next; -} - - -const PLATFORM_O_NONBLOCK @if(!$defined(PLATFORM_O_NONBLOCK)) = 0; - -const AISockType SOCK_STREAM = 1; // Stream -const AISockType SOCK_DGRAM = 2; // Datagram -const AISockType SOCK_RAW = 3; // Raw -const AISockType SOCK_RDM = 4; // Reliably delivered -const AISockType SOCK_SEQPACKET = 5; // Sequential packet - -const AIFlags AI_PASSIVE = 0x1; -const AIFlags AI_CANONNAME = 0x2; -const AIFlags AI_NUMERICHOST = 0x4; - -const AIFamily AF_UNSPEC = 0; -const AIFamily AF_UNIX = 1; -const AIFamily AF_INET = 2; -const AIFamily AF_INET6 = PLATFORM_AF_INET6; -const AIFamily AF_IPX = PLATFORM_AF_IPX; -const AIFamily AF_APPLETALK = PLATFORM_AF_APPLETALK; - -const O_NONBLOCK = PLATFORM_O_NONBLOCK; - -extern fn CInt getaddrinfo(ZString nodename, ZString servname, AddrInfo* hints, AddrInfo** res) @if(SUPPORTS_INET); -extern fn void freeaddrinfo(AddrInfo* res) @if(SUPPORTS_INET); -extern fn CInt setsockopt(NativeSocket socket, CInt level, CInt optname, void* optval, Socklen_t optlen) @if(SUPPORTS_INET); -extern fn CInt getsockopt(NativeSocket socket, CInt level, CInt optname, void* optval, Socklen_t optlen) @if(SUPPORTS_INET); - -module std::net::os @if(!env::LIBC || !(env::WIN32 || env::DARWIN || env::LINUX)); - -const AIFamily PLATFORM_AF_INET6 = 0; -const AIFamily PLATFORM_AF_IPX = 0; -const AIFamily PLATFORM_AF_APPLETALK = 0; - -const IPPPOTO_IP = 0; -const IPPROTO_ICMP = 1; -const IPPROTO_IGMP = 2; -const IPPROTO_IPV4 = 4; -const IPPROTO_TCP = 6; -const IPPROTO_ST = 7; -const IPPROTO_EGP = 8; -const IPPROTO_PIGP = 9; -const IPPROTO_RCCMON = 10; -const IPPROTO_NVPII = 11; -const IPPROTO_PUP = 12; -const IPPROTO_ARGUS = 13; -const IPPROTO_EMCON = 14; -const IPPROTO_XNET = 15; -const IPPROTO_CHAOS = 16; -const IPPROTO_UDP = 17; -const IPPROTO_MUX = 18; -const IPPROTO_MEAS = 19; -const IPPROTO_HMP = 20; -const IPPROTO_PRM = 21; -const IPPROTO_IDP = 22; -const IPPROTO_TRUNK1 = 23; -const IPPROTO_TRUNK2 = 24; -const IPPROTO_LEAF1 = 25; -const IPPROTO_LEAF2 = 26; -const IPPROTO_RDP = 27; -const IPPROTO_IRTP = 28; -const IPPROTO_TP = 29; -const IPPROTO_BLT = 30; -const IPPROTO_NSP = 31; -const IPPROTO_INP = 32; -const IPPROTO_SEP = 33; -const IPPROTO_3PC = 34; -const IPPROTO_IDPR = 35; -const IPPROTO_XTP = 36; -const IPPROTO_DDP = 37; -const IPPROTO_CMTP = 38; -const IPPROTO_TPXX = 39; -const IPPROTO_IL = 40; -const IPPROTO_IPV6 = 41; -const IPPROTO_SDRP = 42; -const IPPROTO_ROUTING = 43; -const IPPROTO_FRAGMENT = 44; -const IPPROTO_IDRP = 45; -const IPPROTO_RSVP = 46; -const IPPROTO_GRE = 47; -const IPPROTO_MHRP = 48; -const IPPROTO_BHA = 49; -const IPPROTO_ESP = 50; -const IPPROTO_AH = 51; -const IPPROTO_INLSP = 52; -const IPPROTO_SWIPE = 53; -const IPPROTO_NHRP = 54; -const IPPROTO_ICMPV6 = 58; -const IPPROTO_NONE = 59; -const IPPROTO_DSTOPTS = 60; -const IPPROTO_AHIP = 61; -const IPPROTO_CFTP = 62; -const IPPROTO_HELLO = 63; -const IPPROTO_SATEXPAK = 64; -const IPPROTO_KRYPTOLAN = 65; -const IPPROTO_RVD = 66; -const IPPROTO_IPPC = 67; -const IPPROTO_ADFS = 68; -const IPPROTO_SATMON = 69; -const IPPROTO_VISA = 70; -const IPPROTO_IPCV = 71; -const IPPROTO_CPNX = 72; -const IPPROTO_CPHB = 73; -const IPPROTO_WSN = 74; -const IPPROTO_PVP = 75; -const IPPROTO_BRSATMON = 76; -const IPPROTO_ND = 77; -const IPPROTO_WBMON = 78; -const IPPROTO_WBEXPAK = 79; -const IPPROTO_EON = 80; -const IPPROTO_VMTP = 81; -const IPPROTO_SVMTP = 82; -const IPPROTO_VINES = 83; -const IPPROTO_TTP = 84; -const IPPROTO_IGP = 85; -const IPPROTO_DGP = 86; -const IPPROTO_TCF = 87; -const IPPROTO_IGRP = 88; -const IPPROTO_OSPFIGP = 89; -const IPPROTO_SRPC = 90; -const IPPROTO_LARP = 91; -const IPPROTO_MTP = 92; -const IPPROTO_AX25 = 93; -const IPPROTO_IPEIP = 94; -const IPPROTO_MICP = 95; -const IPPROTO_SCCSP = 96; -const IPPROTO_ETHERIP = 97; -const IPPROTO_ENCAP = 98; -const IPPROTO_APES = 99; -const IPPROTO_GMTP = 100; -const IPPROTO_PIM = 103; -const IPPROTO_IPCOMP = 108; -const IPPROTO_PGM = 113; -const IPPROTO_SCTP = 132; -const IPPROTO_DIVERT = 254; -const IPPROTO_RAW = 255; \ No newline at end of file diff --git a/lib7/std/net/os/darwin.c3 b/lib7/std/net/os/darwin.c3 deleted file mode 100644 index 9b74fb24b..000000000 --- a/lib7/std/net/os/darwin.c3 +++ /dev/null @@ -1,97 +0,0 @@ -module std::net::os @if(env::DARWIN); -import libc; - -const AIFlags AI_NUMERICSERV = 0x1000; -const AIFlags AI_ALL = 0x100; -const AIFlags AI_V4MAPPED_CFG = 0x200; -const AIFlags AI_ADDRCONFIG = 0x400; -const AIFlags AI_V4MAPPED = 0x800; -const AIFlags AI_UNUSABLE = 0x10000000; -const AIFlags AI_DEFAULT = AI_V4MAPPED_CFG | AI_ADDRCONFIG; - -const AIFamily PLATFORM_AF_IMPLINK = 3; -const AIFamily PLATFORM_AF_PUP = 4; -const AIFamily PLATFORM_AF_CHAOS = 5; -const AIFamily PLATFORM_AF_NS = 6; -const AIFamily PLATFORM_AF_ISO = 7; -const AIFamily PLATFORM_AF_ECMA = 8; -const AIFamily PLATFORM_AF_DATAKIT = 9; -const AIFamily PLATFORM_AF_CCITT = 10; -const AIFamily PLATFORM_AF_SNA = 11; -const AIFamily PLATFORM_AF_DECNET = 12; -const AIFamily PLATFORM_AF_DLI = 13; -const AIFamily PLATFORM_AF_LAT = 14; -const AIFamily PLATFORM_AF_HYLINK = 15; -const AIFamily PLATFORM_AF_APPLETALK = 16; -const AIFamily PLATFORM_AF_ROUTE = 17; -const AIFamily PLATFORM_AF_LINK = 18; -const AIFamily PLATFORM_PSEUDO_AF_XTP = 19; -const AIFamily PLATFORM_AF_COIP = 20; -const AIFamily PLATFORM_AF_CNT = 21; -const AIFamily PLATFORM_PSEUDO_AF_RTIP = 22; -const AIFamily PLATFORM_AF_IPX = 23; -const AIFamily PLATFORM_AF_SIP = 24; -const AIFamily PLATFORM_PSEUDO_AF_PIP = 25; -const AIFamily PLATFORM_AF_NDRV = 27; -const AIFamily PLATFORM_AF_ISDN = 28; -const AIFamily PLATFORM_PSEUDO_AF_KEY = 29; -const AIFamily PLATFORM_AF_INET6 = 30; -const AIFamily PLATFORM_AF_NATM = 31; -const AIFamily PLATFORM_AF_SYSTEM = 32; -const AIFamily PLATFORM_AF_NETBIOS = 33; -const AIFamily PLATFORM_AF_PPP = 34; -const AIFamily PLATFORM_PSEUDO_AF_HDRCMPLT = 35; -const AIFamily PLATFORM_AF_IEEE80211 = 37; -const AIFamily PLATFORM_AF_UTUN = 38; -const AIFamily PLATFORM_AF_VSOCK = 40; -const AIFamily PLATFORM_AF_MAX = 41; - -const int PLATFORM_O_NONBLOCK = 0x04; - -// https://opensource.apple.com/source/xnu/xnu-4570.41.2/bsd/sys/socket.h.auto.html -const int SOL_SOCKET = 0xffff; -const int SO_DEBUG = 0x0001; // turn on debugging info recording -const int SO_ACCEPTCONN = 0x0002; // socket has had listen() -const int SO_REUSEADDR = 0x0004; // allow local address reuse -const int SO_KEEPALIVE = 0x0008; // keep connections alive -const int SO_DONTROUTE = 0x0010; // just use interface addresses -const int SO_BROADCAST = 0x0020; // permit sending of broadcast msgs -const int SO_USELOOPBACK = 0x0040; // bypass hardware when possible -const int SO_LINGER = 0x0080; // linger on close if data present (in ticks) -const int SO_OOBINLINE = 0x0100; // leave received OOB data in line -const int SO_REUSEPORT = 0x0200; // allow local address & port reuse -const int SO_TIMESTAMP = 0x0400; // timestamp received dgram traffic -const int SO_TIMESTAMP_MONOTONIC = 0x0800; // Monotonically increasing timestamp on rcvd dgram -const int SO_DONTTRUNC = 0x2000; // Apple Retain unread data -const int SO_WANTMORE = 0x4000; // Apple: Give hint when more data ready -const int SO_WANTOOBFLAG = 0x8000; // Apple: Want OOB in MSG_FLAG on receive - -const int SO_SNDBUF = 0x1001; // Send buffer size -const int SO_RCVBUF = 0x1002; // Recieve buffer size -const int SO_SNDLOWAT = 0x1003; // Send low-water mark -const int SO_RCVLOWAT = 0x1004; // Receive low-water mark -const int SO_SNDTIMEO = 0x1005; // Send timeout -const int SO_RCVTIMEO = 0x1006; // Receive timeout -const int SO_ERROR = 0x1007; // Get error status and clear -const int SO_TYPE = 0x1008; // Socket type -const int SO_LABEL = 0x1010; // Socket MAC label -const int SO_PEERLABEL = 0x1011; // Socket peer MAC label -const int SO_NREAD = 0x1020; // Apple: get 1st-packet byte count -const int SO_NKE = 0x1021; // Apple: Install socket-level NKE -const int SO_NOSIGPIPE = 0x1022; // Apple: No SIGPIPE on EPIPE -const int SO_NOADDRERR = 0x1023; // Apple: Returns EADDRNOTAVAIL when src is not available anymore -const int SO_NWRITE = 0x1024; // Apple: Get number of bytes currently in send socket buffer -const int SO_REUSESHAREUID = 0x1025; // Apple: Allow reuse of port/socket by different userids -const int SO_LINGER_SEC = 0x1080; // linger on close if data present (in seconds) - - -const CShort POLLRDNORM = 0x0040; -const CShort POLLRDBAND = 0x0080; -const CShort POLLWRNORM = POLLOUT; -const CShort POLLWRBAND = 0x0100; -const CShort POLLEXTEND = 0x0200; // file may have been extended -const CShort POLLATTRIB = 0x0400; // file attributes may have changed -const CShort POLLNLINK = 0x0800; // (un)link/rename may have happened -const CShort POLLWRITE = 0x1000; // file's contents may have changed - -const CInt MSG_PEEK = 0x0002; \ No newline at end of file diff --git a/lib7/std/net/os/linux.c3 b/lib7/std/net/os/linux.c3 deleted file mode 100644 index fb800ffbb..000000000 --- a/lib7/std/net/os/linux.c3 +++ /dev/null @@ -1,92 +0,0 @@ -module std::net::os @if(env::LINUX); -import libc; - -const AIFamily PLATFORM_AF_AX25 = 3; -const AIFamily PLATFORM_AF_IPX = 4; -const AIFamily PLATFORM_AF_APPLETALK = 5; -const AIFamily PLATFORM_AF_NETROM = 6; -const AIFamily PLATFORM_AF_BRIDGE = 7; -const AIFamily PLATFORM_AF_AAL5 = 8; -const AIFamily PLATFORM_AF_X25 = 9; -const AIFamily PLATFORM_AF_INET6 = 10; - -const PLATFORM_O_NONBLOCK = 0o4000; - -// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/socket.h -const int SOL_SOCKET = 1; - -const int SO_DEBUG = 1; // turn on debugging info recording -const int SO_REUSEADDR = 2; // allow local address reuse -const int SO_TYPE = 3; -const int SO_ERROR = 4; -const int SO_DONTROUTE = 5; // just use interface addresses -const int SO_BROADCAST = 6; // permit sending of broadcast msgs -const int SO_SNDBUF = 7; // Send buffer size -const int SO_RCVBUF = 8; // Receive buffer size -const int SO_KEEPALIVE = 9; // keep connections alive -const int SO_OOBINLINE = 10; // leave received OOB data in line -const int SO_NO_CHECK = 11; -const int SO_PRIORITY = 12; -const int SO_LINGER = 13; // linger on close if data present (in ticks) -const int SO_BSDCOMPAT = 14; -const int SO_REUSEPORT = 15; // allow local address & port reuse -const int SO_RCVLOWAT = 18; -const int SO_SNDLOWAT = 19; -const int SO_RCVTIMEO = 20; // IMPORTANT Verify before use -const int SO_SNDTIMEO = 21; // IMPORTANT Verify before use -const int SO_BINDTODEVICE = 25; -const int SO_ATTACH_FILTER = 26; -const int SO_DETACH_FILTER = 27; -const int SO_PEERNAME = 28; -const int SO_TIMESTAMP = 29; // IMPORTANT Verify before use timestamp received dgram traffic -const int SO_ACCEPTCONN = 30; -const int SO_PEERSEC = 31; -const int SO_SNDBUFFORCE = 32; -const int SO_RCVBUFFORCE = 33; -const int SO_PASSSEC = 34; -const int SO_MARK = 36; -const int SO_PROTOCOL = 38; -const int SO_DOMAIN = 39; -const int SO_RXQ_OVFL = 40; -const int SO_WIFI_STATUS = 41; -const int SO_PEEK_OFF = 42; -const int SO_NOFCS = 43; -const int SO_LOCK_FILTER = 44; -const int SO_SELECT_ERR_QUEUE = 45; -const int SO_BUSY_POLL = 46; -const int SO_MAX_PACING_RATE = 47; -const int SO_BPF_EXTENSIONS = 48; -const int SO_INCOMING_CPU = 49; -const int SO_ATTACH_BPF = 50; -const int SO_ATTACH_REUSEPORT_CBPF = 51; -const int SO_ATTACH_REUSEPORT_EBPF = 52; -const int SO_CNX_ADVICE = 53; -const int SO_MEMINFO = 55; -const int SO_INCOMING_NAPI_ID = 56; -const int SO_COOKIE = 57; -const int SO_PEERGROUPS = 59; -const int SO_ZEROCOPY = 60; -const int SO_TXTIME = 61; -const int SO_BINDTOIFINDEX = 62; -const int SO_DETACH_REUSEPORT_BPF = 68; -const int SO_PREFER_BUSY_POLL = 69; -const int SO_BUSY_POLL_BUDGET = 70; -const int SO_NETNS_COOKIE = 71; -const int SO_BUF_LOCK = 72; -const int SO_RESERVE_MEM = 73; -const int SO_TXREHASH = 74; -const int SO_RCVMARK = 75; -const int SO_PASSPIDFD = 76; -const int SO_PEERPIDFD = 77; - -const CUShort POLLRDNORM = 0x0040; -const CUShort POLLRDBAND = 0x0080; -const CUShort POLLWRNORM = 0x0100; -const CUShort POLLWRBAND = 0x0200; -const CUShort POLLMSG = 0x0400; -const CUShort POLLREMOVE = 0x1000; -const CUShort POLLRDHUP = 0x2000; -const CUShort POLLFREE = 0x4000; -const CUShort POLL_BUSY_LOOP = 0x8000; - -const CInt MSG_PEEK = 0x0002; \ No newline at end of file diff --git a/lib7/std/net/os/posix.c3 b/lib7/std/net/os/posix.c3 deleted file mode 100644 index 499537d62..000000000 --- a/lib7/std/net/os/posix.c3 +++ /dev/null @@ -1,96 +0,0 @@ -module std::net::os @if(env::POSIX && SUPPORTS_INET); -import std::io, libc; - -const int F_GETFL = 3; -const int F_SETFL = 4; - -distinct NativeSocket = inline Fd; - -struct Posix_pollfd -{ - CInt fd; - CUShort events; - CUShort revents; -} - -def Posix_nfds_t = CUInt; - -extern fn CInt connect(NativeSocket socket, SockAddrPtr address, Socklen_t address_len); -extern fn NativeSocket socket(AIFamily af, AISockType type, AIProtocol ip_protocol); -extern fn int fcntl(NativeSocket socket, int cmd, ...); -extern fn CInt bind(NativeSocket socket, SockAddrPtr address, Socklen_t address_len); -extern fn CInt listen(NativeSocket socket, CInt backlog); -extern fn NativeSocket accept(NativeSocket socket, SockAddrPtr address, Socklen_t* address_len); -extern fn CInt poll(Posix_pollfd* fds, Posix_nfds_t nfds, CInt timeout); - -const CUShort POLLIN = 0x0001; -const CUShort POLLPRI = 0x0002; -const CUShort POLLOUT = 0x0004; -const CUShort POLLERR = 0x0008; -const CUShort POLLHUP = 0x0010; -const CUShort POLLNVAL = 0x0020; - -fn anyfault convert_error(Errno error) -{ - switch (error) - { - case errno::EACCES: return IoError.NO_PERMISSION; - case errno::EADDRINUSE: return NetError.ADDRESS_IN_USE; - case errno::EALREADY: return NetError.CONNECTION_ALREADY_IN_PROGRESS; - case errno::EBADF: return NetError.BAD_SOCKET_DESCRIPTOR; - case errno::ECONNREFUSED: return NetError.CONNECTION_REFUSED; - case errno::ECONNRESET: return NetError.CONNECTION_RESET; - case errno::EISCONN: return NetError.ALREADY_CONNECTED; - case errno::ENETUNREACH: return NetError.NETWORK_UNREACHABLE; - case errno::ENOTSOCK: return NetError.NOT_A_SOCKET; - case errno::EINTR: return IoError.INTERRUPTED; - case errno::EWOULDBLOCK: return IoError.WOULD_BLOCK; - case errno::EOPNOTSUPP: return NetError.OPERATION_NOT_SUPPORTED_ON_SOCKET; - case errno::ETIMEDOUT: return NetError.CONNECTION_TIMED_OUT; - default: return IoError.GENERAL_ERROR; - } -} - -fn anyfault socket_error() -{ - return convert_error(libc::errno()); -} - -macro bool NativeSocket.is_valid(self) -{ - return (iptr)self >= 0; -} - -macro void! NativeSocket.close(self) -{ - if (libc::close(self)) - { - if (libc::errno() == errno::EBADF) return NetError.INVALID_SOCKET?; - return NetError.GENERAL_ERROR?; - } -} - -macro void! NativeSocket.set_non_blocking(self, bool non_blocking) -{ - int flags = fcntl(self, F_GETFL, 0); - if (non_blocking) - { - if (flags & O_NONBLOCK) return; - flags |= O_NONBLOCK; - } - else - { - if (!(flags & O_NONBLOCK)) return; - flags &= ~(int)O_NONBLOCK; - } - if (fcntl(self, F_SETFL, flags) == -1) - { - if (libc::errno() == errno::EBADF) return NetError.INVALID_SOCKET?; - return NetError.GENERAL_ERROR?; - } -} - -macro bool NativeSocket.is_non_blocking(self) -{ - return fcntl(self, F_GETFL, 0) & O_NONBLOCK != 0; -} diff --git a/lib7/std/net/os/win32.c3 b/lib7/std/net/os/win32.c3 deleted file mode 100644 index 062be2825..000000000 --- a/lib7/std/net/os/win32.c3 +++ /dev/null @@ -1,105 +0,0 @@ -module std::net::os @if(env::WIN32); -import std::os, std::io, libc; - -const AIFamily PLATFORM_AF_IPX = 6; -const AIFamily PLATFORM_AF_APPLETALK = 16; -const AIFamily PLATFORM_AF_NETBIOS = 17; -const AIFamily PLATFORM_AF_INET6 = 23; -const AIFamily PLATFORM_AF_IRDA = 26; -const AIFamily PLATFORM_AF_BTH = 32; - -const int FIONREAD = 1074030207; -const int FIONBIO = -2147195266; -const int FIOASYNC = -2147195267; - -distinct NativeSocket = inline Win32_SOCKET; - -extern fn CInt ioctlsocket(NativeSocket, CLong cmd, CULong *argp); -extern fn WSAError closesocket(NativeSocket); -extern fn NativeSocket socket(AIFamily af, AISockType type, AIProtocol ip_protocol); -extern fn int connect(NativeSocket, SockAddrPtr address, Socklen_t address_len); -extern fn int bind(NativeSocket, SockAddrPtr address, Socklen_t address_len); -extern fn int listen(NativeSocket, int backlog); -extern fn NativeSocket accept(NativeSocket, SockAddrPtr address, Socklen_t* address_len); - -macro bool NativeSocket.is_valid(self) -{ - return self != (NativeSocket)(uptr)-1; -} - -fn void! NativeSocket.set_non_blocking(self, bool non_blocking) -{ - if (ioctlsocket(self, win32::FIONBIO, &&(CULong)non_blocking)) - { - return socket_error()?; - } -} - -macro void! NativeSocket.close(self) -{ - WSAError error = closesocket(self); - if (error) return convert_error(error)?; -} - -// https://github.com/wine-mirror/wine/blob/master/include/winsock.h -const int SOL_SOCKET = 0xffff; -const int SO_DEBUG = 0x0001; -const int SO_ACCEPTCONN = 0x0002; -const int SO_REUSEADDR = 0x0004; -const int SO_KEEPALIVE = 0x0008; -const int SO_DONTROUTE = 0x0010; -const int SO_BROADCAST = 0x0020; -const int SO_USELOOPBACK = 0x0040; -const int SO_LINGER = 0x0080; -const int SO_OOBINLINE = 0x0100; -const int SO_SNDBUF = 0x1001; -const int SO_RCVBUF = 0x1002; -const int SO_SNDLOWAT = 0x1003; -const int SO_RCVLOWAT = 0x1004; -const int SO_SNDTIMEO = 0x1005; -const int SO_RCVTIMEO = 0x1006; -const int SO_ERROR = 0x1007; -const int SO_TYPE = 0x1008; - -fn anyfault convert_error(WSAError error) -{ - switch (error) - { - case wsa::NOTINITIALISED: return NetError.SOCKETS_NOT_INITIALIZED; - case wsa::ENETDOWN: return NetError.NETWORK_UNREACHABLE; - case wsa::INVALID_HANDLE: return NetError.BAD_SOCKET_DESCRIPTOR; - case wsa::EACCESS: return IoError.NO_PERMISSION; - case wsa::EINPROGRESS: return NetError.STILL_PROCESSING_CALLBACK; - case wsa::EADDRINUSE: return NetError.ADDRESS_IN_USE; - case wsa::EALREADY: return NetError.CONNECTION_ALREADY_IN_PROGRESS; - case wsa::EBADF: return NetError.BAD_SOCKET_DESCRIPTOR; - case wsa::EINTR: return IoError.INTERRUPTED; - case wsa::EWOULDBLOCK: return IoError.WOULD_BLOCK; - case wsa::ECONNREFUSED: return NetError.CONNECTION_REFUSED; - case wsa::EISCONN: return NetError.ALREADY_CONNECTED; - case wsa::ENETUNREACH: return NetError.NETWORK_UNREACHABLE; - case wsa::ENOTSOCK: return NetError.NOT_A_SOCKET; - case wsa::EOPNOTSUPP: return NetError.OPERATION_NOT_SUPPORTED_ON_SOCKET; - case wsa::ETIMEDOUT: return NetError.CONNECTION_TIMED_OUT; - case wsa::ECONNRESET: return NetError.CONNECTION_RESET; - default: return IoError.GENERAL_ERROR; - } -} - -fn anyfault socket_error() -{ - return convert_error(win32_WSAGetLastError()); -} - -const CUShort POLLIN = win32::POLLIN; -const CUShort POLLPRI = win32::POLLPRI; -const CUShort POLLOUT = win32::POLLOUT; -const CUShort POLLERR = win32::POLLERR; -const CUShort POLLHUP = win32::POLLHUP; -const CUShort POLLNVAL = win32::POLLNVAL; -const CUShort POLLRDNORM = win32::POLLRDNORM; -const CUShort POLLRDBAND = win32::POLLRDBAND; -const CUShort POLLWRNORM = win32::POLLWRNORM; -const CUShort POLLWRBAND = win32::POLLWRBAND; - -const int MSG_PEEK = 0x0002; \ No newline at end of file diff --git a/lib7/std/net/socket.c3 b/lib7/std/net/socket.c3 deleted file mode 100644 index b895726dc..000000000 --- a/lib7/std/net/socket.c3 +++ /dev/null @@ -1,183 +0,0 @@ -module std::net @if(os::SUPPORTS_INET); -import std::io, std::os, std::time, libc; - -struct Socket (InStream, OutStream) -{ - NativeSocket sock; - Socklen_t ai_addrlen; - // TODO proper way to get the size of sockaddr_storage - // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms740504(v=vs.85) - char[128] ai_addr_storage; -} - -macro void @loop_over_ai(AddrInfo* ai; @body(NativeSocket fd, AddrInfo* ai)) -{ - while (ai) - { - NativeSocket sockfd = os::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol); - if (sockfd.is_valid()) - { - @body(sockfd, ai); - } - ai = ai.ai_next; - } -} - -const Duration POLL_FOREVER = -1; - -distinct PollSubscribes = ushort; -distinct PollEvents = ushort; - -const PollSubscribes SUBSCRIBE_ANY_READ = os::POLLIN; -const PollSubscribes SUBSCRIBE_PRIO_READ = os::POLLPRI; -const PollSubscribes SUBSCRIBE_OOB_READ = os::POLLRDBAND; -const PollSubscribes SUBSCRIBE_READ = os::POLLRDNORM; -const PollSubscribes SUBSCRIBE_ANY_WRITE = os::POLLOUT; -const PollSubscribes SUBSCRIBE_OOB_WRITE = os::POLLWRBAND; -const PollSubscribes SUBSCRIBE_WRITE = os::POLLWRNORM; - -const PollEvents POLL_EVENT_READ_PRIO = os::POLLPRI; -const PollEvents POLL_EVENT_READ_OOB = os::POLLRDBAND; -const PollEvents POLL_EVENT_READ = os::POLLRDNORM; -const PollEvents POLL_EVENT_WRITE_OOB = os::POLLWRBAND; -const PollEvents POLL_EVENT_WRITE = os::POLLWRNORM; -const PollEvents POLL_EVENT_DISCONNECT = os::POLLHUP; -const PollEvents POLL_EVENT_ERROR = os::POLLERR; -const PollEvents POLL_EVENT_INVALID = os::POLLNVAL; - -struct Poll -{ - NativeSocket socket; - PollSubscribes events; - PollEvents revents; -} - -<* - @param [inout] polls - @param timeout "duration to poll (clamped to CInt.max ms), or POLL_FOREVER." -*> -fn ulong! poll(Poll[] polls, Duration timeout) -{ - return poll_ms(polls, timeout == POLL_FOREVER ? -1 : timeout.to_ms()) @inline; -} - -<* - @param [inout] polls - @param timeout_ms "duration to poll in ms or -1. Clamped to CInt.max" -*> -fn ulong! poll_ms(Poll[] polls, long timeout_ms) -{ - if (timeout_ms > CInt.max) timeout_ms = CInt.max; - $if env::WIN32: - CInt result = win32_WSAPoll((Win32_LPWSAPOLLFD)polls.ptr, (Win32_ULONG)polls.len, (CInt)timeout_ms); - $else - CInt result = os::poll((Posix_pollfd*)polls.ptr, (Posix_nfds_t)polls.len, (CInt)timeout_ms); - $endif - return result < 0 ? os::socket_error()? : (ulong)result; -} - -macro Socket new_socket(fd, ai) -{ - Socket sock = { .sock = fd, .ai_addrlen = ai.ai_addrlen }; - assert(sock.ai_addr_storage.len >= ai.ai_addrlen, "storage %d < addrlen %d", sock.ai_addr_storage.len, ai.ai_addrlen); - mem::copy(&sock.ai_addr_storage, (void*)ai.ai_addr, ai.ai_addrlen); - return sock; -} - -enum SocketOption : char (CInt value) -{ - REUSEADDR = os::SO_REUSEADDR, - REUSEPORT @if(!env::WIN32) = os::SO_REUSEPORT, - KEEPALIVE = os::SO_KEEPALIVE, - BROADCAST = os::SO_BROADCAST, - OOBINLINE = os::SO_OOBINLINE, - DONTROUTE = os::SO_DONTROUTE, -} - -fn bool! Socket.get_broadcast(&self) => self.get_option(BROADCAST); -fn bool! Socket.get_keepalive(&self) => self.get_option(KEEPALIVE); -fn bool! Socket.get_reuseaddr(&self) => self.get_option(REUSEADDR); -fn bool! Socket.get_dontroute(&self) => self.get_option(DONTROUTE); -fn bool! Socket.get_oobinline(&self) => self.get_option(OOBINLINE); - -fn void! Socket.set_broadcast(&self, bool value) => self.set_option(BROADCAST, value); -fn void! Socket.set_keepalive(&self, bool value) => self.set_option(KEEPALIVE, value); -fn void! Socket.set_reuseaddr(&self, bool value) => self.set_option(REUSEADDR, value); -fn void! Socket.set_dontroute(&self, bool value) => self.set_option(DONTROUTE, value); -fn void! Socket.set_oobinline(&self, bool value) => self.set_option(OOBINLINE, value); - -fn void! Socket.set_option(&self, SocketOption option, bool value) -{ - CInt flag = (CInt)value; - int errcode = os::setsockopt(self.sock, os::SOL_SOCKET, option.value, &flag, CInt.sizeof); - if (errcode != 0) return NetError.SOCKOPT_FAILED?; -} - -fn bool! Socket.get_option(&self, SocketOption option) -{ - CInt flag; - int errcode = os::setsockopt(self.sock, os::SOL_SOCKET, option.value, &flag, CInt.sizeof); - if (errcode != 0) return NetError.SOCKOPT_FAILED?; - return (bool)flag; -} - -fn usz! Socket.read(&self, char[] bytes) @dynamic -{ -$if env::WIN32: - isz n = libc::recv(self.sock, bytes.ptr, (int)bytes.len, 0); -$else - isz n = libc::recv(self.sock, bytes.ptr, bytes.len, 0); -$endif - if (n < 0) return os::socket_error()?; - return (usz)n; -} - -fn char! Socket.read_byte(&self) @dynamic => io::read_byte_using_read(self); - -fn usz! Socket.write(&self, char[] bytes) @dynamic -{ -$if env::WIN32: - isz n = libc::send(self.sock, bytes.ptr, (int)bytes.len, 0); -$else - isz n = libc::send(self.sock, bytes.ptr, bytes.len, 0); -$endif - if (n < 0) return os::socket_error()?; - return (usz)n; -} - -fn void! Socket.write_byte(&self, char byte) @dynamic => io::write_byte_using_write(self, byte); - -fn void! Socket.destroy(&self) @dynamic -{ - self.close()!; -} -fn void! Socket.close(&self) @inline @dynamic -{ - self.sock.close()!; -} - -fn usz! Socket.peek(&self, char[] bytes) @dynamic -{ - $if env::WIN32: - isz n = libc::recv(self.sock, bytes.ptr, (int)bytes.len, os::MSG_PEEK); - $else - isz n = libc::recv(self.sock, bytes.ptr, bytes.len, os::MSG_PEEK); - $endif - if (n < 0) return os::socket_error()?; - return (usz)n; -} - -enum SocketShutdownHow : (inline CInt native_value) -{ - RECEIVE = @select(env::WIN32, libc::SD_RECEIVE, libc::SHUT_RD), - SEND = @select(env::WIN32, libc::SD_SEND, libc::SHUT_WR), - BOTH = @select(env::WIN32, libc::SD_BOTH, libc::SHUT_RDWR), -} - -fn void! Socket.shutdown(&self, SocketShutdownHow how) -{ - if (libc::shutdown(self.sock, how) < 0) - { - return os::socket_error()?; - } -} diff --git a/lib7/std/net/socket_private.c3 b/lib7/std/net/socket_private.c3 deleted file mode 100644 index 77f4b2d81..000000000 --- a/lib7/std/net/socket_private.c3 +++ /dev/null @@ -1,113 +0,0 @@ -module std::net @if(os::SUPPORTS_INET); -import std::time, libc, std::os; - -macro apply_sockoptions(sockfd, options) @private -{ - Socket sock = { .sock = sockfd }; - foreach (o : options) sock.set_option(o, true)!; -} - -fn Socket! connect_from_addrinfo(AddrInfo* addrinfo, SocketOption[] options) @private -{ - @loop_over_ai(addrinfo; NativeSocket sockfd, AddrInfo* ai) - { - apply_sockoptions(sockfd, options)!; - int errcode = os::connect(sockfd, ai.ai_addr, ai.ai_addrlen); - // Keep the first successful connection. - if (!errcode) return new_socket(sockfd, ai); - }; - return os::socket_error()?; -} - -fn bool last_error_is_delayed_connect() -{ - $switch - $case env::WIN32: - switch (win32_WSAGetLastError()) - { - case wsa::EWOULDBLOCK: - case wsa::EINPROGRESS: return true; - default: return false; - } - $default: - Errno err = libc::errno(); - return err == errno::EINPROGRESS || err == errno::EAGAIN || err == errno::EWOULDBLOCK; - $endswitch -} - -fn Socket! connect_with_timeout_from_addrinfo(AddrInfo* addrinfo, SocketOption[] options, Duration timeout) @private -{ - Clock c = 0; - @loop_over_ai(addrinfo; NativeSocket sockfd, AddrInfo* ai) - { - apply_sockoptions(sockfd, options)!; - sockfd.set_non_blocking(true)!; - int errcode = os::connect(sockfd, ai.ai_addr, ai.ai_addrlen); - if (!errcode) - { - // It worked, restore blocking. - sockfd.set_non_blocking(false)!; - return new_socket(sockfd, ai); - } - if (last_error_is_delayed_connect()) - { - Duration timeout_left = timeout; - if (c) - { - Duration to_remove = c.to_now().to_duration(); - if (to_remove >= timeout_left) - { - return NetError.CONNECTION_TIMED_OUT?; - } - timeout_left -= to_remove; - } - else - { - c = clock::now(); - } - Poll poll_request = { sockfd, SUBSCRIBE_ANY_WRITE, 0 }; - if (!poll((&poll_request)[:1], timeout_left)!) - { - return NetError.CONNECTION_TIMED_OUT?; - } - if (poll_request.revents & POLL_EVENT_WRITE) - { - sockfd.set_non_blocking(false)!; - return new_socket(sockfd, ai); - } - } - }; - return os::socket_error()?; -} - -fn Socket! connect_async_from_addrinfo(AddrInfo* addrinfo, SocketOption[] options) @private -{ - @loop_over_ai(addrinfo; NativeSocket sockfd, AddrInfo* ai) - { - apply_sockoptions(sockfd, options)!; - sockfd.set_non_blocking(true)!; - int errcode = os::connect(sockfd, ai.ai_addr, ai.ai_addrlen); - if (!errcode || last_error_is_delayed_connect()) - { - // Keep the first successful connection. - return new_socket(sockfd, ai); - } - }; - return os::socket_error()?; -} - -macro @network_loop_over_ai(network, host, port; @body(fd, ai)) @private -{ - AddrInfo* ai = network.addrinfo(host, port)!; - AddrInfo* first = ai; - defer os::freeaddrinfo(first); - while (ai) - { - NativeSocket sockfd = os::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol); - if (sockfd > 0) - { - @body(sockfd, ai); - } - ai = ai.ai_next; - } -} diff --git a/lib7/std/net/tcp.c3 b/lib7/std/net/tcp.c3 deleted file mode 100644 index 716584076..000000000 --- a/lib7/std/net/tcp.c3 +++ /dev/null @@ -1,63 +0,0 @@ -module std::net::tcp @if(os::SUPPORTS_INET); -import std::net @public; -import std::time, libc; - -distinct TcpSocket = inline Socket; -distinct TcpServerSocket = inline Socket; - -fn TcpSocket! connect(String host, uint port, Duration timeout = 0, SocketOption... options, IpProtocol ip_protocol = UNSPECIFIED) -{ - AddrInfo* ai = net::addrinfo(host, port, ip_protocol.ai_family, os::SOCK_STREAM)!; - defer os::freeaddrinfo(ai); - if (timeout > 0) - { - return (TcpSocket)net::connect_with_timeout_from_addrinfo(ai, options, timeout)!; - } - return connect_to(ai, ...options); -} - -fn TcpSocket! connect_async(String host, uint port, SocketOption... options, IpProtocol ip_protocol = UNSPECIFIED) -{ - AddrInfo* ai = net::addrinfo(host, port, ip_protocol.ai_family, os::SOCK_STREAM)!; - defer os::freeaddrinfo(ai); - return connect_async_to(ai, ...options); -} - -fn TcpSocket! connect_to(AddrInfo* ai, SocketOption... options) -{ - return (TcpSocket)net::connect_from_addrinfo(ai, options); -} - -fn TcpSocket! connect_async_to(AddrInfo* ai, SocketOption... options) -{ - return (TcpSocket)net::connect_async_from_addrinfo(ai, options); -} - -fn TcpServerSocket! listen(String host, uint port, uint backlog, SocketOption... options, IpProtocol ip_protocol = UNSPECIFIED) -{ - AddrInfo* ai = net::addrinfo(host, port, ip_protocol.ai_family, os::SOCK_STREAM)!; - defer os::freeaddrinfo(ai); - return listen_to(ai, backlog, ...options); -} - -fn TcpSocket! accept(TcpServerSocket* server_socket) -{ - TcpSocket socket; - socket.ai_addrlen = socket.ai_addr_storage.len; - socket.sock = os::accept(server_socket.sock, (SockAddrPtr)&socket.ai_addr_storage, &socket.ai_addrlen); - if (!socket.sock.is_valid()) return NetError.ACCEPT_FAILED?; - return socket; -} - -fn TcpServerSocket! listen_to(AddrInfo* ai, uint backlog, SocketOption... options) -{ - net::@loop_over_ai(ai; NativeSocket sockfd, AddrInfo* ai_candidate) - { - net::apply_sockoptions(sockfd, options)!; - bool err = os::bind(sockfd, ai_candidate.ai_addr, ai_candidate.ai_addrlen) || os::listen(sockfd, backlog); - if (!err) return (TcpServerSocket)net::new_socket(sockfd, ai_candidate); - }; - return os::socket_error()?; -} - - diff --git a/lib7/std/net/udp.c3 b/lib7/std/net/udp.c3 deleted file mode 100644 index 3ecace47d..000000000 --- a/lib7/std/net/udp.c3 +++ /dev/null @@ -1,28 +0,0 @@ -module std::net::udp @if(os::SUPPORTS_INET); -import std::net @public; - -distinct UdpSocket = inline Socket; - -fn UdpSocket! connect(String host, uint port, SocketOption... options, IpProtocol ip_protocol = UNSPECIFIED) -{ - AddrInfo* ai = net::addrinfo(host, port, ip_protocol.ai_family, os::SOCK_DGRAM)!; - defer os::freeaddrinfo(ai); - return connect_to(ai, ...options); -} - -fn UdpSocket! connect_to(AddrInfo* ai, SocketOption... options) -{ - return (UdpSocket)net::connect_from_addrinfo(ai, options); -} - -fn UdpSocket! connect_async(String host, uint port, SocketOption... options, IpProtocol ip_protocol = UNSPECIFIED) -{ - AddrInfo* ai = net::addrinfo(host, port, ip_protocol.ai_family, os::SOCK_DGRAM)!; - defer os::freeaddrinfo(ai); - return connect_async_to(ai, ...options); -} - -fn UdpSocket! connect_async_to(AddrInfo* ai, SocketOption... options) -{ - return (UdpSocket)net::connect_async_from_addrinfo(ai, options); -} diff --git a/lib7/std/net/url.c3 b/lib7/std/net/url.c3 deleted file mode 100644 index e88da8e24..000000000 --- a/lib7/std/net/url.c3 +++ /dev/null @@ -1,369 +0,0 @@ -module std::net::url; - -import std::io, std::collections::map, std::collections::list; - -fault UrlParsingResult -{ - EMPTY, - INVALID_SCHEME, - INVALID_USER, - INVALID_PASSWORD, - INVALID_HOST, - INVALID_PATH, - INVALID_FRAGMENT, -} - -<* - Represents the actual (decoded) Url. - - An Url can be parsed from a String with `new_parse()` or `temp_parse()`. The - parsed fields are decoded. The only field that is not decoded is `query`. - To access the decoded query values, use `new_parse_query(query)`. - - `Url.to_string()` will re-assemble the fields into a valid Url string with - proper percent-encoded values. - - If the Url struct fields are filled in manually, use the actual (un-encoded) - values. To create a raw query string, initialize an `UrlQueryValues` map, use - `UrlQueryValues.add()` to add the query parameters and, finally, call - `UrlQueryValues.to_string()`. -*> -struct Url(Printable) -{ - String scheme; - String host; - uint port; - String username; - String password; - String path; - String query; - String fragment; - - Allocator allocator; -} - -<* - Parse a URL string into a Url struct. - - @param [in] url_string - @require url_string.len > 0 "the url_string must be len 1 or more" - @return "the parsed Url" -*> -fn Url! tparse(String url_string) => parse(tmem(), url_string); - -<* - Parse a URL string into a Url struct. - - @param [in] url_string - @require url_string.len > 0 "the url_string must be len 1 or more" - @return "the parsed Url" -*> -fn Url! parse(Allocator allocator, String url_string) -{ - url_string = url_string.trim(); - if (!url_string) return UrlParsingResult.EMPTY?; - Url url = { .allocator = allocator }; - - // Parse scheme - if (try pos = url_string.index_of("://")) - { - if (!pos) return UrlParsingResult.INVALID_SCHEME?; - url.scheme = url_string[:pos].copy(allocator); - url_string = url_string[url.scheme.len + 3 ..]; - } - else if (try pos = url_string.index_of(":")) - { - // Handle schemes without authority like 'mailto:' - if (!pos) return UrlParsingResult.INVALID_SCHEME?; - url.scheme = url_string[:pos].copy(allocator); - url.path = decode(allocator, url_string[pos + 1 ..], PATH) ?? UrlParsingResult.INVALID_PATH?!; - return url; - } - - // Parse host, port - if (url.scheme != "urn") - { - usz authority_end = url_string.index_of_chars("/?#") ?? url_string.len; - String authority = url_string[:authority_end]; - - if (try user_info_end = authority.index_of_char('@')) - { - String userinfo = authority[:user_info_end]; - String username @noinit; - String password; - @pool(allocator) - { - String[] userpass = userinfo.tsplit(":", 2); - username = userpass[0]; - if (!username.len) return UrlParsingResult.INVALID_USER?; - url.host = - - url.username = decode(allocator, username, HOST) ?? UrlParsingResult.INVALID_USER?!; - if (userpass.len) url.password = decode(allocator, userpass[1], USERPASS) ?? UrlParsingResult.INVALID_PASSWORD?!; - }; - authority = authority[userinfo.len + 1 ..]; - } - - // Check for IPv6 address in square brackets - String host; - if (authority.starts_with("[") && authority.contains("]")) - { - usz ipv6_end = authority.index_of("]")!; - host = authority[0 .. ipv6_end]; // Includes closing bracket - if ((ipv6_end + 1) < authority.len && authority[.. ipv6_end] == ":") - { - url.port = authority[.. ipv6_end + 1].to_uint()!; - } - } - else - { - @pool(allocator) - { - String[] host_port = authority.tsplit(":", 2); - if (host_port.len > 1) - { - host = host_port[0]; - url.port = host_port[1].to_uint()!; - } - else - { - host = authority; - } - }; - } - url.host = decode(allocator, host, HOST) ?? UrlParsingResult.INVALID_HOST?!; - url_string = url_string[authority_end ..]; - } - - // Parse path - usz! query_index = url_string.index_of_char('?'); - usz! fragment_index = url_string.index_of_char('#'); - - if (@ok(query_index) || @ok(fragment_index)) - { - usz path_end = min(query_index ?? url_string.len, fragment_index ?? url_string.len); - url.path = decode(allocator, url_string[:path_end], PATH) ?? UrlParsingResult.INVALID_PATH?!; - url_string = url_string[path_end ..]; - } - else - { - url.path = decode(allocator, url_string, PATH) ?? UrlParsingResult.INVALID_PATH?!; - url_string = ""; - } - - // Remove the path part from url for further parsing - - - // Parse query - if (url_string.starts_with("?")) - { - usz index = url_string.index_of_char('#') ?? url_string.len; - url.query = url_string[1 .. index - 1].copy(allocator); - url_string = url_string[index ..]; - } - - // Parse fragment - if (url_string.starts_with("#")) - { - url.fragment = decode(allocator, url_string[1..], FRAGMENT) ?? UrlParsingResult.INVALID_FRAGMENT?!; - } - return url; -} - -fn usz! Url.to_format(&self, Formatter* f) @dynamic -{ - usz len; - // Add scheme if it exists - if (self.scheme != "") - { - len += f.print(self.scheme)!; - len += f.print(":")!; - if (self.host.len > 0) len += f.print("//")!; - } - - // Add username and password if they exist - if (self.username) - { - @stack_mem(64; Allocator smem) - { - len += f.print(encode(smem, self.username, USERPASS))!; - }; - if (self.password) - { - len += f.print(":")!; - @stack_mem(64; Allocator smem) - { - len += f.print(encode(smem, self.password, USERPASS))!; - }; - } - len += f.print("@")!; - } - - // Add host - @stack_mem(128; Allocator smem) - { - len += f.print(encode(smem, self.host, HOST))!; - }; - - // Add port - if (self.port) len += f.printf(":%d", self.port)!; - - // Add path - @stack_mem(256; Allocator smem) - { - len += f.print(encode(smem, self.path, PATH))!; - }; - - // Add query if it exists (note that `query` is expected to - // be already properly encoded). - if (self.query) - { - len += f.print("?")!; - len += f.print(self.query)!; - } - - // Add fragment if it exists - if (self.fragment) - { - @stack_mem(256; Allocator smem) - { - len += f.print("#")!; - len += f.print(encode(smem, self.fragment, FRAGMENT))!; - }; - } - return len; -} - -fn String Url.to_string(&self, Allocator allocator) -{ - return string::format(allocator, "%s", *self); -} - - -def UrlQueryValueList = List{String}; - -struct UrlQueryValues -{ - inline HashMap{String, UrlQueryValueList} map; - UrlQueryValueList key_order; -} - -<* - Parse the query parameters of the Url into a UrlQueryValues map. - - @param [in] query - @return "a UrlQueryValues HashMap" -*> -fn UrlQueryValues parse_query_to_temp(String query) => parse_query(tmem(), query); - -<* - Parse the query parameters of the Url into a UrlQueryValues map. - - @param [in] query - @param [inout] allocator - @return "a UrlQueryValues HashMap" -*> -fn UrlQueryValues parse_query(Allocator allocator, String query) -{ - UrlQueryValues vals; - vals.map.init(allocator); - vals.key_order.init(allocator); - - Splitter raw_vals = query.tokenize("&"); - while (try String rv = raw_vals.next()) - { - @pool(allocator) - { - String[] parts = rv.tsplit("=", 2); - String key = tdecode(parts[0], QUERY) ?? parts[0]; - vals.add(key, parts.len == 1 ? key : (tdecode(parts[1], QUERY) ?? parts[1])); - }; - } - return vals; -} - -<* - Add copies of the key and value strings to the UrlQueryValues map. These - copies are freed when the UrlQueryValues map is freed. - - @param [in] self - @param key - @param value - @return "a UrlQueryValues map" -*> -fn UrlQueryValues* UrlQueryValues.add(&self, String key, String value) -{ - String value_copy = value.copy(self.allocator); - if (try existing = self.get_ref(key)) - { - existing.push(value_copy); - } - else - { - UrlQueryValueList new_list; - new_list.init_with_array(self.allocator, { value_copy }); - (*self)[key] = new_list; - self.key_order.push(key.copy(self.allocator)); - } - return self; -} - - - -fn usz! UrlQueryValues.to_format(&self, Formatter* f) @dynamic -{ - usz len; - usz i; - foreach (key: self.key_order) - { - @stack_mem(128; Allocator mem) - { - String encoded_key = encode(mem, key, QUERY); - UrlQueryValueList! values = self.map.get(key); - if (catch values) continue; - foreach (value : values) - { - if (i > 0) len += f.print("&")!; - len += f.print(encoded_key)!; - len += f.print("=")!; - @stack_mem(256; Allocator smem) - { - len += f.print(encode(smem, value, QUERY))!; - }; - i++; - } - }; - } - return len; -} - - -fn void UrlQueryValues.free(&self) -{ - self.map.@each(;String key, UrlQueryValueList values) - { - foreach (value: values) value.free(self.allocator); - values.free(); - }; - self.map.free(); - - foreach (&key: self.key_order) key.free(self.allocator); - self.key_order.free(); -} - -<* - Free an Url struct. - - @param [in] self -*> -fn void Url.free(&self) -{ - if (!self.allocator) return; - self.scheme.free(self.allocator); - self.host.free(self.allocator); - self.username.free(self.allocator); - self.password.free(self.allocator); - self.path.free(self.allocator); - self.query.free(self.allocator); - self.fragment.free(self.allocator); -} diff --git a/lib7/std/net/url_encoding.c3 b/lib7/std/net/url_encoding.c3 deleted file mode 100644 index 2bef12573..000000000 --- a/lib7/std/net/url_encoding.c3 +++ /dev/null @@ -1,173 +0,0 @@ -<* - This module section provides encoding and decoding functions for URL - components according to RFC 3986. -*> -module std::net::url; -import std::encoding::hex; - -enum UrlEncodingMode : char (String allowed) -{ - UNRESERVED = "-_.~", // section 2.3 - PATH = "$&+,/:;=@", // section 3.3 - HOST = "!$&'()*+,;=:[]", // section 3.2.2 (also include ':', '[', ']' for ipv6 hosts) - USERPASS = ";:&=+$,", // section 3.2.1 - QUERY = "", // section 3.4 - FRAGMENT = "$&+,/:;=?@!()*", // section 4.1 -} - -fault UrlDecodingError -{ - INVALID_HEX -} - -<* - Returns true if char c should be encoded according to RFC 3986. - - @param c "Character to check if it should be encoded." - @param mode "Url encoding mode." -*> -fn bool should_encode(char c, UrlEncodingMode mode) @private -{ - // alphanumeric characters are allowed - if (c.is_alnum()) return false; - - // unreserved characters are allowed - if (try UrlEncodingMode.UNRESERVED.allowed.index_of_char(c)) return false; - - // some mode-specific characters are allowed - if (try mode.allowed.index_of_char(c)) return false; - - // everything else must be encoded - return true; -} - -<* - Calculate the length of the percent-encoded string. -*> -fn usz encode_len(String s, UrlEncodingMode mode) @inline -{ - usz n; - foreach (c: s) - { - if (!should_encode(c, mode)) continue; - if (c != ' ' || mode != QUERY) - { - n++; - } - } - return s.len + 2 * n; -} - -<* - Encode the string s for a given encoding mode. - Returned string must be freed. - - @param s "String to encode" - @param mode "Url encoding mode" - @param [inout] allocator - @return "Percent-encoded String" -*> -fn String encode(Allocator allocator, String s, UrlEncodingMode mode) => @pool(allocator) -{ - usz n = encode_len(s, mode); - DString builder = dstring::temp_with_capacity(n); - - foreach(i, c: s) - { - switch - { - // encode spaces in queries - case c == ' ' && mode == QUERY: - builder.append_char('+'); - - // add encoded char - case should_encode(c, mode): - builder.append_char('%'); - String hex = hex::tencode(s[i:1]); - builder.append(hex.to_upper_tcopy()); - - // use char, no encoding needed - default: - builder.append_char(c); - } - } - - return builder.copy_str(allocator); -} - - -<* - Encode string s for a given encoding mode, stored on the temp allocator. - - @param s "String to encode" - @param mode "Url encoding mode" - @return "Percent-encoded String" -*> -fn String tencode(String s, UrlEncodingMode mode) => encode(tmem(), s, mode); - -<* - Calculate the length of the percent-decoded string. - - @return! UrlDecodingError.INVALID_HEX -*> -fn usz! decode_len(String s, UrlEncodingMode mode) @inline -{ - usz n; - foreach (i, c: s) - { - if (c != '%') continue; - if (i + 2 >= s.len || !s[i+1].is_xdigit() || !s[i+2].is_xdigit()) - { - return UrlDecodingError.INVALID_HEX?; - } - n++; - } - return s.len - 2 * n; -} - -<* - Decode string s for a given encoding mode. - Returned string must be freed. - - @param s "String to decode" - @param mode "Url encoding mode" - @param [inout] allocator - @return "Percent-decoded String" -*> -fn String! decode(Allocator allocator, String s, UrlEncodingMode mode) => @pool(allocator) -{ - usz n = decode_len(s, mode)!; - DString builder = dstring::temp_with_capacity(n); - - for (usz i = 0; i < s.len; i++) - { - switch (s[i]) - { - // decode encoded char - case '%': - char[] hex = hex::tdecode(s[i+1:2])!; - builder.append(hex); - i += 2; - - // decode space when in queries - case '+': - builder.append_char((mode == QUERY) ? ' ' : '+'); - - // use char, no decoding needed - default: - builder.append_char(s[i]); - } - } - - return builder.copy_str(allocator); -} - - -<* - Decode string s for a given encoding mode, stored on the temp allocator. - - @param s "String to decode" - @param mode "Url encoding mode" - @return "Percent-decoded String" -*> -fn String! tdecode(String s, UrlEncodingMode mode) => decode(tmem(), s, mode); diff --git a/lib7/std/os/backtrace.c3 b/lib7/std/os/backtrace.c3 deleted file mode 100644 index 39ddce575..000000000 --- a/lib7/std/os/backtrace.c3 +++ /dev/null @@ -1,103 +0,0 @@ -module std::os::backtrace; -import std::collections::list, std::os, std::io; - -fault BacktraceFault -{ - SEGMENT_NOT_FOUND, - EXECUTABLE_PATH_NOT_FOUND, - IMAGE_NOT_FOUND, - NO_BACKTRACE_SYMBOLS, - RESOLUTION_FAILED, -} - -const Backtrace BACKTRACE_UNKNOWN = { 0, "", "", "", 0, null, false }; - -struct Backtrace (Printable) -{ - uptr offset; - String function; - String object_file; - String file; - uint line; - Allocator allocator; - bool is_inline; -} - - -fn bool Backtrace.has_file(&self) -{ - return self.file.len > 0; -} - -fn bool Backtrace.is_unknown(&self) -{ - return !self.object_file.len; -} - -fn usz! Backtrace.to_format(&self, Formatter* formatter) @dynamic -{ - String inline_suffix = self.is_inline ? " [inline]" : ""; - if (self.has_file()) - { - return formatter.printf("%s (in %s) (%s:%d)%s", self.function, self.object_file, self.file, self.line, inline_suffix); - } - if (self.is_unknown()) - { - return formatter.printf("??? (in unknown)%s", inline_suffix); - } - return formatter.printf("%s (in %s) (source unavailable)%s", self.function, self.object_file, inline_suffix); -} -fn void Backtrace.free(&self) -{ - if (!self.allocator) return; - allocator::free(self.allocator, self.function); - allocator::free(self.allocator, self.object_file); - allocator::free(self.allocator, self.file); -} - -fn Backtrace* Backtrace.init(&self, Allocator allocator, uptr offset, String function, String object_file, String file = "", uint line = 0) -{ - if (!allocator) - { - self.offset = offset; - self.function = function; - self.object_file = object_file; - self.file = file; - self.line = 0; - self.allocator = null; - return self; - } - self.offset = offset; - self.function = function.copy(allocator); - self.object_file = object_file.copy(allocator); - self.file = file.copy(allocator); - self.allocator = allocator; - self.line = line; - return self; -} - -fn void*[] capture_current(void*[] buffer) -{ - if (!buffer.len) return buffer[:0]; - $switch - $case env::POSIX: - CInt len = posix::backtrace(buffer.ptr, buffer.len); - return buffer[:len]; - $case env::WIN32: - Win32_WORD len = win32::rtlCaptureStackBackTrace(0, buffer.len, buffer.ptr, null); - return buffer[:len]; - $default: - return buffer[:0]; - $endswitch -} - -def BacktraceList = List{Backtrace}; - -def symbolize_backtrace = linux::symbolize_backtrace @if(env::LINUX); -def symbolize_backtrace = win32::symbolize_backtrace @if(env::WIN32); -def symbolize_backtrace = darwin::symbolize_backtrace @if(env::DARWIN); - -fn BacktraceList! symbolize_backtrace(Allocator allocator, void*[] backtrace) @if(!env::NATIVE_STACKTRACE) -{ - return {}; -} \ No newline at end of file diff --git a/lib7/std/os/cpu.c3 b/lib7/std/os/cpu.c3 deleted file mode 100644 index 25c8627c0..000000000 --- a/lib7/std/os/cpu.c3 +++ /dev/null @@ -1,34 +0,0 @@ -// https://www.cprogramming.com/snippets/source-code/find-the-number-of-cpu-cores-for-windows-mac-or-linux - -module std::os @if(env::DARWIN); -import std::os::macos; - -fn uint num_cpu() -{ - int[2] nm; - usz len = 4; - uint count; - - nm = { darwin::CTL_HW, darwin::HW_NCPU }; - darwin::sysctl(&nm, 2, &count, &len, null, 0); - if (count < 1) count = 1; - return count; -} - -module std::os @if(env::LINUX); -import std::os::posix; - -fn uint num_cpu() -{ - return posix::get_nprocs_conf(); -} - -module std::os @if(env::WIN32); -import std::os::win32; - -fn uint num_cpu() -{ - Win32_SYSTEM_INFO info; - win32::getSystemInfo(&info); - return info.dwNumberOfProcessors; -} \ No newline at end of file diff --git a/lib7/std/os/env.c3 b/lib7/std/os/env.c3 deleted file mode 100644 index 1d60b0f9a..000000000 --- a/lib7/std/os/env.c3 +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) 2021-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::os::env; -import std::io::path, libc, std::os; - -<* - @param [in] name - @require name.len > 0 - @return! SearchResult.MISSING -*> -fn String! get_var(Allocator allocator, String name) => @pool(allocator) -{ - - $switch - $case env::LIBC && !env::WIN32: - ZString val = libc::getenv(name.zstr_tcopy()); - return val ? val.copy(allocator) : SearchResult.MISSING?; - $case env::WIN32: - // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getenvironmentvariable - const usz BUFSIZE = 1024; - WString buff = (WString)tcalloc(BUFSIZE * 2 + 2); - WString wstr = name.to_wstring_tcopy()!; - usz len = win32::getEnvironmentVariableW(wstr, buff, BUFSIZE); - if (len == 0) return SearchResult.MISSING?; - if (len > BUFSIZE) - { - buff = (WString)tmalloc(len * 2 + 2); - win32::getEnvironmentVariableW(wstr, buff, (Win32_DWORD)len); - } - return string::new_from_wstring(allocator, buff); - $default: - return ""; - $endswitch -} - -fn String! tget_var(String name) -{ - return get_var(tmem(), name); -} - -<* - @param [in] name - @param [in] value - @require name.len > 0 -*> -fn bool set_var(String name, String value, bool overwrite = true) => @pool() -{ - $switch - $case env::WIN32: - WString wname = name.to_wstring_tcopy()!!; - if (!overwrite) - { - Char16[8] buff; - if (win32::getEnvironmentVariableW(wname, &buff, 8) > 0) return true; - } - // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setenvironmentvariable - return (win32::setEnvironmentVariableW(wname, value.to_wstring_tcopy()) ?? 1) == 0; - $case env::LIBC && !env::WIN32: - return libc::setenv(name.zstr_tcopy(), value.zstr_tcopy(), (int)overwrite) == 0; - $default: - return false; - $endswitch -} - -<* -Returns the current user's home directory. -*> -fn String! get_home_dir(Allocator using = allocator::heap()) -{ - String home; - $if !env::WIN32: - home = "HOME"; - $else - home = "USERPROFILE"; - $endif - return get_var(using, home); -} - - -<* -Returns the current user's config directory. -*> -fn Path! get_config_dir(Allocator allocator) => @pool(allocator) -{ - $if env::WIN32: - return path::new(allocator, tget_var("AppData")); - $else - $if env::DARWIN: - String s = tget_var("HOME")!; - const DIR = "Library/Application Support"; - $else - String s = tget_var("XDG_CONFIG_HOME") ?? tget_var("HOME")!; - const DIR = ".config"; - $endif - return path::temp(s).append(allocator, DIR); - $endif -} - - -<* - @param [in] name - @require name.len > 0 -*> -fn bool clear_var(String name) => @pool() -{ - $switch - $case env::WIN32: - WString wname = name.to_wstring_tcopy()!!; - return win32::setEnvironmentVariableW(wname, null) == 0; - $case env::LIBC && !env::WIN32: - return libc::unsetenv(name.zstr_tcopy()) == 0; - $default: - return false; - $endswitch -} - -fn String! executable_path(Allocator allocator) -{ - $if env::DARWIN: - return darwin::executable_path(allocator); - $else - return SearchResult.MISSING?; - $endif -} diff --git a/lib7/std/os/linux/heap.c3 b/lib7/std/os/linux/heap.c3 deleted file mode 100644 index 1de0a759b..000000000 --- a/lib7/std/os/linux/heap.c3 +++ /dev/null @@ -1,2 +0,0 @@ -module std::os::linux @if(env::LINUX); -extern fn usz malloc_usable_size(void* ptr); \ No newline at end of file diff --git a/lib7/std/os/linux/linux.c3 b/lib7/std/os/linux/linux.c3 deleted file mode 100644 index 50bf56107..000000000 --- a/lib7/std/os/linux/linux.c3 +++ /dev/null @@ -1,238 +0,0 @@ -module std::os::linux @if(env::LINUX); -import libc, std::os, std::io, std::collections::list; - -extern fn isz readlink(ZString path, char* buf, usz bufsize); - -const PT_PHDR = 6; -const EI_NIDENT = 16; -def Elf32_Half = ushort; -def Elf32_Word = uint; -def Elf32_Addr = uint; -def Elf32_Off = uint; - -struct Elf32_Ehdr -{ - char[EI_NIDENT] e_ident; - Elf32_Half e_type; - Elf32_Half e_machine; - Elf32_Word e_version; - Elf32_Addr e_entry; - Elf32_Off e_phoff; - Elf32_Off e_shoff; - Elf32_Word e_flags; - Elf32_Half e_ehsize; - Elf32_Half e_phentsize; - Elf32_Half e_phnum; - Elf32_Half e_shentsize; - Elf32_Half e_shnum; - Elf32_Half e_shstrndx; -} - -struct Elf32_Phdr -{ - Elf32_Word p_type; - Elf32_Off p_offset; - Elf32_Addr p_vaddr; - Elf32_Addr p_paddr; - Elf32_Word p_filesz; - Elf32_Word p_memsz; - Elf32_Word p_flags; - Elf32_Word p_align; -} - -def Elf64_Addr = ulong; -def Elf64_Half = ushort; -def Elf64_Off = ulong; -def Elf64_Word = uint; -def Elf64_Sword = int; -def Elf64_Sxword = long; -def Elf64_Lword = ulong; -def Elf64_Xword = ulong; - -struct Elf64_Ehdr -{ - char[EI_NIDENT] e_ident; - Elf64_Half e_type; - Elf64_Half e_machine; - Elf64_Word e_version; - Elf64_Addr e_entry; - Elf64_Off e_phoff; - Elf64_Off e_shoff; - Elf64_Word e_flags; - Elf64_Half e_ehsize; - Elf64_Half e_phentsize; - Elf64_Half e_phnum; - Elf64_Half e_shentsize; - Elf64_Half e_shnum; - Elf64_Half e_shstrndx; -} - -struct Elf64_Phdr -{ - Elf64_Word p_type; - Elf64_Word p_flags; - Elf64_Off p_offset; - Elf64_Addr p_vaddr; - Elf64_Addr p_paddr; - Elf64_Xword p_filesz; - Elf64_Xword p_memsz; - Elf64_Xword p_align; -} - -extern fn CInt dladdr(void* addr, Linux_Dl_info* info); - -struct Linux_Dl_info -{ - ZString dli_fname; /* Pathname of shared object */ - void* dli_fbase; /* Base address of shared object */ - ZString dli_sname; /* Name of nearest symbol */ - void* dli_saddr; /* Address of nearest symbol */ -} - -fn ulong! elf_module_image_base(String path) @local -{ - File file = file::open(path, "rb")!; - defer (void)file.close(); - char[4] buffer; - io::read_all(&file, &buffer)!; - if (buffer != { 0x7f, 'E', 'L', 'F'}) return BacktraceFault.IMAGE_NOT_FOUND?; - bool is_64 = file.read_byte()! == 2; - bool is_little_endian = file.read_byte()! == 1; - // Actually, not supported. - if (!is_little_endian) return BacktraceFault.IMAGE_NOT_FOUND?; - file.seek(0)!; - if (is_64) - { - Elf64_Ehdr file_header; - io::read_any(&file, &file_header)!; - if (file_header.e_ehsize != Elf64_Ehdr.sizeof) return BacktraceFault.IMAGE_NOT_FOUND?; - for (isz i = 0; i < file_header.e_phnum; i++) - { - Elf64_Phdr header; - file.seek((usz)file_header.e_phoff + (usz)file_header.e_phentsize * i)!; - io::read_any(&file, &header)!; - if (header.p_type == PT_PHDR) return header.p_vaddr - header.p_offset; - } - return 0; - } - Elf32_Ehdr file_header; - io::read_any(&file, &file_header)!; - if (file_header.e_ehsize != Elf32_Ehdr.sizeof) return BacktraceFault.IMAGE_NOT_FOUND?; - for (isz i = 0; i < file_header.e_phnum; i++) - { - Elf32_Phdr header; - file.seek(file_header.e_phoff + (usz)file_header.e_phentsize * i)!; - io::read_any(&file, &header)!; - if (header.p_type == PT_PHDR) return (ulong)header.p_vaddr - header.p_offset; - } - return 0; -} - -fn void! backtrace_add_from_exec(Allocator allocator, BacktraceList* list, void* addr) @local -{ - char[] buf = mem::temp_alloc_array(char, 1024); - - String exec_path = process::execute_stdout_to_buffer(buf, {"realpath", "-e", string::tformat("/proc/%d/exe", posix::getpid())})!; - String obj_name = exec_path.copy(allocator); - String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", exec_path, string::tformat("0x%x", addr)})!; - return backtrace_add_addr2line(allocator, list, addr, addr2line, obj_name, "???"); -} - -fn void! backtrace_add_from_dlinfo(Allocator allocator, BacktraceList* list, void* addr, Linux_Dl_info* info) @local -{ - char[] buf = mem::temp_alloc_array(char, 1024); - - void* obj_addr = addr - (uptr)info.dli_fbase + (uptr)elf_module_image_base(info.dli_fname.str_view())!; - ZString obj_path = info.dli_fname; - String sname = info.dli_sname ? info.dli_sname.str_view() : "???"; - String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::tformat("0x%x", obj_addr - 1)})!; - return backtrace_add_addr2line(allocator, list, addr, addr2line, info.dli_fname.str_view(), sname); -} - -fn Backtrace! backtrace_line_parse(Allocator allocator, String string, String obj_name, String func_name, bool is_inlined) -{ - String[] parts = string.trim().tsplit(" at "); - if (parts.len != 2) return SearchResult.MISSING?; - - uint line = 0; - String source = ""; - if (!parts[1].contains("?") && parts[1].contains(":")) - { - usz index = parts[1].rindex_of_char(':')!; - source = parts[1][:index]; - line = parts[1][index + 1..].to_uint()!; - } - return { - .function = parts[0].copy(allocator), - .object_file = obj_name.copy(allocator), - .file = source.copy(allocator), - .line = line, - .allocator = allocator, - .is_inline = is_inlined - }; -} -fn void! backtrace_add_addr2line(Allocator allocator, BacktraceList* list, void* addr, String addr2line, String obj_name, String func_name) @local -{ - String[] inline_parts = addr2line.tsplit("(inlined by)"); - usz last = inline_parts.len - 1; - foreach (i, part : inline_parts) - { - bool is_inline = i != last; - Backtrace! trace = backtrace_line_parse(allocator, part, obj_name, func_name, is_inline); - if (catch trace) - { - list.push({ - .function = func_name.copy(allocator), - .object_file = obj_name.copy(allocator), - .offset = (uptr)addr, - .file = "".copy(allocator), - .line = 0, - .allocator = allocator, - .is_inline = is_inline - }); - continue; - } - list.push(trace); - } -} - -fn void! backtrace_add_element(Allocator allocator, BacktraceList *list, void* addr) @local -{ - if (!addr) - { - list.push(backtrace::BACKTRACE_UNKNOWN); - return; - } - - @pool(allocator) - { - Linux_Dl_info info; - if (dladdr(addr, &info) == 0) - { - return backtrace_add_from_exec(allocator, list, addr); - } - return backtrace_add_from_dlinfo(allocator, list, addr, &info); - }; -} - -fn BacktraceList! symbolize_backtrace(Allocator allocator, void*[] backtrace) -{ - BacktraceList list; - list.init(allocator, backtrace.len); - defer catch - { - foreach (trace : list) - { - trace.free(); - } - list.free(); - } - @pool(allocator) - { - foreach (addr : backtrace) - { - backtrace_add_element(allocator, &list, addr)!; - } - }; - return list; -} diff --git a/lib7/std/os/macos/cf_allocator.c3 b/lib7/std/os/macos/cf_allocator.c3 deleted file mode 100644 index 9cb26e194..000000000 --- a/lib7/std/os/macos/cf_allocator.c3 +++ /dev/null @@ -1,18 +0,0 @@ -module std::os::macos::cf @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework"); - -distinct CFAllocatorRef = void*; -distinct CFAllocatorContextRef = void*; -def CFOptionFlags = usz; - -macro CFAllocatorRef default_allocator() => macos_CFAllocatorGetDefault(); -macro void CFAllocatorRef.dealloc(CFAllocatorRef allocator, void* ptr) => macos_CFAllocatorDeallocate(allocator, ptr); -macro void* CFAllocatorRef.alloc(CFAllocatorRef allocator, usz size) => macos_CFAllocatorAllocate(allocator, size, 0); -macro usz CFAllocatorRef.get_preferred_size(CFAllocatorRef allocator, usz req_size) => macos_CFAllocatorGetPreferredSizeForSize(allocator, req_size, 0); -macro void CFAllocatorRef.set_default(CFAllocatorRef allocator) => macos_CFAllocatorSetDefault(allocator); - -extern fn CFAllocatorRef macos_CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContextRef context) @extern("CFAllocatorCreate") @builtin; -extern fn void macos_CFAllocatorDeallocate(CFAllocatorRef allocator, void* ptr) @extern("CFAllocatorDeallocate") @builtin; -extern fn CFAllocatorRef macos_CFAllocatorGetDefault() @extern("CFAllocatorGetDefault") @builtin; -extern fn void macos_CFAllocatorSetDefault(CFAllocatorRef allocator) @extern("CFAllocatorSetDefault") @builtin; -extern fn void* macos_CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extern("CFAllocatorAllocate") @builtin; -extern fn CFIndex macos_CFAllocatorGetPreferredSizeForSize(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extern("CFAllocatorGetPreferredSizeForSize") @builtin; diff --git a/lib7/std/os/macos/cf_array.c3 b/lib7/std/os/macos/cf_array.c3 deleted file mode 100644 index 749afa5af..000000000 --- a/lib7/std/os/macos/cf_array.c3 +++ /dev/null @@ -1,11 +0,0 @@ -module std::os::macos::cf @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework"); - -distinct CFArrayRef = void*; -distinct CFArrayCallBacksRef = void*; -distinct CFMutableArrayRef = void*; -extern fn CFArrayRef macos_CFArrayCreate(CFAllocatorRef allocator, void** values, CFIndex num_values, CFArrayCallBacksRef callBacks) @extern("CFArrayCreate") @builtin; -extern fn CFArrayRef macos_CFArrayCopy(CFAllocatorRef allocator, CFArrayRef array) @extern("CFArrayCopy") @builtin; -extern fn CFIndex macos_CFArrayGetCount(CFArrayRef array) @extern("CFArrayGetCount") @builtin; -extern fn void macos_CFArrayAppendArray(CFMutableArrayRef theArray, CFArrayRef otherArray, CFRange otherRange) @extern("CFArrayAppendArray") @builtin; -extern fn CFMutableArrayRef macos_CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, CFArrayCallBacksRef callBacks) @extern("CFArrayCreateMutable") @builtin; -extern fn void macos_CFArrayAppendValue(CFMutableArrayRef theArray, void *value) @extern("CFArrayAppendValue") @builtin; diff --git a/lib7/std/os/macos/cocoa.c3 b/lib7/std/os/macos/cocoa.c3 deleted file mode 100644 index e380e0fc7..000000000 --- a/lib7/std/os/macos/cocoa.c3 +++ /dev/null @@ -1,3 +0,0 @@ -module std::os::darwin::cocoa @if(env::OS_TYPE == MACOS) @link("Cocoa.framework"); - -extern fn int nsApplicationMain(int argc, char **argv) @extern("NSApplicationMain"); diff --git a/lib7/std/os/macos/core_foundation.c3 b/lib7/std/os/macos/core_foundation.c3 deleted file mode 100644 index 0e61735fd..000000000 --- a/lib7/std/os/macos/core_foundation.c3 +++ /dev/null @@ -1,12 +0,0 @@ -module std::os::macos::cf @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework"); - -distinct CFTypeRef = void*; -def CFIndex = isz; -struct CFRange -{ - CFIndex location; - CFIndex length; -} - -extern fn CFTypeRef macos_CFRetain(CFTypeRef cf) @extern("CFRetain") @builtin; -extern fn void macos_CFRelease(CFTypeRef cf) @extern("CFRelease") @builtin; \ No newline at end of file diff --git a/lib7/std/os/macos/darwin.c3 b/lib7/std/os/macos/darwin.c3 deleted file mode 100644 index f4805855f..000000000 --- a/lib7/std/os/macos/darwin.c3 +++ /dev/null @@ -1,157 +0,0 @@ -module std::os::darwin @if(env::DARWIN); -import std::collections::list, std::os; - -const CTL_UNSPEC = 0; /* unused */ -const CTL_KERN = 1; /* "high kernel": proc, limits */ -const CTL_VM = 2; /* virtual memory */ -const CTL_VFS = 3; /* file system, mount type is next */ -const CTL_NET = 4; /* network, see socket.h */ -const CTL_DEBUG = 5; /* debugging parameters */ -const CTL_HW = 6; /* generic cpu/io */ -const CTL_MACHDEP = 7; /* machine dependent */ -const CTL_USER = 8; /* user-level */ -const CTL_MAXID = 9; /* number of valid top-level ids */ - -const HW_MACHINE = 1; /* string: machine class */ -const HW_MODEL = 2; /* string: specific machine model */ -const HW_NCPU = 3; /* int: number of cpus */ -const HW_BYTEORDER = 4; /* int: machine byte order */ -const HW_PHYSMEM = 5; /* int: total memory */ -const HW_USERMEM = 6; /* int: non-kernel memory */ -const HW_PAGESIZE = 7; /* int: software page size */ -const HW_DISKNAMES = 8; /* strings: disk drive names */ -const HW_DISKSTATS = 9; /* struct: diskstats[] */ -const HW_EPOCH = 10; /* int: 0 for Legacy, else NewWorld */ -const HW_FLOATINGPT = 11; /* int: has HW floating point? */ -const HW_MACHINE_ARCH = 12; /* string: machine architecture */ -const HW_VECTORUNIT = 13; /* int: has HW vector unit? */ -const HW_BUS_FREQ = 14; /* int: Bus Frequency */ -const HW_CPU_FREQ = 15; /* int: CPU Frequency */ -const HW_CACHELINE = 16; /* int: Cache Line Size in Bytes */ -const HW_L1ICACHESIZE = 17; /* int: L1 I Cache Size in Bytes */ -const HW_L1DCACHESIZE = 18; /* int: L1 D Cache Size in Bytes */ -const HW_L2SETTINGS = 19; /* int: L2 Cache Settings */ -const HW_L2CACHESIZE = 20; /* int: L2 Cache Size in Bytes */ -const HW_L3SETTINGS = 21; /* int: L3 Cache Settings */ -const HW_L3CACHESIZE = 22; /* int: L3 Cache Size in Bytes */ -const HW_MAXID = 23; /* number of valid hw ids */ - -extern fn CInt sysctl(CInt *name, CUInt namelen, void *oldp, usz *oldlenp, void *newp, usz newlen); -extern fn CInt darwin_NSGetExecutablePath(char* buffer, uint *size) @extern("_NSGetExecutablePath") @builtin; -extern fn Darwin_segment_command_64* getsegbyname(ZString segname); -extern fn uint _dyld_image_count(); -extern fn ZString _dyld_get_image_name(uint image_index); -extern fn iptr _dyld_get_image_vmaddr_slide(uint image_index); -extern fn CInt dladdr(void* addr, Darwin_Dl_info* info); - -struct Darwin_Dl_info -{ - ZString dli_fname; /* Pathname of shared object */ - void* dli_fbase; /* Base address of shared object */ - ZString dli_sname; /* Name of nearest symbol */ - void* dli_saddr; /* Address of nearest symbol */ -} - -struct Darwin_segment_command_64 -{ - uint cmd; /* LC_SEGMENT_64 */ - uint cmdsize; /* includes sizeof section_64 structs */ - char[16] segname; /* segment name */ - ulong vmaddr; /* memory address of this segment */ - ulong vmsize; /* memory size of this segment */ - ulong fileoff; /* file offset of this segment */ - ulong filesize; /* amount to map from the file */ - int maxprot; /* maximum VM protection */ - int initprot; /* initial VM protection */ - uint nsects; /* number of sections in segment */ - uint flags; /* flags */ -} - - -fn String! executable_path(Allocator allocator) -{ - char[4096] path; - uint len = path.len; - if (darwin_NSGetExecutablePath(&path, &len) < 0) return SearchResult.MISSING?; - return ((ZString)&path).copy(allocator); -} - -fn uptr! load_address() @local -{ - Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT"); - if (!cmd) return BacktraceFault.SEGMENT_NOT_FOUND?; - String path = env::executable_path(tmem()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!; - uint dyld_count = darwin::_dyld_image_count(); - for (uint i = 0; i < dyld_count; i++) - { - ZString image_name = darwin::_dyld_get_image_name(i); - if (!image_name) continue; - if (image_name.str_view() != path) continue; - return cmd.vmaddr + darwin::_dyld_get_image_vmaddr_slide(i); - } - return BacktraceFault.IMAGE_NOT_FOUND?; -} - - -fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_address, Allocator allocator = allocator::heap()) @local -{ - @pool(allocator) - { - if (buffer) - { - char* buf = tmalloc(1024); - String s = process::execute_stdout_to_buffer(buf[:1024], - { "atos", "-o", execpath, "-arch", env::AARCH64 ? "arm64" : "x86_64", "-l", - string::tformat("%p", load_address), - string::tformat("%p", buffer - 1), - "-fullPath" })!; - String[] parts = s.tsplit(" "); - if (parts.len == 4) - { - String[] path_parts = parts[3].tsplit(":"); - return { - .offset = (uptr)buffer, - .function = parts[0].copy(allocator), - .object_file = parts[2][..^2].copy(allocator), - .file = path_parts[0][1..].copy(allocator), - .line = path_parts[1][..^2].to_uint()!, - .allocator = allocator - }; - } - } - Darwin_Dl_info info; - if (!buffer || !darwin::dladdr(buffer, &info)) return backtrace::BACKTRACE_UNKNOWN; - return { - .offset = (uptr)buffer, - .function = info.dli_sname ? info.dli_sname.copy(allocator) : "???".copy(allocator), - .object_file = info.dli_fname.copy(allocator), - .file = "".copy(allocator), - .line = 0, - .allocator = allocator - }; - }; -} - -fn BacktraceList! symbolize_backtrace(Allocator allocator, void*[] backtrace) -{ - void *load_addr = (void *)load_address()!; - BacktraceList list; - list.init(allocator, backtrace.len); - defer catch - { - foreach (trace : list) - { - trace.free(); - } - list.free(); - } - @pool(allocator) - { - String execpath = executable_path(allocator::temp())!; - foreach (addr : backtrace) - { - list.push(backtrace_load_element(execpath, addr, load_addr, allocator) ?? backtrace::BACKTRACE_UNKNOWN); - } - }; - return list; -} diff --git a/lib7/std/os/macos/heap.c3 b/lib7/std/os/macos/heap.c3 deleted file mode 100644 index 632776a88..000000000 --- a/lib7/std/os/macos/heap.c3 +++ /dev/null @@ -1,3 +0,0 @@ -module std::os::darwin @if(env::DARWIN); - -extern fn usz malloc_size(void* ptr); diff --git a/lib7/std/os/macos/objc.c3 b/lib7/std/os/macos/objc.c3 deleted file mode 100644 index 48e2aa0a0..000000000 --- a/lib7/std/os/macos/objc.c3 +++ /dev/null @@ -1,230 +0,0 @@ -module std::os::macos::objc @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework"); - -distinct ObjcClass = void*; -distinct ObjcMethod = void*; -distinct ObjcIvar = void*; -distinct ObjcSelector = void*; -def ObjcId = void*; -def SendVoid = fn void*(void*, ObjcSelector); - -fault ObjcFailure -{ - CLASS_NOT_FOUND, - UNKNOWN_EVENT -} - -macro ZString ObjcClass.name(ObjcClass cls) => class_getName(cls); -macro ObjcClass ObjcClass.superclass(ObjcClass cls) => class_getSuperclass(cls); -macro bool ObjcClass.responds_to(ObjcClass cls, ObjcSelector sel) => class_respondsToSelector(cls, sel); -macro ObjcMethod ObjcClass.method(ObjcClass cls, ObjcSelector name) => class_getClassMethod(cls, name); - -macro bool ObjcSelector.equals(ObjcSelector a, ObjcSelector b) => a == b; -macro bool ObjcClass.equals(ObjcClass a, ObjcClass b) => a == b; - -fn ObjcId alloc(ObjcClass cls) => objc::msg_send(cls, SendVoid, "alloc"); -fn void release(ObjcId id) => objc::msg_send(id, SendVoid, "release"); - -macro ObjcClass! class_by_name(ZString c) -{ - ObjcClass cls = objc::lookUpClass(c); - return cls ?: ObjcFailure.CLASS_NOT_FOUND?; -} - -macro ObjcClass[] class_get_list(Allocator allocator = allocator::heap()) -{ - int num_classes = objc::getClassList(null, 0); - if (!num_classes) return {}; - ObjcClass[] entries = allocator.new_array(ObjcClass, num_classes); - objc::getClassList(entries.ptr, entries.len); - return entries; -} - -extern fn void msgSend(...) @extern("objc_msgSend") @builtin; -extern fn ObjcSelector sel_getUid(ZString); - -macro msg_send(id, $FunctionType, ZString $selector, ...) -{ - return (($FunctionType)&msgSend)((ObjcId)id, sel_getUid($selector), $vasplat); -} - -extern fn ObjcClass getClass(ZString name) @extern("objc_getClass"); -extern fn int getClassList(ObjcClass* buffer, int buffer_count) @extern("objc_getClassList"); -extern fn ObjcClass lookUpClass(ZString name) @extern("objc_lookUpClass") @builtin; - -extern fn ZString class_getName(ObjcClass cls); -extern fn ObjcClass class_getSuperclass(ObjcClass cls); -extern fn ObjcMethod class_getClassMethod(ObjcClass cls, ObjcSelector name); -extern fn bool class_respondsToSelector(ObjcClass cls, ObjcSelector name); -extern fn ObjcSelector sel_registerName(ZString str); -extern fn bool class_addIvar(ObjcClass cls, ZString name, int size, double alignment, ZString types); -extern fn bool class_addMethod(ObjcClass cls, ObjcSelector name, void* imp, ZString types); - -extern fn ObjcIvar getInstanceVariable(ObjcId id, ZString name, void* outValue) @extern("object_getInstanceVariable"); -extern fn ObjcIvar setInstanceVariable(ObjcId id, ZString name, void* value) @extern("object_setInstanceVariable"); -extern fn ObjcClass allocateClassPair(ObjcClass cls, ZString name, uint extraBytes) @extern("objc_allocateClassPair"); - -enum StatusItemLength : (double val) -{ - VARIABLE = -1.0, - SQUARE = -2.0, -} - -enum ApplicationActivationPolicy : (int val) -{ - REGULAR = 0, - ACCESSORY = 1, - PROHIBITED = 2, -} - -enum WindowStyleMask : (int val) -{ - BORDERLESS = 0, - TITLED = 1 << 0, - CLOSABLE = 1 << 1, - MINIATURIZABLE = 1 << 2, - RESIZABLE = 1 << 3, - TEXTURED_BACKGROUND = 1 << 8, - UNIFIED_TITLE_AND_TOOLBAR = 1 << 12, - FULL_SCREEN = 1 << 14, - FULL_SIZE_CONTENT_VIEW = 1 << 15, - UTILITY_WINDOW = 1 << 4, - DOC_MODAL_WINDOW = 1 << 6, - NONACTIVATING_PANEL = 1 << 7, - HUD_WINDOW = 1 << 13 -} - -enum BackingStore : (int val) -{ - RETAINED = 0, - NONRETAINED = 1, - BUFFERED = 2 -} - -enum EventType : (long val) -{ - LEFT_MOUSE_DOWN = 1, - LEFT_MOUSE_UP = 2, - RIGHT_MOUSE_DOWN = 3, - RIGHT_MOUSE_UP = 4, - MOUSE_MOVED = 5, - LEFT_MOUSE_DRAGGED = 6, - RIGHT_MOUSE_DRAGGED = 7, - MOUSE_ENTERED = 8, - MOUSE_EXITED = 9, - KEY_DOWN = 10, - KEY_UP = 11, - FLAGS_CHANGED = 12, - APPKIT_DEFINED = 13, - SYSTEM_DEFINED = 14, - APPLICATION_DEFINED = 15, - PERIODIC = 16, - CURSOR_UPDATE = 17, - SCROLL_WHEEL = 22, - TABLET_POINT = 23, - TABLET_PROXIMITY = 24, - OTHER_MOUSE_DOWN = 25, - OTHER_MOUSE_UP = 26, - OTHER_MOUSE_DRAGGED = 27, - GESTURE = 29, - MAGNIFY = 30, - SWIPE = 31, - ROTATE = 18, - BEGIN_GESTURE = 19, - END_GESTURE = 20, - SMART_MAGNIFY = 32, - QUICK_LOOK = 33, - PRESSURE = 34, - DIRECT_TOUCH = 37, - CHANGE_MODE = 38, -} - -fn EventType! event_type_from(int val) -{ - switch(val) - { - case EventType.LEFT_MOUSE_DOWN.val: return LEFT_MOUSE_DOWN; - case EventType.LEFT_MOUSE_UP.val: return LEFT_MOUSE_UP; - case EventType.RIGHT_MOUSE_DOWN.val: return RIGHT_MOUSE_DOWN; - case EventType.RIGHT_MOUSE_UP.val: return RIGHT_MOUSE_UP; - case EventType.MOUSE_MOVED.val: return MOUSE_MOVED; - case EventType.LEFT_MOUSE_DRAGGED.val: return LEFT_MOUSE_DRAGGED; - case EventType.RIGHT_MOUSE_DRAGGED.val: return RIGHT_MOUSE_DRAGGED; - case EventType.MOUSE_ENTERED.val: return MOUSE_ENTERED; - case EventType.MOUSE_EXITED.val: return MOUSE_EXITED; - case EventType.KEY_DOWN.val: return KEY_DOWN; - case EventType.KEY_UP.val: return KEY_UP; - case EventType.FLAGS_CHANGED.val: return FLAGS_CHANGED; - case EventType.APPKIT_DEFINED.val: return APPKIT_DEFINED; - case EventType.SYSTEM_DEFINED.val: return SYSTEM_DEFINED; - case EventType.APPLICATION_DEFINED.val: return APPLICATION_DEFINED; - case EventType.PERIODIC.val: return PERIODIC; - case EventType.CURSOR_UPDATE.val: return CURSOR_UPDATE; - case EventType.SCROLL_WHEEL.val: return SCROLL_WHEEL; - case EventType.TABLET_POINT.val: return TABLET_POINT; - case EventType.TABLET_PROXIMITY.val: return TABLET_PROXIMITY; - case EventType.OTHER_MOUSE_DOWN.val: return OTHER_MOUSE_DOWN; - case EventType.OTHER_MOUSE_UP.val: return OTHER_MOUSE_UP; - case EventType.OTHER_MOUSE_DRAGGED.val: return OTHER_MOUSE_DRAGGED; - case EventType.GESTURE.val: return GESTURE; - case EventType.MAGNIFY.val: return MAGNIFY; - case EventType.SWIPE.val: return SWIPE; - case EventType.ROTATE.val: return ROTATE; - case EventType.BEGIN_GESTURE.val: return BEGIN_GESTURE; - case EventType.END_GESTURE.val: return END_GESTURE; - case EventType.SMART_MAGNIFY.val: return SMART_MAGNIFY; - case EventType.QUICK_LOOK.val: return QUICK_LOOK; - case EventType.PRESSURE.val: return PRESSURE; - case EventType.DIRECT_TOUCH.val: return DIRECT_TOUCH; - case EventType.CHANGE_MODE.val: return CHANGE_MODE; - default: return ObjcFailure.UNKNOWN_EVENT?; - } -} - -enum EventMask : (long val) -{ - LEFT_MOUSE_DOWN = 1 << EventType.LEFT_MOUSE_DOWN.val, - LEFT_MOUSE_UP = 1 << EventType.LEFT_MOUSE_UP.val, - RIGHT_MOUSE_DOWN = 1 << EventType.RIGHT_MOUSE_DOWN.val, - RIGHT_MOUSE_UP = 1 << EventType.RIGHT_MOUSE_UP.val, - MOUSE_MOVED = 1 << EventType.MOUSE_MOVED.val, - LEFT_MOUSE_DRAGGED = 1 << EventType.LEFT_MOUSE_DRAGGED.val, - RIGHT_MOUSE_DRAGGED = 1 << EventType.RIGHT_MOUSE_DRAGGED.val, - MOUSE_ENTERED = 1 << EventType.MOUSE_ENTERED.val, - MOUSE_EXITED = 1 << EventType.MOUSE_EXITED.val, - KEY_DOWN = 1 << EventType.KEY_DOWN.val, - KEY_UP = 1 << EventType.KEY_UP.val, - FLAGS_CHANGED = 1 << EventType.FLAGS_CHANGED.val, - APPKIT_DEFINED = 1 << EventType.APPKIT_DEFINED.val, - SYSTEM_DEFINED = 1 << EventType.SYSTEM_DEFINED.val, - APPLICATION_DEFINED = 1 << EventType.APPLICATION_DEFINED.val, - PERIODIC = 1 << EventType.PERIODIC.val, - CURSOR_UPDATE = 1 << EventType.CURSOR_UPDATE.val, - SCROLL_WHEEL = 1 << EventType.SCROLL_WHEEL.val, - TABLET_POINT = 1 << EventType.TABLET_POINT.val, - TABLET_PROXIMITY = 1 << EventType.TABLET_PROXIMITY.val, - OTHER_MOUSE_DOWN = 1 << EventType.OTHER_MOUSE_DOWN.val, - OTHER_MOUSE_UP = 1 << EventType.OTHER_MOUSE_UP.val, - OTHER_MOUSE_DRAGGED = 1 << EventType.OTHER_MOUSE_DRAGGED.val, - GESTURE = 1 << EventType.GESTURE.val, - MAGNIFY = 1 << EventType.MAGNIFY.val, - SWIPE = 1 << EventType.SWIPE.val, - ROTATE = 1 << EventType.ROTATE.val, - BEGIN_GESTURE = 1 << EventType.BEGIN_GESTURE.val, - END_GESTURE = 1 << EventType.END_GESTURE.val, - SMART_MAGNIFY = 1L << EventType.SMART_MAGNIFY.val, - DIRECT_TOUCH = 1L << EventType.DIRECT_TOUCH.val, - ANY = long.max, -} - -enum EventModifierFlag : (int val) -{ - CAPS_LOCK = 1 << 16, - SHIFT = 1 << 17, - CONTROL = 1 << 18, - OPTION = 1 << 19, - COMMAND = 1 << 20, - NUMERIC_PAD = 1 << 21, - FUNCTION = 1 << 23, - HELP = 1 << 22, -} - diff --git a/lib7/std/os/posix/files.c3 b/lib7/std/os/posix/files.c3 deleted file mode 100644 index ae87913bf..000000000 --- a/lib7/std/os/posix/files.c3 +++ /dev/null @@ -1,50 +0,0 @@ -module std::os::posix @if(env::POSIX); -import libc; - -def Mode_t = uint; -distinct DIRPtr = void*; - -struct Posix_dirent -{ - Ino_t d_fileno; - Off_t d_off @if(!env::NETBSD); - ushort d_reclen; - ushort d_namelen @if(env::DARWIN || env::NETBSD); - char d_type; - - char d_namelen @if(env::OPENBSD); - char[4] d_pad0 @if(env::OPENBSD); - - char d_pad0 @if(env::FREEBSD); - ushort d_namelen @if(env::FREEBSD); - ushort d_pad1 @if(env::FREEBSD); - - char[255+1] name @if(env::FREEBSD || env::OPENBSD); - char[511+1] name @if(env::NETBSD); - char[1024] name @if(env::DARWIN); - char[?] name @if(!env::DARWIN && !env::BSD_FAMILY); -} - -extern fn int rmdir(ZString); -extern fn int mkdir(ZString, ushort mode_t); -extern fn int chdir(ZString); -extern fn ZString getcwd(char* pwd, usz len); -extern fn CInt pipe(CInt[2]* pipes); -extern fn CFile fdopen(CInt fd, ZString mode); -extern fn CInt access(ZString path, CInt mode); -extern fn Posix_dirent* readdir(DIRPtr) @extern("readdir") @if(!USE_DARWIN_INODE64) ; -extern fn DIRPtr opendir(ZString); -extern fn void closedir(DIRPtr); - -const DT_UNKNOWN = 0; -const DT_FIFO = 1; -const DT_CHR = 2; -const DT_DIR = 4; -const DT_BLK = 6; -const DT_REG = 8; -const DT_LNK = 10; -const DT_SOCK = 12; -const DT_WHT = 14; - -const USE_DARWIN_INODE64 = env::DARWIN && env::X86_64; -extern fn Posix_dirent* readdir(DIRPtr) @extern("readdir$INODE64") @if(USE_DARWIN_INODE64); diff --git a/lib7/std/os/posix/general.c3 b/lib7/std/os/posix/general.c3 deleted file mode 100644 index 19f38e399..000000000 --- a/lib7/std/os/posix/general.c3 +++ /dev/null @@ -1,4 +0,0 @@ -module std::os::posix; - -extern ZString* environ; - diff --git a/lib7/std/os/posix/heap.c3 b/lib7/std/os/posix/heap.c3 deleted file mode 100644 index 0e3d02d91..000000000 --- a/lib7/std/os/posix/heap.c3 +++ /dev/null @@ -1,3 +0,0 @@ -module std::os::posix @if(env::POSIX); - -extern fn CInt posix_memalign(void **memptr, usz alignment, usz size); \ No newline at end of file diff --git a/lib7/std/os/posix/process.c3 b/lib7/std/os/posix/process.c3 deleted file mode 100644 index 054d295ef..000000000 --- a/lib7/std/os/posix/process.c3 +++ /dev/null @@ -1,95 +0,0 @@ -module std::os::posix @if(env::POSIX); -import libc; - -struct Posix_spawn_file_actions_t -{ - int __allocated; - int __used; - void* __actions; - int[16] __pad; -} - -struct Posix_spawnattr_t -{ - void*[42] opaque; -} - -extern fn CInt posix_spawn_file_actions_init(Posix_spawn_file_actions_t *file_actions); -extern fn CInt posix_spawn_file_actions_destroy(Posix_spawn_file_actions_t *file_actions); -extern fn CInt posix_spawn_file_actions_addclose(Posix_spawn_file_actions_t *file_actions, CInt fd); -extern fn CInt posix_spawn_file_actions_adddup2(Posix_spawn_file_actions_t *file_actions, CInt fd, CInt newfd); - -extern fn CInt get_nprocs(); -extern fn CInt get_nprocs_conf(); - -def spawn_file_actions_init = posix_spawn_file_actions_init; -def spawn_file_actions_destroy = posix_spawn_file_actions_destroy; -def spawn_file_actions_addclose = posix_spawn_file_actions_addclose; -def spawn_file_actions_adddup2 = posix_spawn_file_actions_adddup2; - -extern fn CInt posix_spawnp(Pid_t* pid, char* file, Posix_spawn_file_actions_t* file_actions, - Posix_spawnattr_t* attrp, ZString* argv, ZString* envp); -extern fn CInt posix_spawn(Pid_t* pid, char* file, Posix_spawn_file_actions_t* file_actions, - Posix_spawnattr_t* attrp, ZString* argv, ZString* envp); -def spawnp = posix_spawnp; -def spawn = posix_spawn; - -extern fn CInt getpid(); -extern fn CInt kill(Pid_t pid, CInt sig); -extern fn Pid_t waitpid(Pid_t pid, CInt* stat_loc, int options); -extern fn CInt raise(CInt sig); -extern fn ZString* backtrace_symbols(void** buffer, CInt size); -extern fn void backtrace_symbols_fd(void** buffer, CInt size, CInt fd); -macro CInt wEXITSTATUS(CInt status) => (status & 0xff00) >> 8; -macro CInt wTERMSIG(CInt status) => status & 0x7f; -macro CInt wSTOPSIG(CInt status) => wEXITSTATUS(status); -macro bool wIFEXITED(CInt status) => wTERMSIG(status) == 0; -macro bool wIFSIGNALED(CInt status) => ((ichar) ((status & 0x7f) + 1) >> 1) > 0; -macro bool wIFSTOPPED(CInt status) => (status & 0xff) == 0x7f; -macro bool wIFCONTINUED(CInt status) => status == __W_CONTINUED; -macro CInt wWCOREDUMP(CInt status) => status & __WCOREFLAG; -macro CInt w_EXITCODE(CInt ret, CInt sig) => (ret << 8) | sig; -macro CInt w_STOPCODE(CInt sig) => (sig << 8) | 0x7f; -const CInt __WCOREFLAG = 0x80; -const CInt __W_CONTINUED = 0xffff; -const CInt WNOHANG = 1; -const CInt WUNTRACES = 2; - -JmpBuf backtrace_jmpbuf @local; -def BacktraceFn = fn CInt(void** buffer, CInt size); - -fn CInt backtrace(void** buffer, CInt size) -{ - if (size < 1) return 0; - void* handle = libc::dlopen("libc.so.6", libc::RTLD_LAZY|libc::RTLD_NODELETE); - if (handle) - { - BacktraceFn backtrace_fn = libc::dlsym(handle, "backtrace"); - libc::dlclose(handle); - if (backtrace_fn) - { - return backtrace_fn(buffer, size); - } - } - // Loop through the return addresses until we hit a signal. - // This avoids using the frame address. - SignalFunction restore_backtrace = fn void(CInt) { - libc::longjmp(&backtrace_jmpbuf, 1); - }; - SignalFunction sig_bus = libc::signal(libc::SIGBUS, restore_backtrace); - SignalFunction sig_segv = libc::signal(libc::SIGSEGV, restore_backtrace); - SignalFunction sig_ill = libc::signal(libc::SIGILL, restore_backtrace); - - void*[128] buffer_first; - int i = 0; - for (i = 0; i < size; i++) - { - if (libc::setjmp(&backtrace_jmpbuf) == 1) break; - buffer[i] = builtin::get_returnaddress(i); - if (!buffer[i]) break; - } - libc::signal(libc::SIGBUS, sig_bus); - libc::signal(libc::SIGSEGV, sig_segv); - libc::signal(libc::SIGILL, sig_ill); - return i; -} diff --git a/lib7/std/os/posix/threads.c3 b/lib7/std/os/posix/threads.c3 deleted file mode 100644 index 9e1fd1969..000000000 --- a/lib7/std/os/posix/threads.c3 +++ /dev/null @@ -1,113 +0,0 @@ -module std::os::posix @if(env::POSIX); -import std::thread; -import libc; - -const PTHREAD_MUTEX_NORMAL = 0; -const PTHREAD_MUTEX_ERRORCHECK = env::LINUX ? 2 : 1; -const PTHREAD_MUTEX_RECURSIVE = env::LINUX ? 1 : 2; - -def PosixThreadFn = fn void*(void*); -distinct Pthread_t = void*; - -extern fn CInt pthread_create(Pthread_t*, Pthread_attr_t*, PosixThreadFn, void*); -extern fn CInt pthread_cancel(Pthread_t*); -extern fn CInt pthread_detach(Pthread_t); -extern fn CInt pthread_equal(Pthread_t this, Pthread_t other); -extern fn void pthread_exit(void* value_ptr) @noreturn; -extern fn CInt pthread_join(Pthread_t, void** value_ptr); -extern fn CInt pthread_kill(Pthread_t, CInt sig); -extern fn void pthread_once(Pthread_once_t*, OnceFn); -extern fn Pthread_t pthread_self(); -extern fn CInt pthread_setcancelstate(CInt state, CInt* oldstate); -extern fn CInt pthread_setcanceltype(CInt type, CInt* oldtype); -extern fn CInt pthread_testcancel(); - -extern fn CInt pthread_attr_destroy(Pthread_attr_t*); -extern fn CInt pthread_attr_getinheritsched(Pthread_attr_t*, CInt*); -extern fn CInt pthread_attr_getschedparam(Pthread_attr_t*, Pthread_sched_param*); -extern fn CInt pthread_attr_getschedpolicy(Pthread_attr_t*, CInt*); -extern fn CInt pthread_attr_getscope(Pthread_attr_t*, CInt*); -extern fn CInt pthread_attr_getstacksize(Pthread_attr_t*, usz*); -extern fn CInt pthread_attr_getstackaddr(Pthread_attr_t*, void**); -extern fn CInt pthread_attr_getdetachstate(Pthread_attr_t*, CInt*); -extern fn CInt pthread_attr_init(Pthread_attr_t*); -extern fn CInt pthread_attr_setinheritsched(Pthread_attr_t*, CInt); -extern fn CInt pthread_attr_setschedparam(Pthread_attr_t*, Pthread_sched_param*); -extern fn CInt pthread_attr_setschedpolicy(Pthread_attr_t*, CInt); -extern fn CInt pthread_attr_setscope(Pthread_attr_t*, CInt); -extern fn CInt pthread_attr_setstacksize(Pthread_attr_t*, usz); -extern fn CInt pthread_attr_setstackaddr(Pthread_attr_t*, void*); -extern fn CInt pthread_attr_setdetachstate(Pthread_attr_t*, CInt); - -extern fn CInt pthread_mutexattr_destroy(Pthread_mutexattr_t*); -extern fn CInt pthread_mutexattr_getprioceiling(Pthread_mutexattr_t*, CInt*); -extern fn CInt pthread_mutexattr_getprotocol(Pthread_mutexattr_t*, CInt*); -extern fn CInt pthread_mutexattr_gettype(Pthread_mutexattr_t*, CInt*); -extern fn CInt pthread_mutexattr_init(Pthread_mutexattr_t*); -extern fn CInt pthread_mutexattr_setprioceiling(Pthread_mutexattr_t*, CInt); -extern fn CInt pthread_mutexattr_setprotocol(Pthread_mutexattr_t*, CInt); -extern fn CInt pthread_mutexattr_settype(Pthread_mutexattr_t*, CInt); - -extern fn CInt pthread_mutex_destroy(Pthread_mutex_t*); -extern fn CInt pthread_mutex_init(Pthread_mutex_t*, Pthread_mutexattr_t*); -extern fn Errno pthread_mutex_lock(Pthread_mutex_t*); -extern fn Errno pthread_mutex_trylock(Pthread_mutex_t*); -extern fn Errno pthread_mutex_unlock(Pthread_mutex_t*); - -extern fn CInt pthread_condattr_destroy(Pthread_condattr_t*); -extern fn CInt pthread_condattr_init(Pthread_condattr_t*); - -extern fn CInt pthread_cond_broadcast(Pthread_cond_t*); -extern fn CInt pthread_cond_destroy(Pthread_cond_t*); -extern fn CInt pthread_cond_init(Pthread_cond_t*, Pthread_condattr_t*); -extern fn CInt pthread_cond_signal(Pthread_cond_t*); -extern fn CInt pthread_cond_timedwait(Pthread_cond_t*, Pthread_mutex_t*, TimeSpec*); -extern fn CInt pthread_cond_wait(Pthread_cond_t*, Pthread_mutex_t*); - -extern fn CInt pthread_rwlock_destroy(Pthread_rwlock_t*); -extern fn CInt pthread_rwlock_init(Pthread_rwlock_t*, Pthread_rwlockattr_t*); -extern fn CInt pthread_rwlock_rdlock(Pthread_rwlock_t*); -extern fn CInt pthread_rwlock_tryrdlock(Pthread_rwlock_t*); -extern fn CInt pthread_rwlock_trywrlock(Pthread_rwlock_t*); -extern fn CInt pthread_rwlock_unlock(Pthread_rwlock_t*); -extern fn CInt pthread_rwlock_wrlock(Pthread_rwlock_t*); - -extern fn CInt pthread_rwlockattr_destroy(Pthread_rwlockattr_t*); -extern fn CInt pthread_rwlockattr_getpshared(Pthread_rwlockattr_t*, CInt*); -extern fn CInt pthread_rwlockattr_init(Pthread_rwlockattr_t*); -extern fn CInt pthread_rwlockattr_setpshared(Pthread_rwlockattr_t*, CInt); - -extern fn CInt pthread_key_create(Pthread_key_t*, PosixThreadFn routine); -extern fn CInt pthread_key_delete(Pthread_key_t); -extern fn void* pthread_getspecific(Pthread_key_t); -extern fn CInt pthread_setspecific(Pthread_key_t, void* value_ptr); - -extern fn CInt pthread_atfork(OnceFn prepare, OnceFn parent, OnceFn child); -extern fn void pthread_cleanup_pop(CInt execute); -extern fn void pthread_cleanup_push(PosixThreadFn routine, void* routine_arg); - -extern fn int sched_yield(); - -module std::thread::os @if(env::POSIX && !env::LINUX); -distinct Pthread_attr_t = ulong[8]; -distinct Pthread_cond_t = ulong[6]; -distinct Pthread_condattr_t = ulong[8]; -distinct Pthread_key_t = ulong; -distinct Pthread_mutex_t = ulong[8]; -distinct Pthread_mutexattr_t = ulong[2]; -distinct Pthread_once_t = ulong[2]; -distinct Pthread_rwlock_t = ulong[25]; -distinct Pthread_rwlockattr_t = ulong[3]; -distinct Pthread_sched_param = ulong; - -module std::thread::os @if(env::LINUX); -distinct Pthread_attr_t = ulong[7]; // 24 on 32bit -distinct Pthread_cond_t = ulong[6]; -distinct Pthread_condattr_t = uint; -distinct Pthread_key_t = uint; -distinct Pthread_mutex_t = ulong[5]; // 24 on 32 bit -distinct Pthread_mutexattr_t = uint; -distinct Pthread_once_t = int; -distinct Pthread_rwlock_t = ulong[7]; // 32 on 3bit -distinct Pthread_rwlockattr_t = uint; -distinct Pthread_sched_param = uint; diff --git a/lib7/std/os/subprocess.c3 b/lib7/std/os/subprocess.c3 deleted file mode 100644 index 2d374970e..000000000 --- a/lib7/std/os/subprocess.c3 +++ /dev/null @@ -1,488 +0,0 @@ -module std::os::process @if(env::WIN32 || env::POSIX); -import std::io, libc, std::os; - -// This code is based on https://github.com/sheredom/subprocess.h - -fault SubProcessResult -{ - FAILED_TO_CREATE_PIPE, - FAILED_TO_OPEN_STDIN, - FAILED_TO_OPEN_STDOUT, - FAILED_TO_OPEN_STDERR, - FAILED_TO_START_PROCESS, - FAILED_TO_INITIALIZE_ACTIONS, - PROCESS_JOIN_FAILED, - PROCESS_TERMINATION_FAILED, - READ_FAILED, -} - -struct SubProcess -{ - CFile stdin_file; - CFile stdout_file; - CFile stderr_file; - - Win32_HANDLE hProcess @if(env::WIN32); - Win32_HANDLE hStdInput @if(env::WIN32); - Win32_HANDLE hEventOutput @if(env::WIN32); - Win32_HANDLE hEventError @if(env::WIN32); - - Pid_t child @if(!env::WIN32); - int return_status @if(!env::WIN32); - - bool is_alive; -} - -bitstruct SubProcessOptions : int -{ - // Combine stdout and stderr to the same file - bool combined_stdout_stderr; - // Child process should inhert env variables of parent process - bool inherit_environment; - // Enable async reading of stdout/stderr before completion - bool read_async; - // Spawn child process without window if supported - bool no_window; - // Search for program names in the PATH variable. Always enabled on Windows. - // Note: this will **not** search for paths in any provided custom environment - // and instead uses the PATH of the spawning process. - bool search_user_path; -} - -fn void! create_named_pipe_helper(void** rd, void **wr) @local @if(env::WIN32) -{ - Win32_SECURITY_ATTRIBUTES sa_attr = { Win32_SECURITY_ATTRIBUTES.sizeof, null, 1 }; - - tlocal long index = 0; - long unique = index++; - @pool() - { - String s = string::tformat(`\\.\pipe\c3_subprocess.%08x.%08x.%d`, win32::getCurrentProcessId(), win32::getCurrentThreadId(), unique); - Win32_LPCSTR str = (Win32_LPCSTR)s.ptr; - *rd = win32::createNamedPipeA( - str, - win32::PIPE_ACCESS_INBOUND | win32::FILE_FLAG_OVERLAPPED, - win32::PIPE_TYPE_BYTE | win32::PIPE_WAIT, - 1, 4096, 4096, 0, &sa_attr); - if (win32::INVALID_HANDLE_VALUE == *rd) return SubProcessResult.FAILED_TO_CREATE_PIPE?; - *wr = win32::createFileA( - str, win32::GENERIC_WRITE, 0, &sa_attr, - win32::OPEN_EXISTING, win32::FILE_ATTRIBUTE_NORMAL, null); - if (win32::INVALID_HANDLE_VALUE == *wr) return SubProcessResult.FAILED_TO_CREATE_PIPE?; - }; -} - -fn WString convert_command_line_win32(String[] command_line) @inline @if(env::WIN32) @local -{ - DString str = dstring::temp_with_capacity(512); - foreach LINE: (i, s : command_line) - { - if (i != 0) str.append(' '); - - do CHECK_WS: - { - foreach (c : s) - { - switch (c) - { - case '\t': - case ' ': - case '\v': - break CHECK_WS; - } - } - str.append(s); - continue LINE; - }; - str.append('"'); - foreach (j, c : s) - { - switch (c) - { - case '\\': - if (j != s.len - 1 && s[j + 1] == '"') str.append('\\'); - case '"': - str.append('\\'); - } - str.append(c); - } - str.append('"'); - } - str.append('\0'); - return str.str_view().to_wstring_tcopy()!!; -} - -<* - @require !environment || !options.inherit_environment -*> -fn SubProcess! create(String[] command_line, SubProcessOptions options = {}, String[] environment = {}) @if(env::WIN32) -{ - void* rd, wr; - Win32_DWORD flags = win32::CREATE_UNICODE_ENVIRONMENT; - Win32_PROCESS_INFORMATION process_info; - Win32_SECURITY_ATTRIBUTES sa_attr = { Win32_SECURITY_ATTRIBUTES.sizeof, null, 1 }; - Win32_STARTUPINFOW start_info = { - .cb = Win32_STARTUPINFOW.sizeof, - .dwFlags = win32::STARTF_USESTDHANDLES - }; - if (options.no_window) flags |= win32::CREATE_NO_WINDOW; - if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return SubProcessResult.FAILED_TO_CREATE_PIPE?; - // TODO defer catch - if (!win32::setHandleInformation(wr, win32::HANDLE_FLAG_INHERIT, 0)) return SubProcessResult.FAILED_TO_CREATE_PIPE?; - - CFile stdin; - CFile stdout; - CFile stderr; - @pool() - { - WString used_environment = null; - if (!options.inherit_environment) - { - DString env = dstring::temp_with_capacity(64); - if (!environment.len) - { - env.append("\0"); - } - foreach (String s : environment) - { - env.append(s); - env.append("\0"); - } - env.append("\0"); - used_environment = env.str_view().to_wstring_tcopy()!; - } - int fd = win32::_open_osfhandle((iptr)wr, 0); - if (fd != -1) - { - stdin = win32::_fdopen(fd, "wb"); - if (!stdin) return SubProcessResult.FAILED_TO_OPEN_STDIN?; - } - start_info.hStdInput = rd; - if (options.read_async) - { - create_named_pipe_helper(&rd, &wr)!; - } - else - { - if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return SubProcessResult.FAILED_TO_CREATE_PIPE?; - } - if (!win32::setHandleInformation(rd, win32::HANDLE_FLAG_INHERIT, 0)) return SubProcessResult.FAILED_TO_CREATE_PIPE?; - fd = win32::_open_osfhandle((iptr)rd, 0); - if (fd != -1) - { - stdout = win32::_fdopen(fd, "rb"); - if (!stdout) return SubProcessResult.FAILED_TO_OPEN_STDOUT?; - } - - start_info.hStdOutput = wr; - - do - { - if (options.combined_stdout_stderr) - { - stderr = stdout; - start_info.hStdError = start_info.hStdOutput; - break; - } - if (options.read_async) - { - create_named_pipe_helper(&rd, &wr)!; - } - else - { - if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return SubProcessResult.FAILED_TO_CREATE_PIPE?; - } - if (!win32::setHandleInformation(rd, win32::HANDLE_FLAG_INHERIT, 0)) return SubProcessResult.FAILED_TO_CREATE_PIPE?; - - fd = win32::_open_osfhandle((iptr)rd, 0); - if (fd != -1) - { - stderr = win32::_fdopen(fd, "rb"); - if (!stderr) return SubProcessResult.FAILED_TO_OPEN_STDERR?; - } - start_info.hStdError = wr; - }; - void *event_output; - void *event_error; - if (options.read_async) - { - event_output = win32::createEventA(&sa_attr, 1, 1, null); - event_error = win32::createEventA(&sa_attr, 1, 1, null); - } - if (!win32::createProcessW( - null, - convert_command_line_win32(command_line), - null, // process security attributes - null, // primary thread security attributes - 1, // handles are inherited - flags, // creation flags - used_environment, // environment - null, // use parent dir - &start_info, // startup info ptr - &process_info)) return SubProcessResult.FAILED_TO_START_PROCESS?; - }; - // We don't need the handle of the primary thread in the called process. - win32::closeHandle(process_info.hThread); - if (start_info.hStdOutput) - { - win32::closeHandle(start_info.hStdOutput); - if (start_info.hStdOutput != start_info.hStdError) win32::closeHandle(start_info.hStdError); - } - - return { - .hProcess = process_info.hProcess, - .hStdInput = start_info.hStdInput, - .stdin_file = stdin, - .stdout_file = stdout, - .stderr_file = stderr, - .is_alive = true, - }; -} - -<* - @require command_line.len > 0 -*> -fn ZString* tcopy_command_line(String[] command_line) @local @inline @if(env::POSIX) -{ - ZString* copy = mem::temp_alloc_array(ZString, command_line.len + 1); - foreach (i, str : command_line) - { - copy[i] = str.zstr_tcopy(); - } - copy[command_line.len] = null; - return copy; -} - -const ZString[1] EMPTY_ENVIRONMENT @if(env::POSIX) = { null }; -fn ZString* tcopy_env(String[] environment) @local @inline @if(env::POSIX) -{ - if (!environment.len) return &EMPTY_ENVIRONMENT; - ZString* copy = mem::temp_alloc_array(ZString, environment.len + 1); - copy[environment.len] = null; - foreach (i, str : environment) - { - copy[i] = str.zstr_tcopy(); - } - return copy; -} - -fn String! execute_stdout_to_buffer(char[] buffer, String[] command_line, SubProcessOptions options = {}, String[] environment = {}) -{ - SubProcess process = process::create(command_line, options, environment)!; - process.join()!; - usz len = process.read_stdout(buffer.ptr, buffer.len)!; - return (String)buffer[:len - 1]; -} - -<* - @require !environment || !options.inherit_environment -*> -fn SubProcess! create(String[] command_line, SubProcessOptions options = {}, String[] environment = {}) @if(env::POSIX) -{ - CInt[2] stdinfd; - CInt[2] stdoutfd; - CInt[2] stderrfd; - - if (posix::pipe(&stdinfd)) return SubProcessResult.FAILED_TO_OPEN_STDIN?; - if (posix::pipe(&stdoutfd)) return SubProcessResult.FAILED_TO_OPEN_STDOUT?; - if (!options.combined_stdout_stderr && posix::pipe(&stderrfd)) return SubProcessResult.FAILED_TO_OPEN_STDERR?; - - Posix_spawn_file_actions_t actions; - if (posix::spawn_file_actions_init(&actions)) return SubProcessResult.FAILED_TO_INITIALIZE_ACTIONS?; - defer posix::spawn_file_actions_destroy(&actions); - if (posix::spawn_file_actions_addclose(&actions, stdinfd[1])) return SubProcessResult.FAILED_TO_OPEN_STDIN?; - if (posix::spawn_file_actions_adddup2(&actions, stdinfd[0], libc::STDIN_FD)) return SubProcessResult.FAILED_TO_OPEN_STDIN?; - if (posix::spawn_file_actions_addclose(&actions, stdoutfd[0])) return SubProcessResult.FAILED_TO_OPEN_STDOUT?; - if (posix::spawn_file_actions_adddup2(&actions, stdoutfd[1], libc::STDOUT_FD)) return SubProcessResult.FAILED_TO_OPEN_STDOUT?; - if (options.combined_stdout_stderr) - { - if (posix::spawn_file_actions_adddup2(&actions, libc::STDOUT_FD, libc::STDERR_FD)) return SubProcessResult.FAILED_TO_OPEN_STDERR?; - } - else - { - if (posix::spawn_file_actions_addclose(&actions, stderrfd[0])) return SubProcessResult.FAILED_TO_OPEN_STDERR?; - if (posix::spawn_file_actions_adddup2(&actions, stderrfd[1], libc::STDERR_FD)) return SubProcessResult.FAILED_TO_OPEN_STDERR?; - } - Pid_t child; - @pool() - { - ZString* command_line_copy = tcopy_command_line(command_line); - ZString* used_environment = options.inherit_environment ? posix::environ : tcopy_env(environment); - if (options.search_user_path) - { - if (posix::spawnp(&child, command_line_copy[0], &actions, null, command_line_copy, used_environment)) return SubProcessResult.FAILED_TO_START_PROCESS?; - } - else - { - if (posix::spawnp(&child, command_line_copy[0], &actions, null, command_line_copy, used_environment)) return SubProcessResult.FAILED_TO_START_PROCESS?; - } - }; - libc::close(stdinfd[0]); - CFile stdin = libc::fdopen(stdinfd[1], "wb"); - libc::close(stdoutfd[1]); - CFile stdout = libc::fdopen(stdoutfd[0], "rb"); - CFile stderr @noinit; - do - { - if (options.combined_stdout_stderr) - { - stderr = stdout; - break; - } - libc::close(stderrfd[1]); - stderr = libc::fdopen(stderrfd[0], "rb"); - }; - return { - .stdin_file = stdin, - .stdout_file = stdout, - .stderr_file = stderr, - .child = child, - .is_alive = true, - }; -} - -fn CInt! SubProcess.join(&self) @if(env::POSIX) -{ - if (self.stdin_file) - { - libc::fclose(self.stdin_file); - self.stdin_file = null; - } - CInt status; - if (self.child && self.child != posix::waitpid(self.child, &status, 0)) return SubProcessResult.PROCESS_JOIN_FAILED?; - - self.child = 0; - self.is_alive = false; - - return self.return_status = posix::wIFEXITED(status) ? posix::wEXITSTATUS(status) : libc::EXIT_FAILURE; -} - -fn File SubProcess.stdout(&self) -{ - return file::from_handle(self.stdout_file); -} - -fn File SubProcess.stderr(&self) -{ - return file::from_handle(self.stderr_file); -} - -fn CInt! SubProcess.join(&self) @if(env::WIN32) -{ - if (self.stdin_file) - { - libc::fclose(self.stdin_file); - self.stdin_file = null; - } - if (self.hStdInput) - { - win32::closeHandle(self.hStdInput); - self.hStdInput = null; - } - win32::waitForSingleObject(self.hProcess, win32::INFINITE); - Win32_DWORD return_code @noinit; - if (!win32::getExitCodeProcess(self.hProcess, &return_code)) return SubProcessResult.PROCESS_JOIN_FAILED?; - self.is_alive = false; - return return_code; -} - -fn bool SubProcess.destroy(&self) -{ - if (self.stdin_file) libc::fclose(self.stdin_file); - if (self.stdout_file) - { - libc::fclose(self.stdout_file); - if (self.stdout_file != self.stderr_file) libc::fclose(self.stderr_file); - } - self.stdin_file = self.stdout_file = self.stderr_file = null; - $if env::WIN32: - if (self.hProcess) win32::closeHandle(self.hProcess); - if (self.hStdInput) win32::closeHandle(self.hStdInput); - if (self.hEventOutput) win32::closeHandle(self.hEventOutput); - if (self.hEventError) win32::closeHandle(self.hEventError); - self.hProcess = self.hStdInput = self.hEventOutput = self.hEventError = null; - $endif; - return true; -} - -fn void! SubProcess.terminate(&self) -{ - $if env::WIN32: - if (!win32::terminateProcess(self.hProcess, 99)) return SubProcessResult.PROCESS_TERMINATION_FAILED?; - $else - if (posix::kill(self.child, 9)) return SubProcessResult.PROCESS_TERMINATION_FAILED?; - $endif -} - -<* - @require size <= Win32_DWORD.max -*> -fn usz! read_from_file_win32(CFile file, Win32_HANDLE event_handle, char* buffer, usz size) @if(env::WIN32) @local -{ - CInt fd = libc::fileno(file); - Win32_DWORD bytes_read = 0; - Win32_OVERLAPPED overlapped = { .hEvent = event_handle }; - Win32_HANDLE handle = (Win32_HANDLE)win32::_get_osfhandle(fd); - if (!win32::readFile(handle, buffer, (Win32_DWORD)size, &bytes_read, &overlapped)) - { - // Means we've got an async read! - if (win32::getLastError() == win32::ERROR_IO_PENDING) - { - if (!win32::getOverlappedResult(handle, &overlapped, &bytes_read, 1)) - { - switch (win32::getLastError()) - { - case win32::ERROR_IO_INCOMPLETE: - case win32::ERROR_HANDLE_EOF: - break; - default: - return SubProcessResult.READ_FAILED?; - } - } - } - } - return bytes_read; -} -fn usz! read_from_file_posix(CFile file, char* buffer, usz size) @if(env::POSIX) @local -{ - isz bytes_read = libc::read(libc::fileno(file), buffer, size); - if (bytes_read < 0) return SubProcessResult.READ_FAILED?; - return bytes_read; -} - -fn usz! SubProcess.read_stdout(&self, char* buffer, usz size) -{ - $if env::WIN32: - return read_from_file_win32(self.stdout_file, self.hEventOutput, buffer, size); - $else - return read_from_file_posix(self.stdout_file, buffer, size); - $endif -} - -fn usz! SubProcess.read_stderr(&self, char* buffer, usz size) -{ - $if env::WIN32: - return read_from_file_win32(self.stderr_file, self.hEventError, buffer, size); - $else - return read_from_file_posix(self.stderr_file, buffer, size); - $endif -} - -fn bool! SubProcess.is_running(&self) -{ - if (!self.is_alive) return false; - $if env::WIN32: - bool is_alive = win32::waitForSingleObject(self.hProcess, 0) != win32::WAIT_OBJECT_0; - if (!is_alive) self.is_alive = false; - return is_alive; - $else - CInt status; - bool is_alive = posix::waitpid(self.child, &status, posix::WNOHANG) == 0; - if (is_alive) return true; - self.is_alive = false; - self.return_status = posix::wIFEXITED(status) ? posix::wEXITSTATUS(status) : libc::EXIT_FAILURE; - self.child = 0; - self.join()!; - return false; - $endif -} \ No newline at end of file diff --git a/lib7/std/os/win32/files.c3 b/lib7/std/os/win32/files.c3 deleted file mode 100644 index d78789afd..000000000 --- a/lib7/std/os/win32/files.c3 +++ /dev/null @@ -1,107 +0,0 @@ -module std::os::win32 @if(env::WIN32); -import libc; -enum Win32_GET_FILEEX_INFO_LEVELS -{ - STANDARD, - MAX, -} - -struct Win32_FILE_ATTRIBUTE_DATA -{ - Win32_DWORD dwFileAttributes; - Win32_FILETIME ftCreationTime; - Win32_FILETIME ftLastAccessTime; - Win32_FILETIME ftLastWriteTime; - Win32_DWORD nFileSizeHigh; - Win32_DWORD nFileSizeLow; -} - -const MAX_PATH = 260; - -struct Win32_WIN32_FIND_DATAW -{ - Win32_DWORD dwFileAttributes; - Win32_FILETIME ftCreationTime; - Win32_FILETIME ftLastAccessTime; - Win32_FILETIME ftLastWriteTime; - Win32_DWORD nFileSizeHigh; - Win32_DWORD nFileSizeLow; - Win32_DWORD dwReserved0; - Win32_DWORD dwReserved1; - Win32_WCHAR[260] cFileName; - Win32_WCHAR[14] cAlternateFileName; - Win32_DWORD dwFileType; // Obsolete. Do not use. - Win32_DWORD dwCreatorType; // Obsolete. Do not use - Win32_WORD wFinderFlags; // Obsolete. Do not use -} - -def Win32_LPWIN32_FIND_DATAW = Win32_WIN32_FIND_DATAW*; - -extern fn Win32_BOOL closeHandle(Win32_HANDLE) @extern("CloseHandle"); -extern fn Win32_BOOL createPipe(Win32_PHANDLE hReadPipe, Win32_PHANDLE hWritePipe, Win32_LPSECURITY_ATTRIBUTES lpPipeAttributes, Win32_DWORD nSize) @extern("CreatePipe"); -extern fn Win32_BOOL getFileAttributesExW(Win32_LPCWSTR, Win32_GET_FILEEX_INFO_LEVELS, Win32_LPVOID) @extern("GetFileAttributesExW"); -extern fn Win32_BOOL pathFileExistsW(Win32_LPCWSTR) @extern("PathFileExistsW"); -extern fn Win32_DWORD getTempPathW(Win32_DWORD nBufferLength, Win32_LPWSTR lpBuffer) @extern("GetTempPathW"); -extern fn Win32_BOOL setCurrentDirectoryW(Win32_LPCTSTR buffer) @extern("SetCurrentDirectoryW"); -extern fn Win32_BOOL removeDirectoryW(Win32_LPCWSTR lpPathName) @extern("RemoveDirectoryW"); -extern fn Win32_BOOL createDirectoryW(Win32_LPCWSTR lpPathName, Win32_LPSECURITY_ATTRIBUTES lpPipeAttributes) @extern("CreateDirectoryW"); -extern fn Win32_BOOL deleteFileW(Win32_LPCWSTR lpFileName) @extern("DeleteFileW"); -extern fn Win32_HANDLE findFirstFileW(Win32_LPCWSTR lpFileName, Win32_LPWIN32_FIND_DATAW lpFindFileData) @extern("FindFirstFileW"); -extern fn Win32_BOOL findNextFileW(Win32_HANDLE hFindFile, Win32_LPWIN32_FIND_DATAW lpFindFileData) @extern("FindNextFileW"); -extern fn Win32_BOOL findClose(Win32_HANDLE hFindFile) @extern("FindClose"); - -const Win32_DWORD GENERIC_WRITE = 0x40000000; -const Win32_DWORD OPEN_EXISTING = 3; -const Win32_DWORD FILE_ATTRIBUTE_READONLY = 0x01; -const Win32_DWORD FILE_ATTRIBUTE_HIDDEN = 0x02; -const Win32_DWORD FILE_ATTRIBUTE_SYSTEM = 0x04; -const Win32_DWORD FILE_ATTRIBUTE_DIRECTORY = 0x10; -const Win32_DWORD FILE_ATTRIBUTE_ARCHIVE = 0x20; -const Win32_DWORD FILE_ATTRIBUTE_DEVICE = 0x40; -const Win32_DWORD FILE_ATTRIBUTE_NORMAL = 0x80; -const Win32_DWORD FILE_ATTRIBUTE_TEMPORARY = 0x100; -const Win32_DWORD FILE_ATTRIBUTE_SPARSE_FILE = 0x200; -const Win32_DWORD FILE_ATTRIBUTE_REPARSE_POINT = 0x400; -const Win32_DWORD FILE_ATTRIBUTE_COMPRESSED = 0x800; -const Win32_DWORD FILE_ATTRIBUTE_OFFLINE = 0x1000; -const Win32_DWORD FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000; -const Win32_DWORD FILE_ATTRIBUTE_ENCRYPTED = 0x4000; -const Win32_DWORD FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000; -const Win32_DWORD FILE_ATTRIBUTE_VIRTUAL = 0x10000; -const Win32_DWORD FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000; -const Win32_DWORD FILE_ATTRIBUTE_EA = 0x40000; -const Win32_DWORD FILE_ATTRIBUTE_PINNED = 0x80000; -const Win32_DWORD FILE_ATTRIBUTE_UNPINNED = 0x100000; -const Win32_DWORD FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000; -const Win32_DWORD FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000; - -extern fn Win32_HANDLE createFileA( - Win32_LPCSTR lpFileName, - Win32_DWORD dwDesiredAccess, - Win32_DWORD dwShareMode, - Win32_LPSECURITY_ATTRIBUTES lpSecurityAttributes, - Win32_DWORD dwCreationDisposition, - Win32_DWORD dwFlagsAndAttributes, - Win32_HANDLE hTemplateFile -) @extern("CreateFileA"); -extern fn Win32_BOOL readFile(Win32_HANDLE hFile, Win32_LPVOID lpBuffer, Win32_DWORD nNumberOfBytesToRead, - Win32_LPDWORD lpNumberOfBytesRead, Win32_LPOVERLAPPED lpOverlapped -) @extern("ReadFile"); - -extern fn WString _wgetcwd(Char16* buffer, int maxlen); -extern fn usz wcslen(WString str); - -extern fn int _open_osfhandle(iptr osfhandle, int flags); -extern fn iptr _get_osfhandle(int fd); -extern fn CFile _fdopen(int fd, ZString mode); -extern fn CInt _access(ZString path, CInt mode); -extern fn CInt _waccess(WString path, CInt mode); - -extern fn WString _wfullpath(WString absPath, WString relPath, usz maxLength); - -/* -extern ulong _win32_GetCurrentDirectoryW(ulong, Char16* buffer) @extern("GetCurrentDirectoryW"); -extern bool _win32_CreateSymbolicLinkW(WString symlink_file, WString target_file, ulong flags) @extern("CreateSymbolicLinkW"); -extern bool _win32_CopyFileW(WString from_file, WString to_file, bool no_overwrite) @extern("CopyFileW"); -extern ulong _win32_GetFullPathNameW(WString file_name, ulong buffer_len, Char16* buffer, WString* file_part) @extern("GetFullPathNameW"); -*/ diff --git a/lib7/std/os/win32/gdi.c3 b/lib7/std/os/win32/gdi.c3 deleted file mode 100644 index a141a7274..000000000 --- a/lib7/std/os/win32/gdi.c3 +++ /dev/null @@ -1,6 +0,0 @@ -module std::os::win32 @if(env::WIN32); - -extern fn Win32_HBRUSH createSolidBrush(Win32_COLORREF) @extern("CreateSolidBrush"); -extern fn Win32_COLORREF setTextColor(Win32_HDC, Win32_COLORREF) @extern("SetTextColor"); -extern fn CInt setBkMode(Win32_HDC, CInt) @extern("SetBkMode"); -extern fn Win32_BOOL textOut(Win32_HDC, CInt, CInt, Win32_LPCWSTR, CInt) @extern("TextOutW"); \ No newline at end of file diff --git a/lib7/std/os/win32/general.c3 b/lib7/std/os/win32/general.c3 deleted file mode 100644 index 9fd6b193c..000000000 --- a/lib7/std/os/win32/general.c3 +++ /dev/null @@ -1,225 +0,0 @@ -module std::os::win32 @if(env::WIN32); - -extern fn Win32_DWORD getLastError() @extern("GetLastError"); - -const Win32_DWORD ERROR_INVALID_FUNCTION = 0x1; -const Win32_DWORD ERROR_FILE_NOT_FOUND = 0x2; -const Win32_DWORD ERROR_PATH_NOT_FOUND = 0x3; -const Win32_DWORD ERROR_TOO_MANY_OPEN_FILES = 0x4; -const Win32_DWORD ERROR_ACCESS_DENIED = 0x5; -const Win32_DWORD ERROR_INVALID_HANDLE = 0x6; -const Win32_DWORD ERROR_ARENA_TRASHED = 0x7; -const Win32_DWORD ERROR_NOT_ENOUGH_MEMORY = 0x8; -const Win32_DWORD ERROR_INVALID_BLOCK = 0x9; -const Win32_DWORD ERROR_BAD_ENVIRONMENT = 0xA; -const Win32_DWORD ERROR_BAD_FORMAT = 0xB; -const Win32_DWORD ERROR_INVALID_ACCESS = 0xC; -const Win32_DWORD ERROR_INVALID_DATA = 0xD; -const Win32_DWORD ERROR_OUTOFMEMORY = 0xE; -const Win32_DWORD ERROR_INVALID_DRIVE = 0xF; -const Win32_DWORD ERROR_CURRENT_DIRECTORY = 0x10; -const Win32_DWORD ERROR_NOT_SAME_DEVICE = 0x11; -const Win32_DWORD ERROR_NO_MORE_FILES = 0x12; -const Win32_DWORD ERROR_WRITE_PROTECT = 0x13; -const Win32_DWORD ERROR_BAD_UNIT = 0x14; -const Win32_DWORD ERROR_NOT_READY = 0x15; -const Win32_DWORD ERROR_BAD_COMMAND = 0x16; -const Win32_DWORD ERROR_CRC = 0x17; -const Win32_DWORD ERROR_BAD_LENGTH = 0x18; -const Win32_DWORD ERROR_SEEK = 0x19; -const Win32_DWORD ERROR_NOT_DOS_DISK = 0x1A; -const Win32_DWORD ERROR_SECTOR_NOT_FOUND = 0x1B; -const Win32_DWORD ERROR_OUT_OF_PAPER = 0x1C; -const Win32_DWORD ERROR_WRITE_FAULT = 0x1D; -const Win32_DWORD ERROR_READ_FAULT = 0x1E; -const Win32_DWORD ERROR_GEN_FAILURE = 0x1F; -const Win32_DWORD ERROR_SHARING_VIOLATION = 0x20; -const Win32_DWORD ERROR_LOCK_VIOLATION = 0x21; -const Win32_DWORD ERROR_WRONG_DISK = 0x22; -const Win32_DWORD ERROR_SHARING_BUFFER_EXCEEDED = 0x24; -const Win32_DWORD ERROR_HANDLE_EOF = 0x26; -const Win32_DWORD ERROR_HANDLE_DISK_FULL = 0x27; -const Win32_DWORD ERROR_NOT_SUPPORTED = 0x32; -const Win32_DWORD ERROR_REM_NOT_LIST = 0x33; -const Win32_DWORD ERROR_DUP_NAME = 0x34; -const Win32_DWORD ERROR_BAD_NETPATH = 0x35; -const Win32_DWORD ERROR_NETWORK_BUSY = 0x36; -const Win32_DWORD ERROR_DEV_NOT_EXIST = 0x37; -const Win32_DWORD ERROR_TOO_MANY_CMDS = 0x38; -const Win32_DWORD ERROR_ADAP_HDW_ERR = 0x39; -const Win32_DWORD ERROR_BAD_NET_RESP = 0x3A; -const Win32_DWORD ERROR_UNEXP_NET_ERR = 0x3B; -const Win32_DWORD ERROR_BAD_REM_ADAP = 0x3C; -const Win32_DWORD ERROR_PRINTQ_FULL = 0x3D; -const Win32_DWORD ERROR_NO_SPOOL_SPACE = 0x3E; -const Win32_DWORD ERROR_PRINT_CANCELLED = 0x3F; -const Win32_DWORD ERROR_NETNAME_DELETED = 0x40; -const Win32_DWORD ERROR_NETWORK_ACCESS_DENIED = 0x41; -const Win32_DWORD ERROR_BAD_DEV_TYPE = 0x42; -const Win32_DWORD ERROR_BAD_NET_NAME = 0x43; -const Win32_DWORD ERROR_TOO_MANY_NAMES = 0x44; -const Win32_DWORD ERROR_TOO_MANY_SESS = 0x45; -const Win32_DWORD ERROR_SHARING_PAUSED = 0x46; -const Win32_DWORD ERROR_REQ_NOT_ACCEP = 0x47; -const Win32_DWORD ERROR_REDIR_PAUSED = 0x48; -const Win32_DWORD ERROR_FILE_EXISTS = 0x50; -const Win32_DWORD ERROR_CANNOT_MAKE = 0x52; -const Win32_DWORD ERROR_FAIL_I24 = 0x53; -const Win32_DWORD ERROR_OUT_OF_STRUCTURES = 0x54; -const Win32_DWORD ERROR_ALREADY_ASSIGNED = 0x55; -const Win32_DWORD ERROR_INVALID_PASSWORD = 0x56; -const Win32_DWORD ERROR_INVALID_PARAMETER = 0x57; -const Win32_DWORD ERROR_NET_WRITE_FAULT = 0x58; -const Win32_DWORD ERROR_NO_PROC_SLOTS = 0x59; -const Win32_DWORD ERROR_TOO_MANY_SEMAPHORES = 0x64; -const Win32_DWORD ERROR_EXCL_SEM_ALREADY_OWNED = 0x65; -const Win32_DWORD ERROR_SEM_IS_SET = 0x66; -const Win32_DWORD ERROR_TOO_MANY_SEM_REQUESTS = 0x67; -const Win32_DWORD ERROR_INVALID_AT_INTERRUPT_TIME = 0x68; -const Win32_DWORD ERROR_SEM_OWNER_DIED = 0x69; -const Win32_DWORD ERROR_SEM_USER_LIMIT = 0x6A; -const Win32_DWORD ERROR_DISK_CHANGE = 0x6B; -const Win32_DWORD ERROR_DRIVE_LOCKED = 0x6C; -const Win32_DWORD ERROR_BROKEN_PIPE = 0x6D; -const Win32_DWORD ERROR_OPEN_FAILED = 0x6E; -const Win32_DWORD ERROR_BUFFER_OVERFLOW = 0x6F; -const Win32_DWORD ERROR_DISK_FULL = 0x70; -const Win32_DWORD ERROR_NO_MORE_SEARCH_HANDLES = 0x71; -const Win32_DWORD ERROR_INVALID_TARGET_HANDLE = 0x72; -const Win32_DWORD ERROR_INVALID_CATEGORY = 0x75; -const Win32_DWORD ERROR_INVALID_VERIFY_SWITCH = 0x76; -const Win32_DWORD ERROR_BAD_DRIVER_LEVEL = 0x77; -const Win32_DWORD ERROR_CALL_NOT_IMPLEMENTED = 0x78; -const Win32_DWORD ERROR_SEM_TIMEOUT = 0x79; -const Win32_DWORD ERROR_INSUFFICIENT_BUFFER = 0x7A; -const Win32_DWORD ERROR_INVALID_NAME = 0x7B; -const Win32_DWORD ERROR_INVALID_LEVEL = 0x7C; -const Win32_DWORD ERROR_NO_VOLUME_LABEL = 0x7D; -const Win32_DWORD ERROR_MOD_NOT_FOUND = 0x7E; -const Win32_DWORD ERROR_PROC_NOT_FOUND = 0x7F; -const Win32_DWORD ERROR_WAIT_NO_CHILDREN = 0x80; -const Win32_DWORD ERROR_CHILD_NOT_COMPLETE = 0x81; -const Win32_DWORD ERROR_DIRECT_ACCESS_HANDLE = 0x82; -const Win32_DWORD ERROR_NEGATIVE_SEEK = 0x83; -const Win32_DWORD ERROR_SEEK_ON_DEVICE = 0x84; -const Win32_DWORD ERROR_IS_JOIN_TARGET = 0x85; -const Win32_DWORD ERROR_IS_JOINED = 0x86; -const Win32_DWORD ERROR_IS_SUBSTED = 0x87; -const Win32_DWORD ERROR_NOT_JOINED = 0x88; -const Win32_DWORD ERROR_NOT_SUBSTED = 0x89; -const Win32_DWORD ERROR_JOIN_TO_JOIN = 0x8A; -const Win32_DWORD ERROR_SUBST_TO_SUBST = 0x8B; -const Win32_DWORD ERROR_JOIN_TO_SUBST = 0x8C; -const Win32_DWORD ERROR_SUBST_TO_JOIN = 0x8D; -const Win32_DWORD ERROR_BUSY_DRIVE = 0x8E; -const Win32_DWORD ERROR_SAME_DRIVE = 0x8F; -const Win32_DWORD ERROR_DIR_NOT_ROOT = 0x90; -const Win32_DWORD ERROR_DIR_NOT_EMPTY = 0x91; -const Win32_DWORD ERROR_IS_SUBST_PATH = 0x92; -const Win32_DWORD ERROR_IS_JOIN_PATH = 0x93; -const Win32_DWORD ERROR_PATH_BUSY = 0x94; -const Win32_DWORD ERROR_IS_SUBST_TARGET = 0x95; -const Win32_DWORD ERROR_SYSTEM_TRACE = 0x96; -const Win32_DWORD ERROR_INVALID_EVENT_COUNT = 0x97; -const Win32_DWORD ERROR_TOO_MANY_MUXWAITERS = 0x98; -const Win32_DWORD ERROR_INVALID_LIST_FORMAT = 0x99; -const Win32_DWORD ERROR_LABEL_TOO_LONG = 0x9A; -const Win32_DWORD ERROR_TOO_MANY_TCBS = 0x9B; -const Win32_DWORD ERROR_SIGNAL_REFUSED = 0x9C; -const Win32_DWORD ERROR_DISCARDED = 0x9D; -const Win32_DWORD ERROR_NOT_LOCKED = 0x9E; -const Win32_DWORD ERROR_BAD_THREADID_ADDR = 0x9F; -const Win32_DWORD ERROR_BAD_ARGUMENTS = 0xA0; -const Win32_DWORD ERROR_BAD_PATHNAME = 0xA1; -const Win32_DWORD ERROR_SIGNAL_PENDING = 0xA2; -const Win32_DWORD ERROR_MAX_THRDS_REACHED = 0xA4; -const Win32_DWORD ERROR_LOCK_FAILED = 0xA7; -const Win32_DWORD ERROR_BUSY = 0xAA; -const Win32_DWORD ERROR_DEVICE_SUPPORT_IN_PROGRESS = 0xAB; -const Win32_DWORD ERROR_CANCEL_VIOLATION = 0xAD; -const Win32_DWORD ERROR_ATOMIC_LOCKS_NOT_SUPPORTED = 0xAE; -const Win32_DWORD ERROR_INVALID_SEGMENT_NUMBER = 0xB4; -const Win32_DWORD ERROR_INVALID_ORDINAL = 0xB6; -const Win32_DWORD ERROR_ALREADY_EXISTS = 0xB7; -const Win32_DWORD ERROR_INVALID_FLAG_NUMBER = 0xBA; -const Win32_DWORD ERROR_SEM_NOT_FOUND = 0xBB; -const Win32_DWORD ERROR_INVALID_STARTING_CODESEG = 0xBC; -const Win32_DWORD ERROR_INVALID_STACKSEG = 0xBD; -const Win32_DWORD ERROR_INVALID_MODULETYPE = 0xBE; -const Win32_DWORD ERROR_INVALID_EXE_SIGNATURE = 0xBF; - -const Win32_DWORD ERROR_EXE_MARKED_INVALID = 0xC0; -const Win32_DWORD ERROR_BAD_EXE_FORMAT = 0xC1; -const Win32_DWORD ERROR_ITERATED_DATA_EXCEEDS_64K = 0xC2; -const Win32_DWORD ERROR_INVALID_MINALLOCSIZE = 0xC3; -const Win32_DWORD ERROR_DYNLINK_FROM_INVALID_RING = 0xC4; -const Win32_DWORD ERROR_IOPL_NOT_ENABLED = 0xC5; -const Win32_DWORD ERROR_INVALID_SEGDPL = 0xC6; -const Win32_DWORD ERROR_AUTODATASEG_EXCEEDS_64K = 0xC7; -const Win32_DWORD ERROR_RING2SEG_MUST_BE_MOVABLE = 0xC8; -const Win32_DWORD ERROR_RELOC_CHAIN_XEEDS_SEGLIM = 0xC9; -const Win32_DWORD ERROR_INFLOOP_IN_RELOC_CHAIN = 0xCA; -const Win32_DWORD ERROR_ENVVAR_NOT_FOUND = 0xCB; -const Win32_DWORD ERROR_NO_SIGNAL_SENT = 0xCD; -const Win32_DWORD ERROR_FILENAME_EXCED_RANGE = 0xCE; -const Win32_DWORD ERROR_RING2_STACK_IN_USE = 0xCF; -const Win32_DWORD ERROR_META_EXPANSION_TOO_LONG = 0xD0; -const Win32_DWORD ERROR_INVALID_SIGNAL_NUMBER = 0xD1; -const Win32_DWORD ERROR_THREAD_1_INACTIVE = 0xD2; -const Win32_DWORD ERROR_LOCKED = 0xD4; -const Win32_DWORD ERROR_TOO_MANY_MODULES = 0xD6; -const Win32_DWORD ERROR_NESTING_NOT_ALLOWED = 0xD7; -const Win32_DWORD ERROR_EXE_MACHINE_TYPE_MISMATCH = 0xD8; -const Win32_DWORD ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY = 0xD9; -const Win32_DWORD ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY = 0xDA; -const Win32_DWORD ERROR_FILE_CHECKED_OUT = 0xDC; -const Win32_DWORD ERROR_CHECKOUT_REQUIRED = 0xDD; -const Win32_DWORD ERROR_BAD_FILE_TYPE = 0xDE; -const Win32_DWORD ERROR_FILE_TOO_LARGE = 0xDF; -const Win32_DWORD ERROR_FORMS_AUTH_REQUIRED = 0xE0; -const Win32_DWORD ERROR_VIRUS_INFECTED = 0xE1; -const Win32_DWORD ERROR_VIRUS_DELETED = 0xE2; -const Win32_DWORD ERROR_PIPE_LOCAL = 0xE5; -const Win32_DWORD ERROR_BAD_PIPE = 0xE6; -const Win32_DWORD ERROR_PIPE_BUSY = 0xE7; -const Win32_DWORD ERROR_NO_DATA = 0xE8; -const Win32_DWORD ERROR_PIPE_NOT_CONNECTED = 0xE9; -const Win32_DWORD ERROR_MORE_DATA = 0xEA; -const Win32_DWORD ERROR_VC_DISCONNECTED = 0xF0; -const Win32_DWORD ERROR_INVALID_EA_NAME = 0xFE; -const Win32_DWORD ERROR_EA_LIST_INCONSISTENT = 0xFF; -const Win32_DWORD WAIT_TIMEOUT = 0x102; -const Win32_DWORD ERROR_NO_MORE_ITEMS = 0x103; -const Win32_DWORD ERROR_CANNOT_COPY = 0x10A; -const Win32_DWORD ERROR_DIRECTORY = 0x10B; - -const Win32_DWORD ERROR_EAS_DIDNT_FIT = 0x113; -const Win32_DWORD ERROR_EA_FILE_CORRUPT = 0x114; -const Win32_DWORD ERROR_EA_TABLE_FULL = 0x115; -const Win32_DWORD ERROR_INVALID_EA_HANDLE = 0x116; -const Win32_DWORD ERROR_EAS_NOT_SUPPORTED = 0x11A; -const Win32_DWORD ERROR_NOT_OWNER = 0x120; -const Win32_DWORD ERROR_TOO_MANY_POSTS = 0x12A; -const Win32_DWORD ERROR_PARTIAL_COPY = 0x12A; -const Win32_DWORD ERROR_OPLOCK_NOT_GRANTED = 0x12C; -const Win32_DWORD ERROR_INVALID_OPLOCK_PROTOCOL = 0x12D; -const Win32_DWORD ERROR_DISK_TOO_FRAGMENTED = 0x12E; -const Win32_DWORD ERROR_DELETE_PENDING = 0x12F; -const Win32_DWORD ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING = 0x130; -const Win32_DWORD ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME = 0x131; -const Win32_DWORD ERROR_SECURITY_STREAM_IS_INCONSISTENT = 0x132; -const Win32_DWORD ERROR_INVALID_LOCK_RANGE = 0x133; -const Win32_DWORD ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT = 0x134; -const Win32_DWORD ERROR_NOTIFICATION_GUID_ALREADY_DEFINED = 0x135; -const Win32_DWORD ERROR_INVALID_EXCEPTION_HANDLER = 0x136; -const Win32_DWORD ERROR_DUPLICATE_PRIVILEGES = 0x137; -const Win32_DWORD ERROR_NO_RANGES_PROCESSED = 0x138; -const Win32_DWORD ERROR_NOT_ALLOWED_ON_SYSTEM_FILE = 0x139; -const Win32_DWORD ERROR_DISK_RESOURCES_EXHAUSTED = 0x13A; -const Win32_DWORD ERROR_INVALID_TOKEN = 0x13B; -const Win32_DWORD ERROR_DEVICE_FEATURE_NOT_SUPPORTED = 0x13C; -const Win32_DWORD ERROR_MR_MID_NOT_FOUND = 0x13D; -const Win32_DWORD ERROR_SCOPE_NOT_FOUND = 0x13E; -const Win32_DWORD ERROR_UNDEFINED_SCOPE = 0x13F; -const Win32_DWORD ERROR_IO_INCOMPLETE = 0x3E4; -const Win32_DWORD ERROR_IO_PENDING = 0x3E5; \ No newline at end of file diff --git a/lib7/std/os/win32/heap.c3 b/lib7/std/os/win32/heap.c3 deleted file mode 100644 index 16c13a9b3..000000000 --- a/lib7/std/os/win32/heap.c3 +++ /dev/null @@ -1,11 +0,0 @@ -module std::os::win32 @if(env::WIN32); - -extern fn void* _aligned_malloc(usz size, usz alignment); -extern fn void* _aligned_realloc(void* memblock, usz size, usz alignment); -extern fn void* _aligned_recalloc(void* memblock, usz num, usz size, usz alignment); -extern fn void _aligned_free(void* memblock); -extern fn void _aligned_msize(void* memblock, usz alignment, usz offset); -extern fn void* _aligned_offset_malloc(usz size, usz alignment, usz offset); -extern fn void* _aligned_offset_realloc(void* memblock, usz size, usz alignment, usz offset); -extern fn void* _aligned_offset_recalloc(void* memblock, usz num, usz size, usz alignment, usz offset); -extern fn usz _msize(void* memblock); \ No newline at end of file diff --git a/lib7/std/os/win32/process.c3 b/lib7/std/os/win32/process.c3 deleted file mode 100644 index 05fe742ea..000000000 --- a/lib7/std/os/win32/process.c3 +++ /dev/null @@ -1,202 +0,0 @@ -module std::os::win32 @if(env::WIN32); -import std::thread, std::os::backtrace; - -const Win32_DWORD STARTF_USESTDHANDLES = 0x00000100; -const Win32_DWORD CREATE_NO_WINDOW = 0x08000000; -const Win32_DWORD CREATE_PROTECTED_PROCESS = 0x00040000; -const Win32_DWORD CREATE_UNICODE_ENVIRONMENT = 0x00000400; -const uint WAIT_OBJECT_0 = 0; -const uint WAIT_ABANDONED = 128; -const uint WAIT_IO_COMPLETION = 192; -const uint WAIT_FAILED = (uint)-1; -const Win32_DWORD HANDLE_FLAG_INHERIT = 1; -const Win32_DWORD HANDLE_FLAG_PROTECT_FROM_CLOSE = 2; -const uint INFINITE = (uint)-1; -const Win32_DWORD PIPE_ACCESS_DUPLEX = 0x00000003; -const Win32_DWORD PIPE_ACCESS_INBOUND = 0x00000001; -const Win32_DWORD PIPE_ACCESS_OUTBOUND = 0x00000002; -const Win32_DWORD FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000; -const Win32_DWORD FILE_FLAG_WRITE_THROUGH = 0x80000000; -const Win32_DWORD FILE_FLAG_OVERLAPPED = 0x40000000; -const Win32_DWORD WRITE_DAC = 0x00040000; -const Win32_DWORD WRITE_OWNER = 0x00080000; -const Win32_DWORD ACCESS_SYSTEM_SECURITY = 0x01000000; -const Win32_DWORD PIPE_TYPE_BYTE = 0; -const Win32_DWORD PIPE_TYPE_MESSAGE = 4; -const Win32_DWORD PIPE_READMODE_BYTE = 0; -const Win32_DWORD PIPE_READMODE_MESSAGE = 2; -const Win32_DWORD PIPE_WAIT = 0; -const Win32_DWORD PIPE_NOWAIT = 1; -const Win32_DWORD PIPE_ACCEPT_REMOTE_CLIENTS = 0; -const Win32_DWORD PIPE_REJECT_REMOTE_CLIENTS = 8; -const SYMOPT_CASE_INSENSITIVE = 0x01; -const SYMOPT_UNDNAME = 0x02; -const SYMOPT_DEFERRED_LOADS = 0x04; -const SYMOPT_NO_CPP = 0x08; -const SYMOPT_LOAD_LINES = 0x10; -const SYMOPT_OMAP_FIND_NEAREST = 0x20; -const SYMOPT_LOAD_ANYTHING = 0x40; -const SYMOPT_IGNORE_CVREC = 0x80; - -const IMAGE_FILE_MACHINE_UNKNOWN = 0; -const IMAGE_FILE_MACHINE_TARGET_HOST = 1; -const IMAGE_FILE_MACHINE_I386 = 0x014c; -const IMAGE_FILE_MACHINE_IA64 = 0x0200; -const IMAGE_FILE_MACHINE_ARM64 = 0xAA64; -const IMAGE_FILE_MACHINE_AMD64 = 0x8664; -const UNDNAME_COMPLETE = 0x0000; - -extern fn void initializeCriticalSection(Win32_CRITICAL_SECTION* section) @extern("InitializeCriticalSection"); -extern fn void deleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection"); -extern fn Win32_HANDLE createMutex(void*, Win32_BOOL, void*) @extern("CreateMutexA"); -extern fn Win32_BOOL releaseMutex(Win32_HANDLE) @extern("ReleaseMutex"); -extern fn void enterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("EnterCriticalSection"); -extern fn void leaveCriticalSection(Win32_CRITICAL_SECTION* section) @extern("LeaveCriticalSection"); -extern fn Win32_BOOL tryEnterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("TryEnterCriticalSection"); -extern fn Win32_DWORD waitForSingleObject(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds) @extern("WaitForSingleObject"); -extern fn Win32_DWORD waitForSingleObjectEx(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds, Win32_BOOL bAlertable) @extern("WaitForSingleObjectEx"); -extern fn Win32_DWORD waitForMultipleObjects(Win32_DWORD nCount, Win32_HANDLE* lpHandles, Win32_BOOL bWaitAll, Win32_DWORD dwMilliseconds) @extern("WaitForMultipleObjects"); -extern fn Win32_DWORD waitForMultipleObjectsEx(Win32_DWORD nCount, Win32_HANDLE* lpHandles, Win32_BOOL bWaitAll, Win32_DWORD dwMilliseconds, Win32_BOOL bAlertable) @extern("WaitForMultipleObjectsEx"); -extern fn void sleep(uint ms) @extern("Sleep"); -extern fn Win32_BOOL resetEvent(Win32_HANDLE event) @extern("ResetEvent"); -extern fn Win32_BOOL setEvent(Win32_HANDLE handle) @extern("SetEvent"); -extern fn long interlockedCompareExchange(int* dest, int exchange, int comperand) @extern("InterlockedCompareExchange"); -extern fn Win32_DWORD sleepEx(Win32_DWORD ms, Win32_BOOL alertable) @extern("SleepEx"); -extern fn Win32_HANDLE createThread(void* attributes, usz stack, ThreadFn func, Win32_LPVOID arg, Win32_DWORD flags, Win32_LPDWORD thread_id) @extern("CreateThread"); -extern fn Win32_BOOL getExitCodeThread(Win32_HANDLE handle, Win32_LPDWORD exit_code) @extern("GetExitCodeThread"); -extern fn Win32_BOOL getExitCodeProcess(Win32_HANDLE hProcess, Win32_LPDWORD lpExitCode) @extern("GetExitCodeProcess"); -extern fn Win32_DWORD getThreadId(Win32_HANDLE) @extern("GetThreadId"); -extern fn void exitThread(Win32_DWORD dwExitCode) @noreturn @extern("ExitThread"); -extern fn Win32_HANDLE getCurrentThread() @extern("GetCurrentThread"); -extern fn Win32_BOOL terminateProcess(Win32_HANDLE hProcess, Win32_UINT uExitCode) @extern("TerminateProcess"); -extern fn Win32_DWORD getCurrentProcessId() @extern("GetCurrentProcessId"); -extern fn Win32_HANDLE getCurrentProcess() @extern("GetCurrentProcess"); -extern fn Win32_DWORD getCurrentThreadId() @extern("GetCurrentThreadId"); -extern fn Win32_BOOL setHandleInformation(Win32_HANDLE hObject, Win32_DWORD dwMask, Win32_DWORD dwFlags) @extern("SetHandleInformation"); -extern fn Win32_HANDLE createEventA(Win32_LPSECURITY_ATTRIBUTES lpEventAttributes, Win32_BOOL bManualReset, Win32_BOOL bInitialState, Win32_LPCSTR lpName) @extern("CreateEventA"); -extern fn Win32_BOOL createProcessW(Win32_LPCWSTR lpApplicationName, Win32_LPWSTR lpCommandLine, Win32_LPSECURITY_ATTRIBUTES lpProcessAttributes, Win32_LPSECURITY_ATTRIBUTES lpThreadAttributes, Win32_BOOL bInheritHandles, Win32_DWORD dwCreationFlags, Win32_LPVOID lpEnvironment, Win32_LPCWSTR lpCurrentDirectory, Win32_LPSTARTUPINFOW lpStartupInfo, Win32_LPPROCESS_INFORMATION lpProcessInformation) @extern("CreateProcessW"); -extern fn Win32_HANDLE createNamedPipeA(Win32_LPCSTR lpName, Win32_DWORD dwOpenMode, Win32_DWORD dwPipeMode, Win32_DWORD nMaxInstances, Win32_DWORD nOutBufferSize, Win32_DWORD nInBufferSize, Win32_DWORD nDefaultTimeOut, Win32_LPSECURITY_ATTRIBUTES lpSecurityAttributes) @extern("CreateNamedPipeA"); -extern fn Win32_BOOL getOverlappedResult(Win32_HANDLE hFile, Win32_LPOVERLAPPED lpOverlapped, Win32_LPDWORD lpNumberOfBytesTransferred, Win32_BOOL bWait) @extern("GetOverlappedResult"); -extern fn Win32_DWORD getEnvironmentVariableW(Win32_LPCWSTR lpName, Win32_LPWSTR lpBuffer, Win32_DWORD nSize) @extern("GetEnvironmentVariableW"); -extern fn Win32_BOOL setEnvironmentVariableW(Win32_LPCWSTR lpName, Win32_LPCWSTR lpValue) @extern("SetEnvironmentVariableW"); -extern fn void getSystemInfo(Win32_LPSYSTEM_INFO lpSystemInfo) @extern("GetSystemInfo"); -extern fn Win32_BOOL enumProcessModules(Win32_HANDLE hProcess, Win32_HMODULE* lphModule, Win32_DWORD cb, Win32_LPDWORD lpcbNeeded) @extern("K32EnumProcessModules"); -extern fn Win32_BOOL getModuleInformation(Win32_HANDLE hProcess, Win32_HMODULE hModule, Win32_LPMODULEINFO lpmodinfo, Win32_DWORD cb) @extern("K32GetModuleInformation"); -extern fn Win32_DWORD symAddrIncludeInlineTrace(Win32_HANDLE hProcess, Win32_DWORD64 address) @extern("SymAddrIncludeInlineTrace"); -extern fn Win32_BOOL symQueryInlineTrace(Win32_HANDLE hProcess, Win32_DWORD64 startAddress, Win32_DWORD startContext, Win32_DWORD64 startRetAddress, Win32_DWORD64 curAddress, Win32_LPDWORD curContext, Win32_LPDWORD curFrameIndex) @extern("SymQueryInlineTrace"); -extern fn Win32_BOOL symFromInlineContext(Win32_HANDLE hProcess, Win32_DWORD64 address, Win32_ULONG inlineContext, Win32_PDWORD64 displacement, Win32_PSYMBOL_INFO symbol) @extern("SymFromInlineContext"); -extern fn Win32_BOOL symGetLineFromInlineContext(Win32_HANDLE hProcess, Win32_DWORD64 qwAddr, Win32_ULONG inlineContext, Win32_DWORD64 qwModuleBaseAddress, Win32_PDWORD pdwDisplacement, Win32_PIMAGEHLP_LINE64 line64) @extern("SymGetLineFromInlineContext"); -extern fn Win32_ULONG rtlWalkFrameChain(Win32_PVOID*, Win32_ULONG, Win32_ULONG) @extern("RtlWalkFrameChain"); -extern fn Win32_BOOL symInitialize(Win32_HANDLE hProcess, Win32_PCSTR userSearchPath, Win32_BOOL fInvadeProcess) @extern("SymInitialize"); -extern fn Win32_BOOL symCleanup(Win32_HANDLE hProcess) @extern("SymCleanup"); -extern fn Win32_DWORD getModuleFileNameA(Win32_HMODULE hModule, Win32_LPSTR lpFilename, Win32_DWORD nSize) @extern("GetModuleFileNameA"); -extern fn Win32_DWORD getModuleFileNameExA(Win32_HANDLE hProcess, Win32_HMODULE hModule, Win32_LPSTR lpFilename, Win32_DWORD nSize) @extern("GetModuleFileNameExA"); -extern fn Win32_DWORD64 symLoadModuleEx(Win32_HANDLE hProcess, Win32_HANDLE hFile, Win32_PCSTR imageName, Win32_PCSTR moduleName, Win32_DWORD64 baseOfDll, Win32_DWORD dllSize, Win32_PMODLOAD_DATA data, Win32_DWORD flags) @extern("SymLoadModule"); -extern fn Win32_BOOL stackWalk64(Win32_DWORD machineType, Win32_HANDLE hProcess, Win32_HANDLE hThread, Win32_LPSTACKFRAME64 stackFrame, Win32_PVOID contextRecord, Win32_PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryRoutine, Win32_PFUNCTION_TABLE_ACCESS_ROUTINE64 functionTableAccessRoutine, Win32_PGET_MODULE_BASE_ROUTINE64 getModuleBaseRoutine, Win32_PTRANSLATE_ADDRESS_ROUTINE64 translateAddress) @extern("StackWalk64"); -extern fn void rtlCaptureContext(Win32_PCONTEXT contextRecord) @extern("RtlCaptureContext"); -extern fn void* symFunctionTableAccess64(Win32_HANDLE hProcess, Win32_DWORD64 addrBase) @extern("SymFunctionTableAccess64"); -extern fn Win32_DWORD64 symGetModuleBase64(Win32_HANDLE hProcess, Win32_DWORD64 qwAddr) @extern("SymGetModuleBase64"); -extern fn Win32_DWORD getModuleBaseNameA(Win32_HANDLE hProcess, Win32_HMODULE hModule, Win32_LPSTR lpBaseName, Win32_DWORD nSize) @extern("K32GetModuleBaseNameA"); -extern fn Win32_DWORD symGetOptions() @extern("SymGetOptions"); -extern fn Win32_DWORD symSetOptions(Win32_DWORD symOptions) @extern("SymSetOptions"); -extern fn Win32_PIMAGE_NT_HEADERS imageNtHeader(Win32_PVOID base) @extern("ImageNtHeader"); -extern fn Win32_DWORD unDecorateSymbolName(Win32_PCSTR name, Win32_PSTR outputString, Win32_DWORD maxStringLength, Win32_DWORD flags) @extern("UnDecorateSymbolName"); -extern fn Win32_BOOL symFromAddr(Win32_HANDLE hProcess, Win32_DWORD64 address, Win32_PDWORD64 displacement, Win32_PSYMBOL_INFO symbol) @extern("SymFromAddr"); -extern fn Win32_BOOL symGetLineFromAddr64(Win32_HANDLE hProcess, Win32_DWORD64 dwAddr, Win32_PDWORD pdwDisplacement, Win32_PIMAGEHLP_LINE64 line) @extern("SymGetLineFromAddr64"); -extern fn Win32_WORD rtlCaptureStackBackTrace(Win32_DWORD framesToSkip, Win32_DWORD framesToCapture, Win32_PVOID *backTrace, Win32_PDWORD backTraceHash) @extern("RtlCaptureStackBackTrace"); -extern fn Win32_BOOL symGetModuleInfo64(Win32_HANDLE hProcess, Win32_DWORD64 qwAddr, Win32_PIMAGEHLP_MODULE64 moduleInfo) @extern("SymGetModuleInfo64"); -extern fn Win32_HANDLE getModuleHandleA(Win32_LPCSTR lpModuleName) @extern("GetModuleHandleA"); -extern fn Win32_HANDLE getModuleHandleW(Win32_LPCWSTR lpModuleName) @extern("GetModuleHandleW"); - -fn Win32_DWORD! load_modules() -{ - Win32_HANDLE process = getCurrentProcess(); - Win32_DWORD needed; - symInitialize(getCurrentProcess(), null, 1); - Win32_DWORD symOptions = symGetOptions(); - symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME; - symSetOptions(symOptions); - Win32_HMODULE[1024] mod_buffer; - if (!enumProcessModules(process, &mod_buffer, mod_buffer.len, &needed)) - { - return BacktraceFault.RESOLUTION_FAILED?; - } - if (needed > mod_buffer.len) return BacktraceFault.RESOLUTION_FAILED?; - Win32_HMODULE[] modules = mod_buffer[:needed]; - void* base = null; - foreach (mod : modules) - { - Win32_MODULEINFO info; - if (!getModuleInformation(process, mod, &info, $sizeof(info))) - { - return BacktraceFault.RESOLUTION_FAILED?; - } - if (!base) base = info.lpBaseOfDll; - Win32_DWORD load_size = info.sizeOfImage; - char[1024] char_buf; - Win32_DWORD len = getModuleFileNameA(mod, (Win32_LPSTR)&char_buf, char_buf.len - 1); - if (len < 1) continue; - char[1024] module_name; - Win32_DWORD len2 = getModuleBaseNameA(process, mod, (Win32_LPSTR)&module_name, 1021); - if (len2 < 1) continue; - Win32_DWORD64 base_addr = symLoadModuleEx(process, null, (Win32_PCSTR)&char_buf, (Win32_PCSTR)&module_name, (Win32_DWORD64)info.lpBaseOfDll, load_size, null, 0); - } - if (!base) return BacktraceFault.IMAGE_NOT_FOUND?; - Win32_IMAGE_NT_HEADERS* h = imageNtHeader(base); - return h.fileHeader.machine; -} - -struct Symbol -{ - inline Win32_SYMBOL_INFO sym; - char[256] buffer; -} - -Win32_DWORD64 displacement; - -fn BacktraceList! symbolize_backtrace(Allocator allocator, void*[] backtrace) -{ - BacktraceList list; - list.init(allocator, backtrace.len); - Win32_HANDLE process = getCurrentProcess(); - symInitialize(process, null, 1); - defer symCleanup(process); - foreach (addr : backtrace) - { - list.push(resolve_backtrace(allocator, addr, process) ?? backtrace::BACKTRACE_UNKNOWN); - } - return list; -} - -fn Backtrace! resolve_backtrace(Allocator allocator, void* addr, Win32_HANDLE process) -{ - Symbol symbol; - //Win32_DWORD image_type = load_modules()!; - symbol.sizeOfStruct = Win32_SYMBOL_INFO.sizeof; - symbol.maxNameLen = 255; - if (!symFromAddr(process, (Win32_DWORD64)addr - 1, &displacement, &symbol)) - { - return BacktraceFault.NO_BACKTRACE_SYMBOLS?; - } - Win32_IMAGEHLP_MODULE64 module_info; - module_info.sizeOfStruct = Win32_IMAGEHLP_MODULE64.sizeof; - if (!symGetModuleInfo64(process, (Win32_DWORD64)addr - 1, &module_info)) - { - return BacktraceFault.NO_BACKTRACE_SYMBOLS?; - } - ZString module_name = (ZString)&module_info.imageName; - char[256] name; - unDecorateSymbolName(&symbol.name, (Win32_PSTR)&name, 256, UNDNAME_COMPLETE); - Win32_DWORD offset = 0; - Win32_IMAGEHLP_LINE64 line; - Backtrace backtrace; - ZString zname = (ZString)&name; - if (!symGetLineFromAddr64(process, (Win32_ULONG64)addr - 1, &offset, &line)) - { - backtrace.init(allocator, (uptr)addr, zname.str_view(), module_name.str_view()); - return backtrace; - } - String filename = ((ZString)line.fileName).str_view(); - backtrace.init(allocator, (uptr)addr, zname.str_view(), module_name.str_view(), file: filename, line: line.lineNumber); - return backtrace; -} diff --git a/lib7/std/os/win32/types.c3 b/lib7/std/os/win32/types.c3 deleted file mode 100644 index 12b96579b..000000000 --- a/lib7/std/os/win32/types.c3 +++ /dev/null @@ -1,697 +0,0 @@ -module std::os::win32; - -def Win32_BOOL = int; -def Win32_BOOLEAN = Win32_BYTE; -def Win32_BYTE = char; -def Win32_CCHAR = cinterop::CChar; -def Win32_CHAR = cinterop::CChar; -def Win32_COLORREF = Win32_DWORD; -def Win32_DWORD = uint; -def Win32_DWORDLONG = ulong; -def Win32_DWORD_PTR = Win32_ULONG_PTR; -def Win32_DWORD32 = uint; -def Win32_DWORD64 = ulong; -def Win32_FLOAT = float; -def Win32_HACCEL = Win32_HANDLE; -def Win32_HALF_PTR = int; -def Win32_HANDLE = Win32_PVOID; -def Win32_HBITMAP = Win32_HANDLE; -def Win32_HBRUSH = Win32_HANDLE; -def Win32_HCOLORSPACE = Win32_HANDLE; -def Win32_HCONV = Win32_HANDLE; -def Win32_HCONVLIST = Win32_HANDLE; -def Win32_HCURSOR = Win32_HICON; -def Win32_HDC = Win32_HANDLE; -def Win32_HDDEDATA = Win32_HANDLE; -def Win32_HDESK = Win32_HANDLE; -def Win32_HDROP = Win32_HANDLE; -def Win32_HDWP = Win32_HANDLE; -def Win32_HFILE = int; -def Win32_HFONT = Win32_HANDLE; -def Win32_HGDIOBJ = Win32_HANDLE; -def Win32_HGLOBAL = Win32_HANDLE; -def Win32_HHOOK = Win32_HANDLE; -def Win32_HICON = Win32_HANDLE; -def Win32_HINSTANCE = Win32_HANDLE; -def Win32_HKEY = Win32_HANDLE; -def Win32_HKL = Win32_HANDLE; -def Win32_HLOCAL = Win32_HANDLE; -def Win32_HMENU = Win32_HANDLE; -def Win32_HMETAFILE = Win32_HANDLE; -def Win32_HMODULE = Win32_HANDLE; -def Win32_HMONITOR = Win32_HANDLE; -def Win32_HPALETTE = Win32_HANDLE; -def Win32_HPEN = Win32_HANDLE; -def Win32_HRESULT = Win32_LONG; -def Win32_HRGN = Win32_HANDLE; -def Win32_HRSRC = Win32_HANDLE; -def Win32_HSZ = Win32_HANDLE; -def Win32_HWINSTA = Win32_HANDLE; -def Win32_HWND = Win32_HANDLE; -def Win32_INT = int; -def Win32_INT_PTR = iptr; -def Win32_INT8 = ichar; -def Win32_INT16 = short; -def Win32_INT32 = int; -def Win32_INT64 = long; -def Win32_LANGID = Win32_WORD; -def Win32_LCID = Win32_DWORD; -def Win32_LCTYPE = Win32_DWORD; -def Win32_LGRPID = Win32_DWORD; -def Win32_LONG = int; -def Win32_LONGLONG = long; -def Win32_LONG_PTR = iptr; -def Win32_LONG32 = int; -def Win32_LONG64 = long; -def Win32_LPARAM = Win32_LONG_PTR; -def Win32_LPBOOL = Win32_BOOL*; -def Win32_LPBYTE = Win32_BYTE*; -def Win32_LPCOLORREF = Win32_DWORD*; -def Win32_LPCSTR = Win32_CCHAR*; -def Win32_LPCTSTR = Win32_LPCWSTR; -def Win32_LPCVOID = void*; -def Win32_LPCWSTR = Win32_WCHAR*; -def Win32_LPDWORD = Win32_DWORD*; -def Win32_LPHANDLE = Win32_HANDLE*; -def Win32_LPINT = int*; -def Win32_LPLONG = int*; -def Win32_LPSTR = Win32_CCHAR*; -def Win32_LPTSTR = Win32_LPWSTR; -def Win32_LPVOID = void*; -def Win32_LPWORD = Win32_WORD*; -def Win32_LPWSTR = Win32_WCHAR*; -def Win32_LRESULT = Win32_LONG_PTR; -def Win32_NTSTATUS = Win32_LONG; -def Win32_PBOOL = Win32_BOOL*; -def Win32_PBOOLEAN = Win32_BOOLEAN*; -def Win32_PBYTE = Win32_BYTE*; -def Win32_PCHAR = Win32_CHAR*; -def Win32_PCSTR = Win32_CHAR*; -def Win32_PCTSTR = Win32_LPCWSTR; -def Win32_PCUNICODE_STRING = Win32_UNICODE_STRING*; -def Win32_PCWSTR = WString; -def Win32_PDWORD = Win32_DWORD*; -def Win32_PDWORDLONG = Win32_DWORDLONG*; -def Win32_PDWORDPTR = Win32_DWORD_PTR*; -def Win32_PDWORD32 = Win32_DWORD32*; -def Win32_PDWORD64 = Win32_DWORD64*; -def Win32_PFLOAT = Win32_FLOAT*; -def Win32_PHALFPTR = Win32_HALF_PTR*; -def Win32_PHANDLE = Win32_HANDLE*; -def Win32_PHKEY = Win32_HKEY*; -def Win32_PINT = int*; -def Win32_PINTPTR = Win32_INT_PTR*; -def Win32_PINT8 = Win32_INT8*; -def Win32_PINT16 = Win32_INT16*; -def Win32_PINT32 = Win32_INT32*; -def Win32_PINT64 = Win32_INT64*; -def Win32_PLCID = Win32_PDWORD; -def Win32_PLONG = Win32_LONG*; -def Win32_PLONGLONG = Win32_LONGLONG*; -def Win32_PLONG_PTR = Win32_LONG_PTR*; -def Win32_PLONG32 = Win32_LONG32*; -def Win32_PLONG64 = Win32_LONG64*; -def Win32_POINTER_32 = uint; -def Win32_POINTER_64 = uptr; -def Win32_POINTER_SIGNED = iptr; -def Win32_POINTER_UNSIGNED = uptr; -def Win32_PSHORT = Win32_SHORT*; -def Win32_PSIZE_T = usz*; -def Win32_PSSIZE_T = isz*; -def Win32_PSTR = Win32_CHAR*; -def Win32_PTBYTE = Win32_TBYTE*; -def Win32_PTCHAR = Win32_TCHAR*; -def Win32_PTSTR = Win32_LPWSTR; -def Win32_PUCHAR = Win32_UCHAR*; -def Win32_PUHALFPTR = Win32_UHALF_PTR*; -def Win32_PUINT = Win32_UINT*; -def Win32_PUINTPTR = Win32_UINT_PTR*; -def Win32_PUINT8 = Win32_UINT8*; -def Win32_PUINT16 = Win32_UINT16*; -def Win32_PUINT32 = Win32_UINT32*; -def Win32_PUINT64 = Win32_UINT64*; -def Win32_PULONG = Win32_ULONG*; -def Win32_PULONGLONG = Win32_ULONGLONG*; -def Win32_PULONG_PTR = Win32_ULONG_PTR*; -def Win32_PULONG32 = Win32_ULONG32*; -def Win32_PULONG64 = Win32_ULONG64*; -def Win32_PUNICODE_STRING = Win32_UNICODE_STRING*; -def Win32_PUSHORT = Win32_USHORT*; -def Win32_PVOID = void*; -def Win32_PWCHAR = Win32_WCHAR*; -def Win32_PWORD = Win32_WORD*; -def Win32_PWSTR = Win32_WCHAR*; -def Win32_QWORD = ulong; -def Win32_SC_HANDLE = Win32_HANDLE; -def Win32_SC_LOCK = Win32_LPVOID; -def Win32_SERVICE_STATUS_HANDLE = Win32_HANDLE; -def Win32_SHORT = short; -def Win32_SIZE_T = usz; -def Win32_SOCKET = Win32_HANDLE; -def Win32_SSIZE_T = isz; -def Win32_TBYTE = Win32_WCHAR; -def Win32_TCHAR = Win32_WCHAR; -def Win32_UCHAR = char; -def Win32_UHALF_PTR = uint; -def Win32_UINT = uint; -def Win32_UINT_PTR = uptr; -def Win32_UINT8 = char; -def Win32_UINT16 = ushort; -def Win32_UINT32 = uint; -def Win32_UINT64 = ulong; -def Win32_ULONG = uint; -def Win32_ULONGLONG = ulong; -def Win32_ULONG_PTR = ulong; -def Win32_ULONG32 = uint; -def Win32_ULONG64 = ulong; -def Win32_USHORT = ushort; -def Win32_USN = Win32_LONGLONG; -def Win32_WCHAR = Char16; -def Win32_WORD = ushort; -def Win32_WPARAM = Win32_UINT_PTR; - -const INVALID_HANDLE_VALUE = (Win32_HANDLE)(uptr)-1; - -struct Win32_UNICODE_STRING -{ - Win32_USHORT length; - Win32_USHORT maximum_length; - Win32_PWSTR buffer; -} - -union Win32_LARGE_INTEGER -{ - struct - { - Win32_DWORD lowPart; - Win32_LONG highPart; - } - ulong quadPart; -} - -distinct Win32_CRITICAL_SECTION = ulong[5]; - -struct Win32_SECURITY_ATTRIBUTES -{ - Win32_DWORD nLength; - Win32_LPVOID lpSecurityDescriptor; - Win32_BOOL bInheritHandle; -} - - - -def Win32_LPSECURITY_ATTRIBUTES = Win32_SECURITY_ATTRIBUTES*; -def Win32_PSECURITY_ATTRIBUTES = Win32_SECURITY_ATTRIBUTES*; - -struct Win32_STARTUPINFOW -{ - Win32_DWORD cb; - Win32_LPWSTR lpReserved; - Win32_LPWSTR lpDesktop; - Win32_LPWSTR lpTitle; - Win32_DWORD dwX; - Win32_DWORD dwY; - Win32_DWORD dwXSize; - Win32_DWORD dwYSize; - Win32_DWORD dwXCountChars; - Win32_DWORD dwYCountChars; - Win32_DWORD dwFillAttribute; - Win32_DWORD dwFlags; - Win32_WORD wShowWindow; - Win32_WORD cbReserved2; - Win32_LPBYTE lpReserved2; - Win32_HANDLE hStdInput; - Win32_HANDLE hStdOutput; - Win32_HANDLE hStdError; -} - -struct Win32_OVERLAPPED -{ - Win32_ULONG_PTR internal; - Win32_ULONG_PTR internalHigh; - union { - struct { - Win32_DWORD offset; - Win32_DWORD offsetHigh; - } - Win32_PVOID pointer; - } - Win32_HANDLE hEvent; -} - -def Win32_LPOVERLAPPED = Win32_OVERLAPPED*; -def Win32_LPSTARTUPINFOW = Win32_STARTUPINFOW*; - -struct Win32_STARTUPINFOEXW -{ - inline Win32_STARTUPINFOW win32_StartupInfo; - Win32_LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList; -} - -def Win32_LPPROC_THREAD_ATTRIBUTE_LIST = void*; -def Win32_LPSTARTUPINFOEXW = Win32_STARTUPINFOEXW*; - -struct Win32_FILETIME -{ - Win32_DWORD dwLowDateTime; - Win32_DWORD dwHighDateTime; -} - -struct Win32_PROCESS_INFORMATION -{ - Win32_HANDLE hProcess; - Win32_HANDLE hThread; - Win32_DWORD dwProcessId; - Win32_DWORD dwThreadId; -} - -def Win32_PPROCESS_INFORMATION = Win32_PROCESS_INFORMATION*; -def Win32_LPPROCESS_INFORMATION = Win32_PROCESS_INFORMATION*; - -struct Win32_SYSTEM_INFO -{ - union - { - Win32_DWORD dwOemId; - struct - { - Win32_WORD wProcessorArchitecture; - Win32_WORD wReserved; - } - } - Win32_DWORD dwPageSize; - Win32_LPVOID lpMinimumApplicationAddress; - Win32_LPVOID lpMaximumApplicationAddress; - Win32_DWORD_PTR dwActiveProcessorMask; - Win32_DWORD dwNumberOfProcessors; - Win32_DWORD dwProcessorType; - Win32_DWORD dwAllocationGranularity; - Win32_WORD wProcessorLevel; - Win32_WORD wProcessorRevision; -} - -def Win32_LPSYSTEM_INFO = Win32_SYSTEM_INFO*; - -struct Win32_MODULEINFO -{ - Win32_LPVOID lpBaseOfDll; - Win32_DWORD sizeOfImage; - Win32_LPVOID entryPoint; -} - -struct Win32_IMAGEHLP_LINE64 -{ - Win32_DWORD sizeOfStruct; - Win32_PVOID key; - Win32_DWORD lineNumber; - Win32_PCHAR fileName; - Win32_DWORD64 address; -} - -enum Win32_SYM_TYPE -{ - SYM_NONE, - SYM_COFF, - SYM_CV, - SYM_PDB, - SYM_EXPORT, - SYM_DEFERRED, - SYM_SYM, - SYM_DIA, - SYM_VIRTUAL -} - -struct Win32_GUID -{ - CULong data1; - CUShort data2; - CUShort data3; - char[8] data4; -} - -struct Win32_IMAGEHLP_MODULE64 -{ - Win32_DWORD sizeOfStruct; - Win32_DWORD64 baseOfImage; - Win32_DWORD imageSize; - Win32_DWORD timeDateStamp; - Win32_DWORD checkSum; - Win32_DWORD numSyms; - Win32_SYM_TYPE symType; - Win32_CHAR[32] moduleName; - Win32_CHAR[256] imageName; - Win32_CHAR[256] loadedImageName; - Win32_CHAR[256] loadedPdbName; - Win32_DWORD cVSig; - Win32_CHAR** cVData; - Win32_DWORD pdbSig; - Win32_GUID pdbSig70; - Win32_DWORD pdbAge; - Win32_BOOL pdbUnmatched; - Win32_BOOL dbgUnmatched; - Win32_BOOL lineNumbers; - Win32_BOOL globalSymbols; - Win32_BOOL typeInfo; - Win32_BOOL sourceIndexed; - Win32_BOOL publics; - Win32_DWORD machineType; - Win32_DWORD reserved; -} - -def Win32_PIMAGEHLP_MODULE64 = Win32_IMAGEHLP_MODULE64*; - -struct Win32_ARM64_NT_CONTEXT @align(16) -{ - Win32_DWORD contextFlags; - Win32_DWORD cpsr; - union - { - struct - { - Win32_DWORD64 x0; - Win32_DWORD64 x1; - Win32_DWORD64 x2; - Win32_DWORD64 x3; - Win32_DWORD64 x4; - Win32_DWORD64 x5; - Win32_DWORD64 x6; - Win32_DWORD64 x7; - Win32_DWORD64 x8; - Win32_DWORD64 x9; - Win32_DWORD64 x10; - Win32_DWORD64 x11; - Win32_DWORD64 x12; - Win32_DWORD64 x13; - Win32_DWORD64 x14; - Win32_DWORD64 x15; - Win32_DWORD64 x16; - Win32_DWORD64 x17; - Win32_DWORD64 x18; - Win32_DWORD64 x19; - Win32_DWORD64 x20; - Win32_DWORD64 x21; - Win32_DWORD64 x22; - Win32_DWORD64 x23; - Win32_DWORD64 x24; - Win32_DWORD64 x25; - Win32_DWORD64 x26; - Win32_DWORD64 x27; - Win32_DWORD64 x28; - Win32_DWORD64 fp; - Win32_DWORD64 lr; - } - Win32_DWORD64[31] x; - } - Win32_DWORD64 sp; - Win32_DWORD64 pc; - Win32_ARM64_NT_NEON128[32] v; - Win32_DWORD fpcr; - Win32_DWORD fpsr; - Win32_DWORD[ARM64_MAX_BREAKPOINTS] bcr; - Win32_DWORD64[ARM64_MAX_BREAKPOINTS] bvr; - Win32_DWORD[ARM64_MAX_WATCHPOINTS] wcr; - Win32_DWORD64[ARM64_MAX_WATCHPOINTS] wvr; -} -const ARM64_MAX_BREAKPOINTS = 8; -const ARM64_MAX_WATCHPOINTS = 2; - -struct Win32_ARM64_NT_NEON128 -{ - Win32_ULONGLONG low; - Win32_LONGLONG high; -} - -struct Win32_XMM_SAVE_AREA32 -{ - Win32_USHORT controlWord; - Win32_USHORT statusWord; - Win32_UCHAR tagWord; - Win32_UCHAR reserved1; - Win32_USHORT errorOpcode; - Win32_ULONG errrorOffset; - Win32_USHORT errorSelector; - Win32_USHORT reserved2; - Win32_ULONG dataOffset; - Win32_USHORT dataSelector; - Win32_USHORT reserved3; - Win32_ULONG mxCsr; - Win32_ULONG mxCsr_Mask; - Win32_M128A[8] floatRegisters; - Win32_M128A[16] xmmRegisters; - Win32_UCHAR[96] reserved4; -} - -struct Win32_AMD64_CONTEXT @align(16) -{ - Win32_DWORD64 p1Home; - Win32_DWORD64 p2Home; - Win32_DWORD64 p3Home; - Win32_DWORD64 p4Home; - Win32_DWORD64 p5Home; - Win32_DWORD64 p6Home; - Win32_DWORD contextFlags; - Win32_DWORD mxCsr; - Win32_WORD segCs; - Win32_WORD segDs; - Win32_WORD segEs; - Win32_WORD segFs; - Win32_WORD segGs; - Win32_WORD segSs; - Win32_DWORD eFlags; - Win32_DWORD64 dr0; - Win32_DWORD64 dr1; - Win32_DWORD64 dr2; - Win32_DWORD64 dr3; - Win32_DWORD64 dr6; - Win32_DWORD64 dr7; - Win32_DWORD64 rax; - Win32_DWORD64 rcx; - Win32_DWORD64 rdx; - Win32_DWORD64 rbx; - Win32_DWORD64 rsp; - Win32_DWORD64 rbp; - Win32_DWORD64 rsi; - Win32_DWORD64 rdi; - Win32_DWORD64 r8; - Win32_DWORD64 r9; - Win32_DWORD64 r10; - Win32_DWORD64 r11; - Win32_DWORD64 r12; - Win32_DWORD64 r13; - Win32_DWORD64 r14; - Win32_DWORD64 r15; - Win32_DWORD64 rip; - union - { - Win32_XMM_SAVE_AREA32 fltSave; - //Win32_NEON128[16] q; - Win32_ULONGLONG[32] d; - struct - { - Win32_M128A[2] header; - Win32_M128A[8] legacy; - Win32_M128A xmm0; - Win32_M128A xmm1; - Win32_M128A xmm2; - Win32_M128A xmm3; - Win32_M128A xmm4; - Win32_M128A xmm5; - Win32_M128A xmm6; - Win32_M128A xmm7; - Win32_M128A xmm8; - Win32_M128A xmm9; - Win32_M128A xmm10; - Win32_M128A xmm11; - Win32_M128A xmm12; - Win32_M128A xmm13; - Win32_M128A xmm14; - Win32_M128A xmm15; - } - Win32_DWORD[32] s; - } - Win32_M128A[26] vectorRegister; - Win32_DWORD64 vectorControl; - Win32_DWORD64 debugControl; - Win32_DWORD64 lastBranchToRip; - Win32_DWORD64 lastBranchFromRip; - Win32_DWORD64 lastExceptionToRip; - Win32_DWORD64 lastExceptionFromRip; -} - -const CONTEXT_AMD64 = 0x00100000; -const CONTEXT_AMD64_CONTROL = CONTEXT_AMD64 | 0x0001; -const CONTEXT_AMD64_INTEGER = CONTEXT_AMD64 | 0x0002; -const CONTEXT_AMD64_SEGMENTS = CONTEXT_AMD64 | 0x0004; -const CONTEXT_AMD64_FLOATING_POINT = CONTEXT_AMD64 | 0x0008; -const CONTEXT_AMD64_DEBUG_REGISTERS = CONTEXT_AMD64 | 0x0010; -const CONTEXT_AMD64_FULL = CONTEXT_AMD64_CONTROL | CONTEXT_AMD64_INTEGER | CONTEXT_AMD64_FLOATING_POINT; -const CONTEXT_AMD64_ALL = CONTEXT_AMD64_CONTROL | CONTEXT_AMD64_INTEGER | CONTEXT_AMD64_SEGMENTS | CONTEXT_AMD64_FLOATING_POINT | CONTEXT_AMD64_DEBUG_REGISTERS; - -def CONTEXT_CONTROL = CONTEXT_AMD64_CONTROL; -def CONTEXT_FULL = CONTEXT_AMD64_FULL; -def CONTEXT_ALL = CONTEXT_AMD64_ALL; - -def Win32_CONTEXT = Win32_AMD64_CONTEXT; -def Win32_PCONTEXT = Win32_CONTEXT*; - -struct Win32_M128A @align(16) -{ - Win32_ULONGLONG low; - Win32_LONGLONG high; -} - -struct Win32_IMAGE_DATA_DIRECTORY -{ - Win32_ULONG virtualAddress; - Win32_ULONG size; -} - -struct Win32_IMAGE_OPTIONAL_HEADER64 -{ - Win32_WORD magic; - Win32_BYTE majorLinkerVersion; - Win32_BYTE minorLinkerVersion; - Win32_DWORD sizeOfCode; - Win32_DWORD sizeOfInitializedData; - Win32_DWORD sizeOfUninitializedData; - Win32_DWORD addressOfEntryPoint; - Win32_DWORD baseOfCode; - Win32_ULONGLONG imageBase; - Win32_DWORD sectionAlignment; - Win32_DWORD fileAlignment; - Win32_WORD majorOperatingSystemVersion; - Win32_WORD minorOperatingSystemVersion; - Win32_WORD majorImageVersion; - Win32_WORD minorImageVersion; - Win32_WORD majorSubsystemVersion; - Win32_WORD minorSubsystemVersion; - Win32_DWORD win32VersionValue; - Win32_DWORD sizeOfImage; - Win32_DWORD sizeOfHeaders; - Win32_DWORD checkSum; - Win32_WORD subsystem; - Win32_WORD dllCharacteristics; - Win32_ULONGLONG sizeOfStackReserve; - Win32_ULONGLONG sizeOfStackCommit; - Win32_ULONGLONG sizeOfHeapReserve; - Win32_ULONGLONG sizeOfHeapCommit; - Win32_DWORD loaderFlags; - Win32_DWORD numberOfRvaAndSizes; - Win32_IMAGE_DATA_DIRECTORY[16] dataDirectory; -} - -def Win32_PIMAGE_OPTIONAL_HEADER64 = Win32_IMAGE_OPTIONAL_HEADER64*; -struct Win32_IMAGE_FILE_HEADER -{ - Win32_WORD machine; - Win32_WORD numberOfSections; - Win32_DWORD timeDateStamp; - Win32_DWORD pointerToSymbolTable; - Win32_DWORD numberOfSymbols; - Win32_WORD sizeOfOptionalHeader; - Win32_WORD characteristics; -} - -def Win32_PIMAGE_FILE_HEADER = Win32_IMAGE_FILE_HEADER*; - -struct Win32_IMAGE_NT_HEADERS -{ - Win32_DWORD signature; - Win32_IMAGE_FILE_HEADER fileHeader; - Win32_IMAGE_OPTIONAL_HEADER64 optionalHeader; -} - -def Win32_PIMAGE_NT_HEADERS = Win32_IMAGE_NT_HEADERS*; - -struct Win32_SYMBOL_INFO -{ - Win32_ULONG sizeOfStruct; - Win32_ULONG typeIndex; - Win32_ULONG64[2] reserved; - Win32_ULONG index; - Win32_ULONG size; - Win32_ULONG64 modBase; - Win32_ULONG flags; - Win32_ULONG64 value; - Win32_ULONG64 address; - Win32_ULONG register; - Win32_ULONG scope; - Win32_ULONG tag; - Win32_ULONG nameLen; - Win32_ULONG maxNameLen; - Win32_CHAR[1] name; -} - -def Win32_PSYMBOL_INFO = Win32_SYMBOL_INFO*; - -struct Win32_MODLOAD_DATA -{ - Win32_DWORD ssize; - Win32_DWORD ssig; - Win32_PVOID data; - Win32_DWORD size; - Win32_DWORD flags; -} - -enum Win32_ADDRESS_MODE -{ - ADDR_MODE_1616, - ADDR_MODE_1632, - ADDR_MODE_REAL, - ADDR_MODE_FLAT, -} - -struct Win32_ADDRESS64 -{ - Win32_DWORD64 offset; - Win32_WORD segment; - Win32_ADDRESS_MODE mode; -} - -struct Win32_KDHELP64 -{ - Win32_DWORD64 thread; - Win32_DWORD thCallbackStack; - Win32_DWORD thCallbackBStore; - Win32_DWORD nextCallback; - Win32_DWORD framePointer; - Win32_DWORD64 kiCallUserMode; - Win32_DWORD64 keUserCallbackDispatcher; - Win32_DWORD64 systemRangeStart; - Win32_DWORD64 kiUserExceptionDispatcher; - Win32_DWORD64 stackBase; - Win32_DWORD64 stackLimit; - Win32_DWORD buildVersion; - Win32_DWORD retpolineStubFunctionTableSize; - Win32_DWORD64 retpolineStubFunctionTable; - Win32_DWORD retpolineStubOffset; - Win32_DWORD retpolineStubSize; - Win32_DWORD64[2] reserved0; -} - -struct Win32_STACKFRAME64 -{ - Win32_ADDRESS64 addrPC; - Win32_ADDRESS64 addrReturn; - Win32_ADDRESS64 addrFrame; - Win32_ADDRESS64 addrStack; - Win32_ADDRESS64 addrBStore; - Win32_PVOID funcTableEntry; - Win32_DWORD64[4] params; - Win32_BOOL far; - Win32_BOOL virtual; - Win32_DWORD64[3] reserved; - Win32_KDHELP64 kdHelp; -} - -def Win32_PREAD_PROCESS_MEMORY_ROUTINE64 = fn Win32_BOOL(Win32_HANDLE hProcess, Win32_DWORD64 qwBaseAddress, Win32_PVOID lpBuffer, Win32_DWORD nSize, Win32_LPDWORD lpNumberOfBytesRead); -def Win32_PFUNCTION_TABLE_ACCESS_ROUTINE64 = fn Win32_PVOID(Win32_HANDLE ahProcess, Win32_DWORD64 addrBase); -def Win32_PGET_MODULE_BASE_ROUTINE64 = fn Win32_DWORD64(Win32_HANDLE hProcess, Win32_DWORD64 address); -def Win32_PTRANSLATE_ADDRESS_ROUTINE64 = fn Win32_DWORD64(Win32_HANDLE hProcess, Win32_HANDLE hThread, Win32_LPADDRESS64 lpaddr); -def Win32_PKDHELP64 = Win32_KDHELP64*; -def Win32_LPADDRESS64 = Win32_ADDRESS64*; -def Win32_LPSTACKFRAME64 = Win32_STACKFRAME64*; -def Win32_PMODLOAD_DATA = Win32_MODLOAD_DATA*; -def Win32_PIMAGEHLP_LINE64 = Win32_IMAGEHLP_LINE64*; -def Win32_LPMODULEINFO = Win32_MODULEINFO*; - diff --git a/lib7/std/os/win32/wincon.c3 b/lib7/std/os/win32/wincon.c3 deleted file mode 100644 index 70cb951c0..000000000 --- a/lib7/std/os/win32/wincon.c3 +++ /dev/null @@ -1,61 +0,0 @@ -// console - -module std::os::win32 @if(env::WIN32); - -struct Win32_KEY_EVENT_RECORD -{ - Win32_BOOL bKeyDown; - Win32_WORD wRepeatCount; - Win32_WORD wVirtualKeyCode; - Win32_WORD wVirtualScanCode; - union uChar - { - Win32_WCHAR unicodeChar; - Win32_CHAR asciiChar; - } - Win32_DWORD dwControlKeyState; -} - -struct Win32_COORD -{ - Win32_SHORT x; - Win32_SHORT y; -} - -struct Win32_MOUSE_EVENT_RECORD -{ - Win32_COORD dwMousePosition; - Win32_DWORD dwButtonState; - Win32_DWORD dwControlKeyState; - Win32_DWORD dwEventFlags; -} - -struct Win32_WINDOW_BUFFER_SIZE_RECORD -{ - Win32_COORD dwSize; -} - -struct Win32_MENU_EVENT_RECORD -{ - Win32_UINT dwCommandId; -} - -struct Win32_FOCUS_EVENT_RECORD -{ - Win32_BOOL bSetFocus; -} - -struct Win32_INPUT_RECORD -{ - Win32_WORD eventType; - union event - { - Win32_KEY_EVENT_RECORD keyEvent; - Win32_MOUSE_EVENT_RECORD mouseEvent; - Win32_WINDOW_BUFFER_SIZE_RECORD windowBufferSizeEvent; - Win32_MENU_EVENT_RECORD menuEvent; - Win32_FOCUS_EVENT_RECORD focusEvent; - } -} - -def Win32_PCOORD = Win32_COORD*; \ No newline at end of file diff --git a/lib7/std/os/win32/windef.c3 b/lib7/std/os/win32/windef.c3 deleted file mode 100644 index 05fed41f5..000000000 --- a/lib7/std/os/win32/windef.c3 +++ /dev/null @@ -1,51 +0,0 @@ -module std::os::win32 @if(env::WIN32); - -struct Win32_RECT -{ - Win32_LONG left; - Win32_LONG top; - Win32_LONG right; - Win32_LONG bottom; -} - -struct Win32_POINT -{ - Win32_LONG x; - Win32_LONG y; -} - -struct Win32_SIZE -{ - Win32_LONG cx; - Win32_LONG cy; -} - -struct Win32_WSABUF -{ - Win32_ULONG len; - Win32_CHAR* buf; -} - -struct Win32_SOCKADDR -{ - Win32_USHORT sa_family; - Win32_CHAR[14]* sa_data; -} - -def Win32_PSIZE = Win32_SIZE*; -def Win32_NPSIZE = Win32_SIZE*; -def Win32_LPSIZE = Win32_SIZE*; - -def Win32_PPOINT = Win32_POINT*; -def Win32_NPOINT = Win32_POINT*; -def Win32_LPOINT = Win32_POINT*; - -def Win32_PRECT = Win32_RECT*; -def Win32_NPRECT = Win32_RECT*; -def Win32_LPRECT = Win32_RECT*; - -def Win32_PWSABUF = Win32_WSABUF*; -def Win32_LPWSABUF = Win32_WSABUF*; - -def Win32_PSOCKADDR = Win32_SOCKADDR*; -def Win32_LPSOCKADDR = Win32_SOCKADDR*; \ No newline at end of file diff --git a/lib7/std/os/win32/winuser.c3 b/lib7/std/os/win32/winuser.c3 deleted file mode 100644 index 6cd7021ec..000000000 --- a/lib7/std/os/win32/winuser.c3 +++ /dev/null @@ -1,150 +0,0 @@ -module std::os::win32 @if(env::WIN32); - -def Win32_WNDPROC = fn Win32_LRESULT(Win32_HWND, Win32_UINT, Win32_WPARAM, Win32_LPARAM); - -struct Win32_WNDCLASSEXW -{ - Win32_UINT cbSize; - Win32_UINT style; - Win32_WNDPROC lpfnWndProc; - CInt cbClsExtra; - CInt cbWndExtra; - Win32_HINSTANCE hInstance; - Win32_HICON hIcon; - Win32_HCURSOR hCursor; - Win32_HBRUSH hbrBackground; - Win32_LPCWSTR lpszMenuName; - Win32_LPCWSTR lpszClassName; - Win32_HICON hIconSm; -} - -struct Win32_MSG -{ - Win32_HWND hwnd; - Win32_UINT message; - Win32_WPARAM wParam; - Win32_LPARAM lParam; - Win32_DWORD time; - Win32_POINT pt; - Win32_DWORD lPrivate; -} - -struct Win32_PAINTSTRUCT -{ - Win32_HDC hdc; - Win32_BOOL fErase; - Win32_RECT rcPaint; - Win32_BOOL fRestore; - Win32_BOOL fIncUpdate; - Win32_BYTE[32] rgbReserved; -} -def Win32_PWNDCLASSEXW = Win32_WNDCLASSEXW*; -def Win32_LPWNDCLASSEXW = Win32_WNDCLASSEXW*; -def Win32_NPWNDCLASSEXW = Win32_WNDCLASSEXW*; - -def Win32_PPAINTSTRUCT = Win32_PAINTSTRUCT*; -def Win32_LPPAINTSTRUCT = Win32_PAINTSTRUCT*; -def Win32_NPPAINTSTRUCT = Win32_PAINTSTRUCT*; - -def Win32_PMSG = Win32_MSG*; -def Win32_LPMSG = Win32_MSG*; -def Win32_NPMSG = Win32_MSG*; - -def Win32_ATOM = ushort; - -const Win32_DWORD WS_BORDER = 0x00800000L; -const Win32_DWORD WS_CAPTION = 0x00C00000L; -const Win32_DWORD WS_CHILD = 0x40000000L; -const Win32_DWORD WS_CHILDWINDOW = 0x40000000L; -const Win32_DWORD WS_CLIPCHILDREN = 0x02000000L; -const Win32_DWORD WS_CLIPSIBLINGS = 0x04000000L; -const Win32_DWORD WS_DISABLED = 0x08000000L; -const Win32_DWORD WS_DLGFRAME = 0x00400000L; -const Win32_DWORD WS_GROUP = 0x00020000L; -const Win32_DWORD WS_HSCROLL = 0x00100000L; -const Win32_DWORD WS_ICONIC = 0x20000000L; -const Win32_DWORD WS_MAXIMIZE = 0x01000000L; -const Win32_DWORD WS_MAXIMIZEBOX = 0x00010000L; -const Win32_DWORD WS_MINIMIZE = 0x20000000L; -const Win32_DWORD WS_MINIMIZEBOX = 0x00020000L; -const Win32_DWORD WS_OVERLAPPED = 0x00000000L; -const Win32_DWORD WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; -const Win32_DWORD WS_POPUP = 0x80000000L; -const Win32_DWORD WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU; -const Win32_DWORD WS_SIZEBOX = 0x00040000L; -const Win32_DWORD WS_SYSMENU = 0x00080000L; -const Win32_DWORD WS_TABSTOP = 0x00010000L; -const Win32_DWORD WS_THICKFRAME = 0x00040000L; -const Win32_DWORD WS_TILED = 0x00000000L; -const Win32_DWORD WS_TILEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; -const Win32_DWORD WS_VISIBLE = 0x10000000L; -const Win32_DWORD WS_VSCROLL = 0x00200000L; - -const Win32_UINT MB_OK = 0x00000000; -const Win32_UINT MB_OKCANCEL = 0x00000001; -const Win32_UINT MB_ABORTRETRYIGNORE = 0x00000002; -const Win32_UINT MB_YESNOCANCEL = 0x00000003; -const Win32_UINT MB_YESNO = 0x00000004; -const Win32_UINT MB_RETRYCANCEL = 0x00000005; -const Win32_UINT MB_CANCELTRYCONTINUE = 0x00000006; -const Win32_UINT MB_ICONHAND = 0x00000010; -const Win32_UINT MB_ICONQUESTION = 0x00000020; -const Win32_UINT MB_ICONEXCLAMATION = 0x00000030; -const Win32_UINT MB_ICONASTERISK = 0x00000040; -const Win32_UINT MB_USERICON = 0x00000080; -const Win32_UINT MB_ICONWARNING = MB_ICONEXCLAMATION; -const Win32_UINT MB_ICONERROR = MB_ICONHAND; -const Win32_UINT MB_ICONINFORMATION = MB_ICONASTERISK; -const Win32_UINT MB_ICONSTOP = MB_ICONHAND; - -const GWL_WNDPROC @if(env::ARCH_32_BIT) = -4; -const GWL_HINSTANCE @if(env::ARCH_32_BIT) = -6; -const GWL_HWNDPARENT @if(env::ARCH_32_BIT) = -8; -const GWL_STYLE = -16; -const GWL_EXSTYLE = -20; -const GWL_USERDATA @if(env::ARCH_32_BIT) = -21; -const GWL_ID = -12; -const GWLP_WNDPROC = -4; -const GWLP_HINSTANCE = -6; -const GWLP_HWNDPARENT = -8; -const GWLP_USERDATA = -21; -const GWLP_ID = -12; - -extern fn Win32_HDC beginPaint(Win32_HWND, Win32_LPPAINTSTRUCT) @extern("BeginPaint"); -extern fn Win32_LRESULT callWindowProcW(Win32_WNDPROC lpPrevWndFunc, Win32_HWND hWnd, Win32_UINT msg, Win32_WPARAM wParam, Win32_LPARAM lParam) @extern("CallWindowProcW"); -extern fn Win32_HWND createWindowExW(Win32_DWORD, Win32_LPCWSTR, Win32_LPCWSTR, Win32_DWORD, CInt, CInt, CInt, CInt, Win32_HWND, Win32_HMENU, Win32_HINSTANCE, Win32_LPVOID) @extern("CreateWindowExW"); -extern fn Win32_LRESULT defWindowProcW(Win32_HWND, Win32_UINT, Win32_WPARAM, Win32_LPARAM) @extern("DefWindowProcW"); -extern fn Win32_BOOL dispatchMessage(Win32_MSG* lpMsg) @extern("DispatchMessageW"); -extern fn Win32_BOOL endPaint(Win32_HWND, Win32_LPPAINTSTRUCT) @extern("EndPaint"); -extern fn Win32_BOOL getMessageW(Win32_LPMSG, Win32_HWND, Win32_UINT, Win32_UINT) @extern("GetMessageW"); -extern fn Win32_BOOL getUpdateRect(Win32_HWND hWnd, Win32_LPRECT lpRect, Win32_BOOL bErase) @extern("GetUpdateRect"); -extern fn Win32_LONG_PTR getWindowLongPtrW(Win32_HWND hWnd, CInt nIndex) @extern("GetWindowLongPtrW"); -extern fn Win32_LONG getWindowLongW(Win32_HWND hWnd, CInt nIndex) @extern("GetWindowLongW"); -extern fn Win32_HCURSOR loadCursorW(Win32_HINSTANCE instance, Win32_LPCWSTR cursorName) @extern("LoadCursorW"); -extern fn Win32_HICON loadIconW(Win32_HINSTANCE instance, Win32_LPCWSTR iconName) @extern("LoadIconW"); -extern fn int messageBoxW(Win32_HWND hWnd, Win32_LPCWSTR lpText, Win32_LPCWSTR lpCaption, Win32_UINT uType) @extern("MessageBoxW"); -extern fn void postQuitMessage(CInt) @extern("PostQuitMessage"); -extern fn Win32_ATOM registerClassExW(Win32_WNDCLASSEXW*) @extern("RegisterClassExW"); -extern fn Win32_LONG_PTR setWindowLongPtrW(Win32_HWND hWnd, CInt nIndex, Win32_LONG_PTR dwNewLong) @extern("SetWindowLongPtrW"); -extern fn Win32_LONG setWindowLongW(Win32_HWND hWnd, CInt nIndex, Win32_LONG dwNewLong) @extern("SetWindowLongW"); -extern fn Win32_BOOL showWindow(Win32_HWND, CInt) @extern("ShowWindow"); -extern fn Win32_BOOL translateMessage(Win32_MSG* lpMsg) @extern("TranslateMessage"); -extern fn Win32_BOOL updateWindow(Win32_HWND) @extern("UpdateWindow"); - -macro getWindowLongPtr(Win32_HWND hWnd, CInt nIndex) -{ - $if env::ARCH_64_BIT: - return getWindowLongPtrW(hWnd, nIndex); - $else - return getWindowLongW(hWnd, nIndex); - $endif -} - -macro setWindowLongPtr(Win32_HWND hWnd, CInt nIndex, dwNewLong) -{ - $if env::ARCH_64_BIT: - return setWindowLongPtrW(hWnd, nIndex, dwNewLong); - $else - return setWindowLongW(hWnd, nIndex, dwNewLong); - $endif -} diff --git a/lib7/std/os/win32/ws2def.c3 b/lib7/std/os/win32/ws2def.c3 deleted file mode 100644 index 041b62701..000000000 --- a/lib7/std/os/win32/ws2def.c3 +++ /dev/null @@ -1,31 +0,0 @@ -module std::os::win32 @if(env::WIN32); - -struct Win32_addrinfo -{ - Win32_INT ai_flags; - Win32_INT ai_family; - Win32_INT ai_socktype; - Win32_INT ai_protocol; - Win32_SIZE_T ai_addrlen; - Win32_CHAR* ai_canonname; - Win32_SOCKADDR* ai_addr; - Win32_ADDRINFO* ai_next; -} - -def Win32_ADDRINFO = Win32_addrinfo; -def Win32_ADDRINFOA = Win32_ADDRINFO; -def Win32_PADDRINFOA = Win32_ADDRINFO*; - -struct Win32_addrinfoW { - Win32_INT ai_flags; - Win32_INT ai_family; - Win32_INT ai_socktype; - Win32_INT ai_protocol; - Win32_SIZE_T ai_addrlen; - Win32_PWSTR ai_canonname; - Win32_SOCKADDR *ai_addr; - Win32_addrinfo *ai_next; -} - -def Win32_ADDRINFOW = Win32_addrinfoW; -def Win32_PADDRINFOW = Win32_addrinfoW*; \ No newline at end of file diff --git a/lib7/std/os/win32/wsa.c3 b/lib7/std/os/win32/wsa.c3 deleted file mode 100644 index d80b238c7..000000000 --- a/lib7/std/os/win32/wsa.c3 +++ /dev/null @@ -1,232 +0,0 @@ -module std::os::win32 @if(env::WIN32); - -// See https://github.com/wine-mirror/wine/blob/master/include/winsock2.h - -distinct WSAError = int; - -struct Win32_pollfd -{ - Win32_SOCKET fd; - Win32_SHORT events; - Win32_SHORT revents; -} -def Win32_WSAPOLLFD = Win32_pollfd; -def Win32_PWSAPOLLFD = Win32_WSAPOLLFD*; -def Win32_LPWSAPOLLFD = Win32_WSAPOLLFD*; - -struct Win32_InAddr -{ - union - { - struct s_un_b - { - Win32_UCHAR s_b1; - Win32_UCHAR s_b2; - Win32_UCHAR s_b3; - Win32_UCHAR s_b4; - } - struct s_un_w - { - Win32_USHORT s_w1; - Win32_USHORT s_w2; - } - Win32_ULONG s_addr; - } -} - -struct Win32_SOCKADDR_IN -{ - Win32_SHORT sin_family; - Win32_USHORT sin_port; - Win32_InAddr sin_addr; - Win32_CHAR[8]* sin_zero; -} - -const usz _SS_PAD1SIZE = 6; -const usz _SS_PAD2SIZE = 112; - -struct Win32_SOCKADDR_STORAGE -{ - Win32_USHORT ss_family; - Win32_CHAR[_SS_PAD1SIZE]* __ss_pad1; - Win32_INT64 __ss_align; - Win32_CHAR[_SS_PAD2SIZE]* __ss_pad2; -} - -def Win32_WSAOVERLAPPED = Win32_OVERLAPPED; -def Win32_LPWSAOVERLAPPED = Win32_WSAOVERLAPPED*; - -def Win32_LPWSAOVERLAPPED_COMPLETION_ROUTINE = fn void ( - Win32_DWORD dwError, - Win32_DWORD cbTransferred, - Win32_LPWSAOVERLAPPED - lpOverlapped, - Win32_DWORD dwFlags -); - -def Win32_LPFN_WSARECV = fn CInt( - Win32_SOCKET socket, - Win32_LPWSABUF buffers, - Win32_DWORD buffer_count, - Win32_LPDWORD bytes, - Win32_LPDWORD flags, - Win32_LPWSAOVERLAPPED overlapped, - Win32_LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine -); - -def Win32_LPFN_WSARECVFROM = fn CInt( - Win32_SOCKET socket, - Win32_LPWSABUF buffers, - Win32_DWORD buffer_count, - Win32_LPDWORD bytes, - Win32_LPDWORD flags, - Win32_SOCKADDR* addr, - Win32_LPINT addr_len, - Win32_LPWSAOVERLAPPED overlapped, - Win32_LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine -); - -def Win32_LPFn_CONNECTEX = fn bool( - Win32_SOCKET, - Win32_SOCKADDR*, - Win32_INT, - Win32_PVOID, - Win32_DWORD, - Win32_LPDWORD, - void* -); - -def Win32_LPFn_ACCEPTEX = fn bool( - Win32_SOCKET, - Win32_SOCKET, - Win32_PVOID, - Win32_DWORD, - Win32_DWORD, - Win32_DWORD, - Win32_LPDWORD, - void* -); - -const Win32_SHORT POLLERR = 0x0001; -const Win32_SHORT POLLHUP = 0x0002; -const Win32_SHORT POLLNVAL = 0x0004; -const Win32_SHORT POLLWRNORM = 0x0010; -const Win32_SHORT POLLWRBAND = 0x0020; -const Win32_SHORT POLLRDNORM = 0x0100; -const Win32_SHORT POLLRDBAND = 0x0200; -const Win32_SHORT POLLPRI = 0x0400; -const Win32_SHORT POLLIN = POLLRDNORM | POLLRDBAND; -const Win32_SHORT POLLOUT = POLLWRNORM; - -const SD_RECEIVE = 0x00; -const SD_SEND = 0x01; -const SD_BOTH = 0x02; - -extern fn CInt win32_WSAPoll(Win32_LPWSAPOLLFD fdArray, Win32_ULONG fds, Win32_INT timeout) @extern("WSAPoll") @builtin; -extern fn WSAError win32_WSAGetLastError() @extern("WSAGetLastError") @builtin; -extern fn void win32_WSASetLastError(WSAError error) @extern("WSASetLastError") @builtin; -extern fn CInt win32_WSAStartup(Win32_WORD, void*) @extern("WSAStartup") @builtin; -extern fn CInt win32_WSACleanup() @extern("WSACleanup") @builtin; - -const int FIONBIO = -2147195266; -const int FIONREAD = 1074030207; -const int SIOCATMARK = 1074033415; - -module std::os::win32::wsa @if(env::WIN32); - -const WSAError NO_ERROR = 0; -const WSAError INVALID_HANDLE = 6; -const WSAError NOT_ENOUGH_MEMORY = 8; -const WSAError INVALID_PARAMETER = 87; -const WSAError OPERATION_ABORTED = 995; -const WSAError IO_INCOMPLETE = 996; -const WSAError IO_PENDING = 997; -const WSAError EINTR = 10004; -const WSAError EBADF = 10009; -const WSAError EACCESS = 10013; -const WSAError EFAULT = 10014; -const WSAError EINVAL = 10022; -const WSAError EMFILE = 10024; -const WSAError EWOULDBLOCK = 10035; -const WSAError EINPROGRESS = 10036; -const WSAError EALREADY = 10037; -const WSAError ENOTSOCK = 10038; -const WSAError EDESTADDRREQ = 10039; -const WSAError EMSGSIZE = 10040; -const WSAError EPROTOTYPE = 10041; -const WSAError ENOPROTOOPT = 10042; -const WSAError EPROTONOSUPPORT = 10043; -const WSAError ESOCKTNOSUPPORT = 10044; -const WSAError EOPNOTSUPP = 10045; -const WSAError EPFNOSUPPORT = 10046; -const WSAError EAFNOSUPPORT = 10047; -const WSAError EADDRINUSE = 10048; -const WSAError EADDRNOTAVAIL = 10049; -const WSAError ENETDOWN = 10050; -const WSAError ENETUNREACH = 10051; -const WSAError ENETRESET = 10052; -const WSAError ECONNABORTED = 10053; -const WSAError ECONNRESET = 10054; -const WSAError ENOBUFS = 10055; -const WSAError EISCONN = 10056; -const WSAError ENOTCONN = 10057; -const WSAError ESHUTDOWN = 10058; -const WSAError ETOOMANYREFS = 10059; -const WSAError ETIMEDOUT = 10060; -const WSAError ECONNREFUSED = 10061; -const WSAError ELOOP = 10062; -const WSAError ENAMETOOLONG = 10063; -const WSAError EHOSTDOWN = 10064; -const WSAError EHOSTUNREACH = 10065; -const WSAError ENOTEMPTY = 10066; -const WSAError EPROCLIM = 10067; -const WSAError EUSERS = 10068; -const WSAError EDQUOT = 10069; -const WSAError ESTALE = 10070; -const WSAError EREMOTE = 10071; -const WSAError SYSNOTREADY = 10091; -const WSAError VERNOTSUPPORTED = 10092; -const WSAError NOTINITIALISED = 10093; -const WSAError EDISCON = 10101; -const WSAError ENOMORE = 10102; -const WSAError ECANCELLED = 10103; -const WSAError EINVALIDPROCTABLE = 10104; -const WSAError EINVALIDPROVIDER = 10105; -const WSAError EPROVIDERFAILEDINIT = 10106; -const WSAError SYSCALLFAILURE = 10107; -const WSAError SERVICE_NOT_FOUND = 10108; -const WSAError TYPE_NOT_FOUND = 10109; -const WSAError E_NO_MORE = 10110; -const WSAError E_CANCELLED = 10111; -const WSAError REFUSED = 10112; -const WSAError HOST_NOT_FOUND = 11001; -const WSAError TRY_AGAIN = 11002; -const WSAError NO_RECOVERY = 11003; -const WSAError NO_DATA = 11004; -const WSAError QOS_RECEIVERS = 11005; -const WSAError QOS_SENDERS = 11006; -const WSAError QOS_NO_SENDERS = 11007; -const WSAError QOS_NO_RECEIVERS = 11008; -const WSAError QOS_REQUEST_CONFIRMED = 11009; -const WSAError QOS_ADMISSION_FAILURE = 11010; -const WSAError QOS_POLICY_FAILURE = 11011; -const WSAError QOS_BAD_STYLE = 11012; -const WSAError QOS_BAD_OBJECT = 11013; -const WSAError QOS_TRAFFIC_CTRL_ERROR = 11014; -const WSAError QOS_GENERIC_ERROR = 11015; -const WSAError QOS_ESERVICETYPE = 11016; -const WSAError QOS_EFLOWSPEC = 11017; -const WSAError QOS_EPROVSPECBUF = 11018; -const WSAError QOS_EFILTERSTYLE = 11019; -const WSAError QOS_EFILTERTYPE = 11020; -const WSAError QOS_EFILTERCOUNT = 11021; -const WSAError QOS_EOBJLENGTH = 11022; -const WSAError QOS_EFLOWCOUNT = 11023; -const WSAError QOS_EUNKOWNPSOBJ = 11024; -const WSAError QOS_EPOLICYOBJ = 11025; -const WSAError QOS_EFLOWDESC = 11026; -const WSAError QOS_EPSFLOWSPEC = 11027; -const WSAError QOS_EPSFILTERSPEC = 11028; -const WSAError QOS_ESDMODEOBJ = 11029; -const WSAError QOS_ESHAPERATEOBJ = 11030; -const WSAError QOS_RESERVED_PETYPE = 11031; diff --git a/lib7/std/sort/binarysearch.c3 b/lib7/std/sort/binarysearch.c3 deleted file mode 100644 index 931a3afe8..000000000 --- a/lib7/std/sort/binarysearch.c3 +++ /dev/null @@ -1,51 +0,0 @@ -module std::sort; - -<* -Perform a binary search over the sorted array and return the index -in [0, array.len) where x would be inserted or cmp(i) is true and cmp(j) is true for j in [i, array.len). - @require @is_sortable(list) "The list must be sortable" - @require @is_valid_cmp_fn(cmp, list, context) "Expected a comparison function which compares values" - @require @is_valid_context(cmp, context) "Expected a valid context" -*> -macro usz binarysearch(list, x, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin -{ - usz i; - usz len = len_from_list(list); - var $no_cmp = @is_empty_macro_slot(cmp); - var $has_context = @is_valid_macro_slot(context); - for (usz j = len; i < j;) - { - usz half = i + (j - i) / 2; - $if $no_cmp: - switch - { - case greater(list[half], x): j = half; - case less(list[half], x): i = half + 1; - default: return half; - } - $else - - $switch - $case $defined(cmp(list[0], list[0], context)): - int res = cmp(list[half], x, context); - $case $defined(cmp(list[0], list[0])): - assert(!$has_context); - int res = cmp(list[half], x); - $case $defined(cmp(&list[0], &list[0], context)): - int res = cmp(&list[half], &x, context); - $case $defined(cmp(&list[0], &list[0])): - assert(!$has_context); - int res = cmp(&list[half], &x); - $default: - assert(false, "Invalid comparison function"); - $endswitch - switch - { - case res > 0: j = half; - case res < 0: i = half + 1; - default: return half; - } - $endif - } - return i; -} \ No newline at end of file diff --git a/lib7/std/sort/countingsort.c3 b/lib7/std/sort/countingsort.c3 deleted file mode 100644 index 09ab21a32..000000000 --- a/lib7/std/sort/countingsort.c3 +++ /dev/null @@ -1,181 +0,0 @@ -module std::sort; -import std::sort::is; -import std::sort::cs; -import std::sort::qs; - -<* -Sort list using the counting sort algorithm. - @require @is_sortable(list) "The list must be indexable and support .len or .len()" - @require @is_cmp_key_fn(key_fn, list) "Expected a transformation function which returns an unsigned integer." -*> -macro countingsort(list, key_fn = EMPTY_MACRO_SLOT) @builtin -{ - usz len = sort::len_from_list(list); - cs::csort{$typeof(list), $typeof(key_fn)}(list, 0, len, key_fn, ~((uint)0)); -} - -macro insertionsort_indexed(list, start, end, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin -{ - is::isort{$typeof(list), $typeof(cmp), $typeof(context)}(list, (usz)start, (usz)end, cmp, context); -} - -macro quicksort_indexed(list, start, end, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin -{ - qs::qsort{$typeof(list), $typeof(cmp), $typeof(context)}(list, (isz)start, (isz)(end-1), cmp, context); -} - -module std::sort::cs{Type, KeyFn}; - -def Counts = usz[256] @private; -def Ranges = usz[257] @private; -def Indexs = char[256] @private; -def ElementType = $typeof((Type){}[0]); - -const bool NO_KEY_FN @private = types::is_same(KeyFn, EmptySlot); -const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $assignable((Type){}[0], $typefrom(KeyFn.paramsof[0].type)); -const bool LIST_HAS_REF @private = $defined(&(Type){}[0]); - -def KeyFnReturnType = $typefrom(KeyFn.returns) @if(!NO_KEY_FN); -def KeyFnReturnType = ElementType @if(NO_KEY_FN); -def CmpCallback = fn int(ElementType, ElementType) @if(KEY_BY_VALUE && NO_KEY_FN); -def CmpCallback = fn int(ElementType*, ElementType*) @if(!KEY_BY_VALUE && NO_KEY_FN); -def CmpCallback = fn int(ElementType, ElementType, KeyFn) @if(KEY_BY_VALUE && !NO_KEY_FN); -def CmpCallback = fn int(ElementType*, ElementType*, KeyFn) @if(!KEY_BY_VALUE && !NO_KEY_FN); - -fn void csort(Type list, usz low, usz high, KeyFn key_fn, uint byte_idx) -{ - if (high <= low) return; - $if NO_KEY_FN: - CmpCallback compare_fn = fn (lhs, rhs) => compare_to(lhs, rhs); - $else - CmpCallback compare_fn = fn (lhs, rhs, key_fn) => compare_to(key_fn(lhs), key_fn(rhs)); - $endif; - - byte_idx = byte_idx >= KeyFnReturnType.sizeof ? KeyFnReturnType.sizeof - 1 : byte_idx; - - Counts counts; - Ranges ranges; - Indexs indexs; - - KeyFnReturnType mn = ~(KeyFnReturnType)0; - KeyFnReturnType mx = 0; - - char last_key = 0; - char keys_ordered = 1; - - for (usz i = low; i < high; i++) - { - $switch - $case NO_KEY_FN: - KeyFnReturnType k = list[i]; - $case KEY_BY_VALUE: - KeyFnReturnType k = key_fn(list[i]); - $case LIST_HAS_REF: - KeyFnReturnType k = key_fn(&list[i]); - $default: - KeyFnReturnType k = key_fn(&&list[i]); - $endswitch; - - char key_byte = (char)((k >> (byte_idx * 8)) & 0xff); - ++counts[key_byte]; - - mn = k < mn ? k : mn; - mx = k > mx ? k : mx; - - keys_ordered = keys_ordered & (char)(key_byte >= last_key); - last_key = key_byte; - } - - KeyFnReturnType diff = mx - mn; - if (diff == 0) return; - - ushort fallback0_count = 0; - ushort fallback1_count = 0; - ushort recursion_count = 0; - - usz total = 0; - foreach (char i, count : counts) - { - indexs[fallback0_count] = i; - indexs[255 - recursion_count] = i; - - fallback0_count += (ushort)(count > 1 && count <= 32); - recursion_count += (ushort)(count > 128); - - counts[i] = total; - ranges[i] = total; - total += count; - } - ranges[256] = total; - - ushort remaining_indexs = 256 - (fallback0_count + recursion_count); - for(ushort i = 0; (i < 256) && remaining_indexs; i++) { - indexs[fallback0_count + fallback1_count] = (char)i; - usz count = ranges[i + 1] - ranges[i]; - ushort within_fallback1_range = (ushort)(count > 32 && count <= 128); - fallback1_count += within_fallback1_range; - remaining_indexs -= within_fallback1_range; - } - - if (!keys_ordered) - { - usz sorted_count = 0; - - do - { - foreach (x, s : counts) - { - usz e = ranges[x + 1]; - sorted_count += (e - s); - for (; s < e; s++) - { - $switch - $case NO_KEY_FN: - KeyFnReturnType k = list[low + s]; - $case KEY_BY_VALUE: - KeyFnReturnType k = key_fn(list[low + s]); - $case LIST_HAS_REF: - KeyFnReturnType k = key_fn(&list[low + s]); - $default: - KeyFnReturnType k = key_fn(&&list[low + s]); - $endswitch; - char k_idx = (char)(k >> (byte_idx * 8)); - usz target_idx = counts[k_idx]; - @swap(list[low + s], list[low + target_idx]); - counts[k_idx]++; - } - } - } while (sorted_count < ranges[256]); - } - - if (byte_idx) - { - for (usz p = 0; p < fallback0_count; p++) { - usz i = indexs[p]; - - usz start_offset = ranges[i]; - usz end_offset = ranges[i + 1]; - - insertionsort_indexed(list, low + start_offset, low + end_offset, compare_fn, key_fn); - } - - for (usz p = 0; p < fallback1_count; p++) { - usz i = indexs[fallback0_count + p]; - - usz start_offset = ranges[i]; - usz end_offset = ranges[i + 1]; - - quicksort_indexed(list, low + start_offset, low + end_offset, compare_fn, key_fn); - } - - for (usz p = 0; p < recursion_count; p++) - { - usz i = indexs[255 - p]; - - usz start_offset = ranges[i]; - usz end_offset = ranges[i + 1]; - - csort(list, low + start_offset, low + end_offset, key_fn, byte_idx - 1); - } - } -} diff --git a/lib7/std/sort/insertionsort.c3 b/lib7/std/sort/insertionsort.c3 deleted file mode 100644 index f3106067c..000000000 --- a/lib7/std/sort/insertionsort.c3 +++ /dev/null @@ -1,70 +0,0 @@ -module std::sort; -import std::sort::is; - -<* -Sort list using the quick sort algorithm. - @require @is_sortable(list) "The list must be indexable and support .len or .len()" - @require @is_valid_cmp_fn(cmp, list, context) "Expected a comparison function which compares values" -*> -macro insertionsort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin @safemacro -{ - $if @typekind(list) == POINTER &&& (@typekind(*list) == ARRAY || @typekind(*list) == VECTOR): - $typeof((*list)[0])[] list2 = list; - is::isort{$typeof(list2), $typeof(cmp), $typeof(context)}(list2, 0, list.len, cmp, context); - $else - usz len = sort::len_from_list(list); - is::isort{$typeof(list), $typeof(cmp), $typeof(context)}(list, 0, (isz)len, cmp, context); - $endif -} - -module std::sort::is{Type, CmpFn, Context}; - -def ElementType = $typeof(((Type){})[0]); - -fn void isort(Type list, usz low, usz high, CmpFn comp, Context context) -{ - var $has_cmp = @is_valid_macro_slot(comp); - var $has_context = @is_valid_macro_slot(context); - var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type)); - var $has_get_ref = $defined(&list[0]); - for (usz i = low; i < high; ++i) - { - usz j = i; - for (;j > low;) - { - $if $has_get_ref: - ElementType *rhs = &list[j]; - ElementType *lhs = &list[--j]; - $switch - $case $cmp_by_value && $has_context: - if (comp(*rhs, *lhs, context) >= 0) break; - $case $cmp_by_value: - if (comp(*rhs, *lhs) >= 0) break; - $case $has_cmp && $has_context: - if (comp(rhs, lhs, context) >= 0) break; - $case $has_cmp: - if (comp(rhs, lhs) >= 0) break; - $default: - if (!less(*rhs, *lhs)) break; - $endswitch - @swap(*rhs, *lhs); - $else - usz r = j; - --j; - $switch - $case $cmp_by_value && $has_context: - if (comp(list[r], list[j], context) >= 0) break; - $case $cmp_by_value: - if (comp(list[r], list[j]) >= 0) break; - $case $has_cmp && $has_context: - if (comp(&list[r], &list[j], context) >= 0) break; - $case $has_cmp: - if (comp(&list[r], &list[j]) >= 0) break; - $default: - if (!less(list[r], list[j])) break; - $endswitch - @swap(list[r], list[j]); - $endif - } - } -} diff --git a/lib7/std/sort/quicksort.c3 b/lib7/std/sort/quicksort.c3 deleted file mode 100644 index ca7e77eb2..000000000 --- a/lib7/std/sort/quicksort.c3 +++ /dev/null @@ -1,150 +0,0 @@ -module std::sort; -import std::sort::qs; - -<* -Sort list using the quick sort algorithm. - @require @is_sortable(list) "The list must be indexable and support .len or .len()" - @require @is_valid_cmp_fn(cmp, list, context) "Expected a comparison function which compares values" - @require @is_valid_context(cmp, context) "Expected a valid context" -*> -macro quicksort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin -{ - $if @typekind(list) == POINTER &&& (@typekind(*list) == ARRAY || @typekind(*list) == VECTOR): - $typeof((*list)[0])[] list2 = list; - qs::qsort{$typeof(list2), $typeof(cmp), $typeof(context)}(list2, 0, (isz)list.len - 1, cmp, context); - $else - usz len = sort::len_from_list(list); - qs::qsort{$typeof(list), $typeof(cmp), $typeof(context)}(list, 0, (isz)len - 1, cmp, context); - $endif -} - -<* -Select the (k+1)th smallest element in an unordered list using Hoare's -selection algorithm (Quickselect). k should be between 0 and len-1. The data -list will be partially sorted. - - @require @is_sortable(list) "The list must be indexable and support .len or .len()" - @require @is_valid_cmp_fn(cmp, list, context) "expected a comparison function which compares values" - @require @is_valid_context(cmp, context) "Expected a valid context" -*> -macro quickselect(list, isz k, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin -{ - usz len = sort::len_from_list(list); - return qs::qselect{$typeof(list), $typeof(cmp), $typeof(context)}(list, 0, (isz)len - 1, k, cmp, context); -} - -module std::sort::qs{Type, CmpFn, Context}; - -def ElementType = $typeof(((Type){})[0]); - -struct StackElementItem @private -{ - isz low; - isz high; -} - -def Stack = StackElementItem[64] @private; - -// Based on https://alienryderflex.com/quicksort by Darel Rex Finley, Public Domain. - -fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context) -{ - if (low >= 0 && high >= 0 && low < high) - { - Stack stack; - stack[0].low = low; - stack[0].high = high; - isz i; - isz l; - isz h; - while (i >= 0) - { - l = stack[i].low; - h = stack[i].high; - - if (l < h) - { - l = @partition(list, l, h, cmp, context); - stack[i + 1].low = l + 1; - stack[i + 1].high = stack[i].high; - stack[i++].high = l; - if (stack[i].high - stack[i].low > stack[i - 1].high - stack[i - 1].low) - { - @swap(stack[i], stack[i - 1]); - } - } - else - { - i--; - } - } - } -} - -<* -@require low <= k "kth smalles element is smaller than lower bounds" -@require k <= high "kth smalles element is larger than upper bounds" -*> -fn ElementType! qselect(Type list, isz low, isz high, isz k, CmpFn cmp, Context context) -{ - if (low >= 0 && high >= 0 && low < high) - { - isz l = low; - isz h = high; - isz pivot; - - usz max_retries = 64; - while (l <= h && max_retries--) - { - pivot = @partition(list, l, h, cmp, context); - if (k == pivot) return list[k]; - if (k < pivot) - { - h = pivot - 1; - } - else - { - l = pivot + 1; - } - } - } - return SearchResult.MISSING?; -} - -macro @partition(Type list, isz l, isz h, CmpFn cmp, Context context) -{ - var $has_cmp = @is_valid_macro_slot(cmp); - var $has_context = @is_valid_macro_slot(context); - var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type)); - - ElementType pivot = list[l]; - while (l < h) - { - $switch - $case $cmp_by_value && $has_context: - while (cmp(list[h], pivot, context) >= 0 && l < h) h--; - if (l < h) list[l++] = list[h]; - while (cmp(list[l], pivot, context) <= 0 && l < h) l++; - $case $cmp_by_value: - while (cmp(list[h], pivot) >= 0 && l < h) h--; - if (l < h) list[l++] = list[h]; - while (cmp(list[l], pivot) <= 0 && l < h) l++; - $case $has_cmp && $has_context: - while (cmp(&list[h], &pivot, context) >= 0 && l < h) h--; - if (l < h) list[l++] = list[h]; - while (cmp(&list[l], &pivot, context) <= 0 && l < h) l++; - $case $has_cmp: - while (cmp(&list[h], &pivot) >= 0 && l < h) h--; - if (l < h) list[l++] = list[h]; - while (cmp(&list[l], &pivot) <= 0 && l < h) l++; - $default: - while (greater_eq(list[h], pivot) && l < h) h--; - if (l < h) list[l++] = list[h]; - while (less_eq(list[l], pivot) && l < h) l++; - $endswitch - if (l < h) list[h--] = list[l]; - } - list[l] = pivot; - - return l; -} diff --git a/lib7/std/sort/sort.c3 b/lib7/std/sort/sort.c3 deleted file mode 100644 index f4f7dfbd6..000000000 --- a/lib7/std/sort/sort.c3 +++ /dev/null @@ -1,63 +0,0 @@ -module std::sort; - -macro usz @len_from_list(&list) @deprecated -{ - return len_from_list(*list); -} - -macro usz len_from_list(list) -{ - $if $defined(list.len()): - return list.len(); - $else - return list.len; - $endif -} - -macro bool @is_sortable(#list) -{ - $switch - $case !$defined(#list[0]): - return false; - $case !$defined(#list.len): - return false; - $case @typekind(#list) == VECTOR || @typekind(#list) == ARRAY: - return false; - $case $defined(&#list[0]) &&& !types::is_same($typeof(&#list[0]), $typeof(#list[0])*): - return false; - $default: - return true; - $endswitch -} - -macro bool @is_valid_context(#cmp, #context) -{ - return @is_valid_macro_slot(#cmp) || @is_empty_macro_slot(#context); -} - -macro bool @is_valid_cmp_fn(#cmp, #list, #context) -{ - var $Type = $typeof(#cmp); - var $no_context = @is_empty_macro_slot(#context); - $switch - $case @is_empty_macro_slot(#cmp): return true; - $case $Type.kindof != FUNC ||| $Type.returns.kindof != SIGNED_INT: return false; - $case $defined(#cmp(#list[0], #list[0], #context)): return true; - $case $defined(#cmp(#list[0], #list[0])): return $no_context; - $case $defined(#cmp(&#list[0], &#list[0], #context)): return true; - $case $defined(#cmp(&#list[0], &#list[0])): return $no_context; - $default: return false; - $endswitch -} - -macro bool @is_cmp_key_fn(#key_fn, #list) -{ - $switch - $case @is_empty_macro_slot(#key_fn): return true; - $case $typeof(#key_fn).kindof != FUNC: return false; - $case $typeof(#key_fn).returns.kindof != UNSIGNED_INT: return false; - $case $defined(#key_fn(#list[0])): return true; - $case $defined(#key_fn(&&(#list[0]))): return true; - $default: return false; - $endswitch -} \ No newline at end of file diff --git a/lib7/std/sort/sorted.c3 b/lib7/std/sort/sorted.c3 deleted file mode 100644 index 40bc1f801..000000000 --- a/lib7/std/sort/sorted.c3 +++ /dev/null @@ -1,59 +0,0 @@ -module std::sort; - -<* -Returns true if list is sorted in either ascending or descending order. - @require @is_sortable(list) "The list must be indexable and support .len or .len()" - @require @is_valid_cmp_fn(cmp, list, ctx) "Expected a comparison function which compares values" - @require @is_valid_context(cmp, ctx) "Expected a valid context" -*> -macro bool is_sorted(list, cmp = EMPTY_MACRO_SLOT, ctx = EMPTY_MACRO_SLOT) @builtin -{ - var $Type = $typeof(list); - usz len = sort::len_from_list(list); - if (len <= 1) return true; - var check_sort = fn bool($Type list, usz start, usz end, $typeof(cmp) cmp, $typeof(ctx) ctx) - { - usz i; - int sort_order; - - // determine sort order (ascending or descending) - for (i = start; i < end && sort_order == 0; i++) - { - sort_order = @sort_cmp(list, i, cmp, ctx); - } - - // no sort order found, all elements are the same, consider list sorted - if (sort_order == 0) return true; - - // compare adjacent elements to the sort order - for (; i < end; i++) - { - if (sort_order * @sort_cmp(list, i, cmp, ctx) < 0) return false; - } - return true; - }; - return check_sort(list, 0, len - 1, cmp, ctx); -} - -macro int @sort_cmp(list, pos, cmp, ctx) @local -{ - var $has_cmp = @is_valid_macro_slot(cmp); - var $has_context = @is_valid_macro_slot(ctx); - var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom($typeof(cmp).paramsof[0].type)); - - var a = list[pos]; - var b = list[pos+1]; - - $switch - $case $cmp_by_value && $has_context: - return cmp(a, b); - $case $cmp_by_value: - return cmp(a, b); - $case $has_cmp && $has_context: - return cmp(&a, &b, ctx); - $case $has_cmp: - return cmp(&a, &b); - $default: - return compare_to(a,b); - $endswitch -} diff --git a/lib7/std/threads/buffered_channel.c3 b/lib7/std/threads/buffered_channel.c3 deleted file mode 100644 index 2d53c1f4d..000000000 --- a/lib7/std/threads/buffered_channel.c3 +++ /dev/null @@ -1,160 +0,0 @@ -module std::thread::channel{Type}; - -distinct BufferedChannel = void*; - -struct BufferedChannelImpl @private -{ - Allocator allocator; - Mutex mu; - bool closed; - usz size; - usz elems; - - usz sendx; - usz send_waiting; - ConditionVariable send_cond; - - usz readx; - usz read_waiting; - ConditionVariable read_cond; - - Type[?] buf; -} - -fn void! BufferedChannel.init(&self, Allocator allocator, usz size = 1) -{ - BufferedChannelImpl* channel = allocator::new_with_padding(allocator, BufferedChannelImpl, Type.sizeof * size)!; - defer catch allocator::free(allocator, channel); - - channel.allocator = allocator; - channel.size = size; - channel.elems = 0; - channel.sendx = 0; - channel.send_waiting = 0; - channel.readx = 0; - channel.read_waiting = 0; - - channel.mu.init()!; - defer catch (void)channel.mu.destroy(); - channel.send_cond.init()!; - defer catch (void)channel.send_cond.destroy(); - channel.read_cond.init()!; - defer catch (void)channel.read_cond.destroy(); - - *self = (BufferedChannel)channel; -} - -fn void! BufferedChannel.destroy(&self) -{ - BufferedChannelImpl* channel = (BufferedChannelImpl*)(*self); - - anyfault err = @catch(channel.mu.destroy()); - err = @catch(channel.send_cond.destroy()) ?: err; - err = @catch(channel.read_cond.destroy()) ?: err; - allocator::free(channel.allocator, channel); - - *self = null; - - if (err) return err?; -} - -fn void! BufferedChannel.push(self, Type val) -{ - BufferedChannelImpl* channel = (BufferedChannelImpl*)self; - - channel.mu.lock()!; - defer catch (void)channel.mu.unlock(); - - // if channel is full -> wait - while (channel.elems == channel.size && !channel.closed) - { - channel.send_waiting++; - channel.send_cond.wait(&channel.mu)!; - channel.send_waiting--; - } - - // check if channel is closed - if (channel.closed) return ThreadFault.CHANNEL_CLOSED?; - - // save value to buf - channel.buf[channel.sendx] = val; - - // move pointer - channel.sendx++; - if (channel.sendx == channel.size) - { - channel.sendx = 0; - } - - // channelge elems counter - channel.elems++; - - // if someone is waiting -> awake him - if (channel.read_waiting > 0) - { - channel.read_cond.signal()!; - } - - channel.mu.unlock()!; -} - -fn Type! BufferedChannel.pop(self) -{ - BufferedChannelImpl* channel = (BufferedChannelImpl*)self; - - channel.mu.lock()!; - defer catch (void)channel.mu.unlock(); - - // if chan is empty -> wait for sender - while (channel.elems == 0 && !channel.closed) - { - channel.read_waiting++; - channel.read_cond.wait(&channel.mu)!; - channel.read_waiting--; - } - - // check if chan is closed and empty - if (channel.closed && channel.elems == 0) - { - return ThreadFault.CHANNEL_CLOSED?; - } - - // read from buf - Type ret = channel.buf[channel.readx]; - - // move pointer - channel.readx++; - if (channel.readx == channel.size) - { - channel.readx = 0; - } - - // change elems counter - channel.elems--; - - // if someone is waiting -> awake him - if (channel.send_waiting > 0) - { - channel.send_cond.signal()!; - } - - channel.mu.unlock()!; - - return ret; -} - -fn void! BufferedChannel.close(self) -{ - BufferedChannelImpl* channel = (BufferedChannelImpl*)self; - - anyfault err = @catch(channel.mu.lock()); - - channel.closed = true; - - err = @catch(channel.read_cond.broadcast()) ?: err; - err = @catch(channel.send_cond.broadcast()) ?: err; - err = @catch(channel.mu.unlock()) ?: err; - - if (err) return err?; -} - diff --git a/lib7/std/threads/fixed_pool.c3 b/lib7/std/threads/fixed_pool.c3 deleted file mode 100644 index be000576b..000000000 --- a/lib7/std/threads/fixed_pool.c3 +++ /dev/null @@ -1,178 +0,0 @@ -module std::thread::threadpool @if (env::POSIX || env::WIN32); -import std::thread; - -// Please do not use this one in production. - -fault ThreadPoolResult -{ - QUEUE_FULL -} - -def ThreadPoolFn = fn void(any[] args); - -struct FixedThreadPool -{ - Mutex mu; - QueueItem[] queue; - usz qindex; - usz num_threads; - bitstruct : char { - bool initialized; - bool stop; - bool stop_now; - } - Thread[] pool; - ConditionVariable notify; -} - -struct QueueItem @private -{ - ThreadPoolFn func; - any[] args; -} - -<* - @require !self.initialized "ThreadPool must not be already initialized" - @require threads > 0 && threads < 0x1000 `Threads should be greater than 0 and less than 0x1000` - @require queue_size < 0x10000 `Queue size must be less than 65536` -*> -fn void! FixedThreadPool.init(&self, usz threads, usz queue_size = 0) -{ - if (queue_size == 0) queue_size = threads * 32; - defer catch @ok(self.destroy()); - assert(queue_size > 0); - *self = { - .num_threads = threads, - .initialized = true, - .queue = mem::alloc_array(QueueItem, queue_size), - .pool = mem::new_array(Thread, threads) - }; - self.mu.init()!; - self.notify.init()!; - foreach (&thread : self.pool) - { - thread.create(&process_work, self)!; - // The thread resources will be cleaned up when the thread exits. - thread.detach()!; - } -} - -<* - Stop all the threads and cleanup the pool. - Any pending work will be dropped. -*> -fn void! FixedThreadPool.destroy(&self) -{ - return self.@shutdown(stop_now); -} - -<* - Stop all the threads and cleanup the pool. - Any pending work will be processed. -*> -fn void! FixedThreadPool.stop_and_destroy(&self) -{ - return self.@shutdown(stop); -} - -macro void! FixedThreadPool.@shutdown(&self, #stop) @private -{ - if (self.initialized) - { - self.mu.lock()!; - self.#stop = true; - self.notify.broadcast()!; - self.mu.unlock()!; - // Wait for all threads to shutdown. - while (true) - { - self.mu.lock()!; - defer self.mu.unlock()!!; - if (self.num_threads == 0) - { - break; - } - self.notify.signal()!; - } - self.mu.destroy()!; - self.initialized = false; - while (self.qindex) - { - free_qitem(self.queue[--self.qindex]); - } - free(self.queue); - self.queue = {}; - } -} - -<* - Push a new job to the pool. - return Excuse if the queue is full, in which case the job is ignored. -*> -fn void! FixedThreadPool.push(&self, ThreadPoolFn func, args...) -{ - self.mu.lock()!; - defer self.mu.unlock()!!; - if (self.qindex == self.queue.len) return ThreadPoolResult.QUEUE_FULL?; - any[] data; - if (args.len) - { - data = mem::alloc_array(any, args.len); - foreach (i, arg : args) data[i] = allocator::clone_any(allocator::heap(), arg); - } - self.queue[self.qindex] = { .func = func, .args = data }; - self.qindex++; - defer catch - { - free_qitem(self.queue[--self.qindex]); - } - // Notify the threads that work is available. - self.notify.broadcast()!; -} - -fn int process_work(void* self_arg) @private -{ - FixedThreadPool* self = self_arg; - while (true) - { - self.mu.lock()!!; - if (self.stop_now) - { - // Shutdown requested. - self.num_threads--; - self.mu.unlock()!!; - return 0; - } - // Wait for work. - while (self.qindex == 0) - { - if (self.stop) - { - // Shutdown requested. - self.num_threads--; - self.mu.unlock()!!; - return 0; - } - self.notify.wait(&self.mu)!!; - if (self.stop_now) - { - // Shutdown requested. - self.num_threads--; - self.mu.unlock()!!; - return 0; - } - } - // Process the job. - self.qindex--; - QueueItem item = self.queue[self.qindex]; - self.mu.unlock()!!; - defer free_qitem(item); - item.func(item.args); - } -} - -fn void free_qitem(QueueItem item) @private -{ - foreach (arg : item.args) free(arg.ptr); - free(item.args); -} diff --git a/lib7/std/threads/os/cpu.c3 b/lib7/std/threads/os/cpu.c3 deleted file mode 100644 index d803dc827..000000000 --- a/lib7/std/threads/os/cpu.c3 +++ /dev/null @@ -1,69 +0,0 @@ -// https://www.cprogramming.com/snippets/source-code/find-the-number-of-cpu-cores-for-windows-mac-or-linux - -module std::thread::cpu @if(env::DARWIN); -import libc; - -const CTL_UNSPEC = 0; /* unused */ -const CTL_KERN = 1; /* "high kernel": proc, limits */ -const CTL_VM = 2; /* virtual memory */ -const CTL_VFS = 3; /* file system, mount type is next */ -const CTL_NET = 4; /* network, see socket.h */ -const CTL_DEBUG = 5; /* debugging parameters */ -const CTL_HW = 6; /* generic cpu/io */ -const CTL_MACHDEP = 7; /* machine dependent */ -const CTL_USER = 8; /* user-level */ -const CTL_MAXID = 9; /* number of valid top-level ids */ - -const HW_MACHINE = 1; /* string: machine class */ -const HW_MODEL = 2; /* string: specific machine model */ -const HW_NCPU = 3; /* int: number of cpus */ -const HW_BYTEORDER = 4; /* int: machine byte order */ -const HW_PHYSMEM = 5; /* int: total memory */ -const HW_USERMEM = 6; /* int: non-kernel memory */ -const HW_PAGESIZE = 7; /* int: software page size */ -const HW_DISKNAMES = 8; /* strings: disk drive names */ -const HW_DISKSTATS = 9; /* struct: diskstats[] */ -const HW_EPOCH = 10; /* int: 0 for Legacy, else NewWorld */ -const HW_FLOATINGPT = 11; /* int: has HW floating point? */ -const HW_MACHINE_ARCH = 12; /* string: machine architecture */ -const HW_VECTORUNIT = 13; /* int: has HW vector unit? */ -const HW_BUS_FREQ = 14; /* int: Bus Frequency */ -const HW_CPU_FREQ = 15; /* int: CPU Frequency */ -const HW_CACHELINE = 16; /* int: Cache Line Size in Bytes */ -const HW_L1ICACHESIZE = 17; /* int: L1 I Cache Size in Bytes */ -const HW_L1DCACHESIZE = 18; /* int: L1 D Cache Size in Bytes */ -const HW_L2SETTINGS = 19; /* int: L2 Cache Settings */ -const HW_L2CACHESIZE = 20; /* int: L2 Cache Size in Bytes */ -const HW_L3SETTINGS = 21; /* int: L3 Cache Settings */ -const HW_L3CACHESIZE = 22; /* int: L3 Cache Size in Bytes */ -const HW_MAXID = 23; /* number of valid hw ids */ - -fn uint native_cpu() -{ - int[2] nm; - usz len = 4; - uint count; - - nm = { CTL_HW, HW_NCPU }; - libc::sysctl(&nm, 2, &count, &len, null, 0); - if (count < 1) count = 1; - return count; -} - -module std::thread::cpu @if(env::LINUX); -import libc; - -fn uint native_cpu() -{ - return libc::get_nprocs_conf(); -} - -module std::thread::cpu @if(env::WIN32); -import libc; - -fn uint native_cpu() -{ - SystemInfo info; - libc::get_system_info(&info); - return info.dwNumberOfProcessors; -} \ No newline at end of file diff --git a/lib7/std/threads/os/thread_none.c3 b/lib7/std/threads/os/thread_none.c3 deleted file mode 100644 index 6b916ad74..000000000 --- a/lib7/std/threads/os/thread_none.c3 +++ /dev/null @@ -1,6 +0,0 @@ -module std::thread::os @if (!env::POSIX && !env::WIN32); - -distinct NativeMutex = int; -distinct NativeConditionVariable = int; -distinct NativeOnceFlag = int; -distinct NativeThread = int; \ No newline at end of file diff --git a/lib7/std/threads/os/thread_posix.c3 b/lib7/std/threads/os/thread_posix.c3 deleted file mode 100644 index 034498058..000000000 --- a/lib7/std/threads/os/thread_posix.c3 +++ /dev/null @@ -1,219 +0,0 @@ -module std::thread::os @if(env::POSIX); -import std::os::posix, std::time, libc; - -struct NativeMutex -{ - Pthread_mutex_t mutex; - bool initialized; -} - -def NativeConditionVariable = Pthread_cond_t; - -struct NativeThread -{ - inline Pthread_t pthread; - ThreadFn thread_fn; - void* arg; -} - -def NativeOnceFlag = Pthread_once_t; - -<* - @require !self.is_initialized() : "Mutex is already initialized" - @ensure self.is_initialized() -*> -fn void! NativeMutex.init(&self, MutexType type) -{ - Pthread_mutexattr_t attr; - if (posix::pthread_mutexattr_init(&attr)) return ThreadFault.INIT_FAILED?; - defer posix::pthread_mutexattr_destroy(&attr); - if (type & thread::MUTEX_RECURSIVE) - { - if (posix::pthread_mutexattr_settype(&attr, posix::PTHREAD_MUTEX_RECURSIVE)) return ThreadFault.INIT_FAILED?; - } - else - { - $if env::COMPILER_SAFE_MODE: - if (posix::pthread_mutexattr_settype(&attr, posix::PTHREAD_MUTEX_ERRORCHECK)) return ThreadFault.INIT_FAILED?; - $endif - } - if (posix::pthread_mutex_init(&self.mutex, &attr)) return ThreadFault.INIT_FAILED?; - self.initialized = true; -} - -fn bool NativeMutex.is_initialized(&self) -{ - return self.initialized; -} - -<* - @require self.is_initialized() : "Mutex was not initialized" - @ensure !self.is_initialized() -*> -fn void! NativeMutex.destroy(&self) -{ - if (posix::pthread_mutex_destroy(&self.mutex)) return ThreadFault.DESTROY_FAILED?; - *self = {}; -} - -<* - @require self.is_initialized() : "Mutex was not initialized" -*> -fn void! NativeMutex.lock(&self) -{ - if (posix::pthread_mutex_lock(&self.mutex)) return ThreadFault.LOCK_FAILED?; -} - -<* - @require self.is_initialized() : "Mutex was not initialized" -*> -fn void! NativeMutex.lock_timeout(&self, ulong ms) -{ - /* Try to acquire the lock and, if we fail, sleep for 5ms. */ - Errno result; - while ((result = posix::pthread_mutex_trylock(&self.mutex)) == errno::EBUSY) - { - if (!ms) break; - ulong sleep = min(5, ms); - if (!libc::nanosleep(&&time::ms(ms).to_timespec(), null)) return ThreadFault.LOCK_FAILED?; - ms -= sleep; - } - switch (result) - { - case errno::OK: - return; - case errno::EBUSY: - case errno::ETIMEDOUT: - return ThreadFault.LOCK_TIMEOUT?; - default: - return ThreadFault.LOCK_FAILED?; - } -} - -<* - @require self.is_initialized() : "Mutex was not initialized" -*> -fn bool NativeMutex.try_lock(&self) -{ - return !posix::pthread_mutex_trylock(&self.mutex); -} - -<* - @require self.is_initialized() : "Mutex was not initialized" -*> -fn void! NativeMutex.unlock(&self) -{ - if (posix::pthread_mutex_unlock(&self.mutex)) return ThreadFault.UNLOCK_FAILED?; -} - -fn void! NativeConditionVariable.init(&cond) -{ - if (posix::pthread_cond_init(cond, null)) return ThreadFault.INIT_FAILED?; -} - -fn void! NativeConditionVariable.destroy(&cond) -{ - if (posix::pthread_cond_destroy(cond)) return ThreadFault.DESTROY_FAILED?; -} - -fn void! NativeConditionVariable.signal(&cond) -{ - if (posix::pthread_cond_signal(cond)) return ThreadFault.SIGNAL_FAILED?; -} - -fn void! NativeConditionVariable.broadcast(&cond) -{ - if (posix::pthread_cond_broadcast(cond)) return ThreadFault.SIGNAL_FAILED?; -} - -<* - @require mtx.is_initialized() -*> -fn void! NativeConditionVariable.wait(&cond, NativeMutex* mtx) -{ - if (posix::pthread_cond_wait(cond, &mtx.mutex)) return ThreadFault.WAIT_FAILED?; -} - -<* - @require mtx.is_initialized() -*> -fn void! NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms) -{ - TimeSpec now; - if (libc::timespec_get(&now, libc::TIME_UTC) != libc::TIME_UTC) return ThreadFault.WAIT_FAILED?; - now.ns += (CLong)((ms % 1000) * 1000_000); - now.s += (Time_t)(ms / 1000 + now.ns / 1000_000_000); - now.ns = now.ns % 1000_000_000; - switch (posix::pthread_cond_timedwait(cond, &mtx.mutex, &now)) - { - case errno::ETIMEDOUT: - return ThreadFault.WAIT_TIMEOUT?; - case errno::OK: - return; - default: - return ThreadFault.WAIT_FAILED?; - } -} - -tlocal NativeThread current_thread @private; - -fn void* callback(void* arg) @private -{ - NativeThread* thread = arg; - current_thread = *thread; - return (void*)(iptr)thread.thread_fn(thread.arg); -} - -fn void! NativeThread.create(&thread, ThreadFn thread_fn, void* arg) -{ - thread.thread_fn = thread_fn; - thread.arg = arg; - if (posix::pthread_create(&thread.pthread, null, &callback, thread) != 0) - { - return ThreadFault.INIT_FAILED?; - } -} - -fn void! NativeThread.detach(thread) -{ - if (posix::pthread_detach(thread.pthread)) return ThreadFault.DETACH_FAILED?; -} - -fn void native_thread_exit(int result) -{ - posix::pthread_exit((void*)(iptr)result); -} - -fn NativeThread native_thread_current() -{ - return current_thread; -} - -fn bool NativeThread.equals(thread, NativeThread other) -{ - return (bool)posix::pthread_equal(thread.pthread, other.pthread); -} - -fn int! NativeThread.join(thread) -{ - void *pres; - if (posix::pthread_join(thread.pthread, &pres)) return ThreadFault.JOIN_FAILED?; - return (int)(iptr)pres; -} - -fn void NativeOnceFlag.call_once(&flag, OnceFn func) -{ - posix::pthread_once(flag, func); -} - -fn void native_thread_yield() -{ - posix::sched_yield(); -} - -fn void! native_sleep_nano(NanoDuration nano) -{ - if (nano <= 0) return; - if (libc::nanosleep(&&nano.to_timespec(), null)) return ThreadFault.INTERRUPTED?; -} - diff --git a/lib7/std/threads/os/thread_win32.c3 b/lib7/std/threads/os/thread_win32.c3 deleted file mode 100644 index 94c265413..000000000 --- a/lib7/std/threads/os/thread_win32.c3 +++ /dev/null @@ -1,321 +0,0 @@ -module std::thread::os @if(env::WIN32); -import std::os::win32, std::time; - -distinct NativeThread = inline Win32_HANDLE; - -struct NativeMutex -{ - union - { - Win32_CRITICAL_SECTION critical_section; - Win32_HANDLE handle; - } - // Size is less than a Win32_HANDLE so due to alignment - // there is no benefit to pack these into a bitstruct. - uint locks; - bool recursive; - bool timed; -} - -struct NativeOnceFlag -{ - int status; - Win32_CRITICAL_SECTION lock; -} - -struct NativeConditionVariable -{ - union - { - struct - { - Win32_HANDLE event_one; - Win32_HANDLE event_all; - } - Win32_HANDLE[2] events; - } - uint waiters_count; - Win32_CRITICAL_SECTION waiters_count_lock; -} - -fn void! NativeMutex.init(&mtx, MutexType type) -{ - mtx.locks = 0; - mtx.recursive = (bool)(type & thread::MUTEX_RECURSIVE); - mtx.timed = (bool)(type & thread::MUTEX_TIMED); - if (!mtx.timed) - { - win32::initializeCriticalSection(&(mtx.critical_section)); - return; - } - if (!(mtx.handle = win32::createMutex(null, 0, null))) return ThreadFault.INIT_FAILED?; -} - -fn void! NativeMutex.destroy(&mtx) -{ - mtx.locks = 0; - if (!mtx.timed) - { - win32::deleteCriticalSection(&mtx.critical_section); - return; - } - if (!win32::closeHandle(mtx.handle)) return ThreadFault.DESTROY_FAILED?; -} - -fn void! NativeMutex.lock(&mtx) -{ - if (!mtx.timed) - { - win32::enterCriticalSection(&mtx.critical_section); - } - else - { - switch (win32::waitForSingleObject(mtx.handle, win32::INFINITE)) - { - case win32::WAIT_OBJECT_0: - break; - case win32::WAIT_ABANDONED: - default: - return ThreadFault.LOCK_FAILED?; - - } - } - if (!mtx.recursive && mtx.locks) - { - return ThreadFault.LOCK_FAILED?; - } - mtx.locks++; -} - - -<* - @require mtx.timed "Only available for timed locks" -*> -fn void! NativeMutex.lock_timeout(&mtx, ulong ms) -{ - if (ms > uint.max) ms = uint.max; - switch (win32::waitForSingleObject(mtx.handle, (uint)ms)) - { - case win32::WAIT_OBJECT_0: - break; - case win32::WAIT_TIMEOUT: - return ThreadFault.LOCK_TIMEOUT?; - case win32::WAIT_ABANDONED: - default: - return ThreadFault.LOCK_FAILED?; - } - if (!mtx.recursive && mtx.locks) - { - return ThreadFault.LOCK_FAILED?; - } - mtx.locks++; -} - -fn bool NativeMutex.try_lock(&mtx) -{ - bool success = mtx.timed - ? win32::waitForSingleObject(mtx.handle, 0) == win32::WAIT_OBJECT_0 - : (bool)win32::tryEnterCriticalSection(&mtx.critical_section); - - if (!success) return false; - if (!mtx.recursive) - { - if (mtx.locks) - { - if (mtx.timed) - { - win32::releaseMutex(mtx.handle); - } - else - { - win32::leaveCriticalSection(&mtx.critical_section); - } - return false; - } - } - mtx.locks++; - return true; -} - -fn void! NativeMutex.unlock(&mtx) -{ - if (!mtx.locks) return ThreadFault.UNLOCK_FAILED?; - mtx.locks--; - if (!mtx.timed) - { - win32::leaveCriticalSection(&mtx.critical_section); - return; - } - if (!win32::releaseMutex(mtx.handle)) return ThreadFault.UNLOCK_FAILED?; -} - -const int CONDITION_EVENT_ONE = 0; -const int CONDITION_EVENT_ALL = 1; - -fn void! NativeConditionVariable.init(&cond) -{ - cond.waiters_count = 0; - win32::initializeCriticalSection(&cond.waiters_count_lock); - cond.event_one = win32::createEventA(null, 0, 0, null); - if (!cond.event_one) - { - cond.event_all = (Win32_HANDLE)0; - return ThreadFault.INIT_FAILED?; - } - cond.event_all = win32::createEventA(null, 1, 0, null); - if (!cond.event_all) - { - win32::closeHandle(cond.event_one); - cond.event_one = (Win32_HANDLE)0; - return ThreadFault.INIT_FAILED?; - } -} - -fn void! NativeConditionVariable.destroy(&cond) @maydiscard -{ - if (cond.event_one) win32::closeHandle(cond.event_one); - if (cond.event_all) win32::closeHandle(cond.event_all); - win32::deleteCriticalSection(&cond.waiters_count_lock); -} - -fn void! NativeConditionVariable.signal(&cond) -{ - win32::enterCriticalSection(&cond.waiters_count_lock); - bool have_waiters = cond.waiters_count > 0; - win32::leaveCriticalSection(&cond.waiters_count_lock); - if (have_waiters && !win32::setEvent(cond.event_one)) return ThreadFault.SIGNAL_FAILED?; -} - -fn void! NativeConditionVariable.broadcast(&cond) -{ - win32::enterCriticalSection(&cond.waiters_count_lock); - bool have_waiters = cond.waiters_count > 0; - win32::leaveCriticalSection(&cond.waiters_count_lock); - if (have_waiters && !win32::setEvent(cond.event_all)) return ThreadFault.SIGNAL_FAILED?; -} - -fn void! timedwait(NativeConditionVariable* cond, NativeMutex* mtx, uint timeout) @private -{ - win32::enterCriticalSection(&cond.waiters_count_lock); - cond.waiters_count++; - win32::leaveCriticalSection(&cond.waiters_count_lock); - - mtx.unlock()!; - - uint result = win32::waitForMultipleObjects(2, &cond.events, 0, timeout); - switch (result) - { - case win32::WAIT_TIMEOUT: - mtx.lock()!; - return ThreadFault.WAIT_TIMEOUT?; - case win32::WAIT_FAILED: - mtx.lock()!; - return ThreadFault.WAIT_FAILED?; - default: - break; - } - - win32::enterCriticalSection(&cond.waiters_count_lock); - cond.waiters_count--; - // If event all && no waiters - bool last_waiter = result == 1 && !cond.waiters_count; - win32::leaveCriticalSection(&cond.waiters_count_lock); - - if (last_waiter) - { - if (!win32::resetEvent(cond.event_all)) - { - mtx.lock()!; - return ThreadFault.WAIT_FAILED?; - } - } - - mtx.lock()!; -} - -fn void! NativeConditionVariable.wait(&cond, NativeMutex* mtx) @inline -{ - return timedwait(cond, mtx, win32::INFINITE) @inline; -} - -fn void! NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms) @inline -{ - if (ms > uint.max) ms = uint.max; - return timedwait(cond, mtx, (uint)ms) @inline; -} - -fn void! NativeThread.create(&thread, ThreadFn func, void* args) -{ - if (!(*thread = (NativeThread)win32::createThread(null, 0, func, args, 0, null))) return ThreadFault.INIT_FAILED?; -} - -fn void! NativeThread.detach(thread) @inline -{ - if (!win32::closeHandle(thread)) return ThreadFault.DETACH_FAILED?; -} - - -fn void native_thread_exit(int result) @inline -{ - win32::exitThread((uint)result); -} - -fn void native_thread_yield() -{ - win32::sleep(0); -} - -fn void NativeOnceFlag.call_once(&flag, OnceFn func) -{ - while (@volatile_load(flag.status) < 3) - { - switch (@volatile_load(flag.status)) - { - case 0: - if (mem::compare_exchange_volatile(&flag.status, 1, 0, AtomicOrdering.SEQ_CONSISTENT, AtomicOrdering.SEQ_CONSISTENT) == 0) - { - win32::initializeCriticalSection(&flag.lock); - win32::enterCriticalSection(&flag.lock); - @volatile_store(flag.status, 2); - func(); - @volatile_store(flag.status, 3); - win32::leaveCriticalSection(&flag.lock); - return; - } - break; - case 1: - break; - case 2: - win32::enterCriticalSection(&flag.lock); - win32::leaveCriticalSection(&flag.lock); - break; - } - } -} - -fn int! NativeThread.join(thread) -{ - uint res; - if (win32::waitForSingleObject(thread, win32::INFINITE) == win32::WAIT_FAILED) return ThreadFault.JOIN_FAILED?; - if (!win32::getExitCodeThread(thread, &res)) return ThreadFault.JOIN_FAILED?; - defer win32::closeHandle(thread); - return res; -} - -fn NativeThread native_thread_current() -{ - return (NativeThread)win32::getCurrentThread(); -} - -fn bool NativeThread.equals(thread, NativeThread other) -{ - return win32::getThreadId(thread) == win32::getThreadId(other); -} - -fn void! native_sleep_nano(NanoDuration ns) -{ - long ms = ns.to_ms(); - if (ms <= 0) return; - if (ms > Win32_DWORD.max) ms = Win32_DWORD.max; - if (win32::sleepEx((Win32_DWORD)ms, (Win32_BOOL)true) == win32::WAIT_IO_COMPLETION) return ThreadFault.INTERRUPTED?; -} diff --git a/lib7/std/threads/pool.c3 b/lib7/std/threads/pool.c3 deleted file mode 100644 index 55afb6c07..000000000 --- a/lib7/std/threads/pool.c3 +++ /dev/null @@ -1,137 +0,0 @@ -module std::thread::pool{SIZE}; -import std::thread; - -struct ThreadPool -{ - Mutex mu; - QueueItem[SIZE] queue; - usz qindex; - usz num_threads; - bitstruct : char { - bool initialized; - bool stop; - bool stop_now; - } - - Thread[SIZE] pool; - ConditionVariable notify; -} - -struct QueueItem @private -{ - ThreadFn func; - void* arg; -} - -<* - @require !self.initialized "ThreadPool must not be already initialized" -*> -fn void! ThreadPool.init(&self) -{ - defer catch @ok(self.destroy()); - *self = { .num_threads = SIZE, .initialized = true }; - self.mu.init()!; - self.notify.init()!; - foreach (&thread : self.pool) - { - thread.create(&process_work, self)!; - // The thread resources will be cleaned up when the thread exits. - thread.detach()!; - } -} - -<* - Stop all the threads and cleanup the pool. - Any pending work will be dropped. -*> -fn void! ThreadPool.destroy(&self) -{ - return self.@shutdown(stop_now); -} - -<* - Stop all the threads and cleanup the pool. - Any pending work will be processed. -*> -fn void! ThreadPool.stop_and_destroy(&self) -{ - return self.@shutdown(stop); -} - -macro void! ThreadPool.@shutdown(&self, #stop) @private -{ - if (self.initialized) - { - self.mu.lock()!; - self.#stop = true; - self.notify.broadcast()!; - self.mu.unlock()!; - // Wait for all threads to shutdown. - while (true) - { - self.mu.lock()!; - defer self.mu.unlock()!!; - if (self.num_threads == 0) - { - break; - } - self.notify.signal()!; - } - self.mu.destroy()!; - self.initialized = false; - } -} - -<* - Push a new job to the pool. - Returns whether the queue is full, in which case the job is ignored. -*> -fn void! ThreadPool.push(&self, ThreadFn func, void* arg) -{ - while (true) - { - self.mu.lock()!; - defer self.mu.unlock()!!; - if (self.qindex < SIZE) - { - self.queue[self.qindex] = { .func = func, .arg = arg }; - self.qindex++; - // Notify the threads that work is available. - self.notify.broadcast()!; - return; - } - } -} - -fn int process_work(void* arg) @private -{ - ThreadPool* self = arg; - while (true) - { - self.mu.lock()!!; - // Wait for work. - while (self.qindex == 0) - { - if (self.stop) - { - // Shutdown requested. - self.num_threads--; - self.mu.unlock()!!; - return 0; - } - self.notify.wait(&self.mu)!!; - if (self.stop_now) - { - // Shutdown requested. - self.num_threads--; - self.mu.unlock()!!; - return 0; - } - } - // Process the job. - self.qindex--; - QueueItem item = self.queue[self.qindex]; - self.mu.unlock()!!; - item.func(item.arg); - } -} \ No newline at end of file diff --git a/lib7/std/threads/thread.c3 b/lib7/std/threads/thread.c3 deleted file mode 100644 index 2cb2424d1..000000000 --- a/lib7/std/threads/thread.c3 +++ /dev/null @@ -1,80 +0,0 @@ -module std::thread; -import std::thread::os; -import std::time; - -distinct MutexType = int; - -const MutexType MUTEX_PLAIN = 0; -const MutexType MUTEX_TIMED = 1; -const MutexType MUTEX_RECURSIVE = 2; - -distinct Mutex = NativeMutex; -distinct TimedMutex = inline Mutex; -distinct RecursiveMutex = inline Mutex; -distinct TimedRecursiveMutex = inline Mutex; -distinct ConditionVariable = NativeConditionVariable; -distinct Thread = inline NativeThread; -distinct OnceFlag = NativeOnceFlag; -def OnceFn = fn void(); - -def ThreadFn = fn int(void* arg); - -fault ThreadFault -{ - INIT_FAILED, - DESTROY_FAILED, - LOCK_FAILED, - LOCK_TIMEOUT, - UNLOCK_FAILED, - SIGNAL_FAILED, - WAIT_FAILED, - WAIT_TIMEOUT, - DETACH_FAILED, - JOIN_FAILED, - INTERRUPTED, - CHANNEL_CLOSED, -} - -macro void! Mutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_PLAIN); -macro void! TimedMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_TIMED); -macro void! RecursiveMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_RECURSIVE); -macro void! TimedRecursiveMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_TIMED | MUTEX_RECURSIVE); -macro void! Mutex.destroy(&mutex) => NativeMutex.destroy((NativeMutex*)mutex); -macro void! Mutex.lock(&mutex) => NativeMutex.lock((NativeMutex*)mutex); -macro void! TimedMutex.lock_timeout(&mutex, ulong ms) => NativeMutex.lock_timeout((NativeMutex*)mutex, ms); -macro void! TimedRecursiveMutex.lock_timeout(&mutex, ulong ms) => NativeMutex.lock_timeout((NativeMutex*)mutex, ms); -macro bool Mutex.try_lock(&mutex) => NativeMutex.try_lock((NativeMutex*)mutex); -macro void! Mutex.unlock(&mutex) => NativeMutex.unlock((NativeMutex*)mutex); -macro void Mutex.@in_lock(&mutex; @body) -{ - (void)mutex.lock(); - defer (void)mutex.unlock(); - @body(); -} - -macro void! ConditionVariable.init(&cond) => NativeConditionVariable.init((NativeConditionVariable*)cond); -macro void! ConditionVariable.destroy(&cond) => NativeConditionVariable.destroy((NativeConditionVariable*)cond); -macro void! ConditionVariable.signal(&cond) => NativeConditionVariable.signal((NativeConditionVariable*)cond); -macro void! ConditionVariable.broadcast(&cond) => NativeConditionVariable.broadcast((NativeConditionVariable*)cond); -macro void! ConditionVariable.wait(&cond, Mutex* mutex) -{ - return NativeConditionVariable.wait((NativeConditionVariable*)cond, (NativeMutex*)mutex); -} -macro void! ConditionVariable.wait_timeout(&cond, Mutex* mutex, ulong ms) -{ - return NativeConditionVariable.wait_timeout((NativeConditionVariable*)cond, (NativeMutex*)mutex, ms); -} - -macro void! Thread.create(&thread, ThreadFn thread_fn, void* arg) => NativeThread.create(thread, thread_fn, arg); -macro void! Thread.detach(thread) => NativeThread.detach(thread); -macro int! Thread.join(thread) => NativeThread.join(thread); -macro bool Thread.equals(thread, Thread other) => NativeThread.equals(thread, other); - -macro void OnceFlag.call(&flag, OnceFn func) => NativeOnceFlag.call_once((NativeOnceFlag*)flag, func); - -macro void yield() => os::native_thread_yield(); -macro Thread current() => (Thread)os::native_thread_current(); -macro void exit(int result) => os::native_thread_exit(result); -macro void! sleep(Duration d) @maydiscard => os::native_sleep_nano(d.to_nano()); -macro void! sleep_ms(ulong ms) @maydiscard => sleep(time::ms(ms)); -macro void! sleep_ns(NanoDuration ns) @maydiscard => os::native_sleep_nano(ns); diff --git a/lib7/std/threads/unbuffered_channel.c3 b/lib7/std/threads/unbuffered_channel.c3 deleted file mode 100644 index a17bf748d..000000000 --- a/lib7/std/threads/unbuffered_channel.c3 +++ /dev/null @@ -1,138 +0,0 @@ -module std::thread::channel{Type}; - -distinct UnbufferedChannel = void*; - -struct UnbufferedChannelImpl @private -{ - Allocator allocator; - Mutex mu; - Type buf; - bool closed; - - Mutex send_mu; - usz send_waiting; - ConditionVariable send_cond; - - Mutex read_mu; - usz read_waiting; - ConditionVariable read_cond; -} - -fn void! UnbufferedChannel.init(&self, Allocator allocator) -{ - UnbufferedChannelImpl* channel = allocator::alloc(allocator, UnbufferedChannelImpl); - defer catch (void)allocator::free(allocator, channel); - - channel.allocator = allocator; - channel.send_waiting = 0; - channel.read_waiting = 0; - - channel.mu.init()!; - defer catch (void)channel.mu.destroy(); - channel.send_mu.init()!; - defer catch (void)channel.send_mu.destroy(); - channel.send_cond.init()!; - defer catch (void)channel.send_cond.destroy(); - channel.read_mu.init()!; - defer catch (void)channel.read_mu.destroy(); - channel.read_cond.init()!; - - *self = (UnbufferedChannel)channel; -} - -fn void! UnbufferedChannel.destroy(&self) -{ - UnbufferedChannelImpl* channel = (UnbufferedChannelImpl*)(*self); - - anyfault err = @catch(channel.mu.destroy()); - err = @catch(channel.send_mu.destroy()) ?: err; - err = @catch(channel.send_cond.destroy()) ?: err; - err = @catch(channel.read_mu.destroy()) ?: err; - err = @catch(channel.read_cond.destroy()) ?: err; - allocator::free(channel.allocator, channel); - - if (err) return err?; - - *self = null; -} - -fn void! UnbufferedChannel.push(self, Type val) -{ - UnbufferedChannelImpl* channel = (UnbufferedChannelImpl*)self; - - channel.mu.lock()!; - defer catch (void)channel.mu.unlock(); - channel.send_mu.lock()!; - defer catch (void)channel.send_mu.unlock(); - - if (channel.closed) - { - return ThreadFault.CHANNEL_CLOSED?; - } - - // store value in the buffer - channel.buf = val; - // show that we are waiting for reader - channel.send_waiting++; - - // if reader is already waiting for us -> awake him - if (channel.read_waiting > 0) - { - channel.read_cond.signal()!; - } - - // wait until reader takes value from buffer - channel.send_cond.wait(&channel.mu)!; - - if (channel.closed) return ThreadFault.CHANNEL_CLOSED?; - - channel.mu.unlock()!; - channel.send_mu.unlock()!; -} - -fn Type! UnbufferedChannel.pop(self) -{ - UnbufferedChannelImpl* channel = (UnbufferedChannelImpl*)self; - - channel.mu.lock()!; - defer catch (void)channel.mu.unlock(); - channel.read_mu.lock()!; - defer catch (void)channel.read_mu.unlock(); - - // if no one is waiting, then there is nothing in the buffer - while (!channel.closed && channel.send_waiting == 0) - { - channel.read_waiting++; - channel.read_cond.wait(&channel.mu)!; - channel.read_waiting--; - } - - if (channel.closed) return ThreadFault.CHANNEL_CLOSED?; - - // take value from buffer - Type ret = channel.buf; - - // awake sender - channel.send_waiting--; - channel.send_cond.signal()!; - - channel.mu.unlock()!; - channel.read_mu.unlock()!; - - return ret; -} - -fn void! UnbufferedChannel.close(self) -{ - UnbufferedChannelImpl* channel = (UnbufferedChannelImpl*)self; - - anyfault err = @catch(channel.mu.lock()); - - channel.closed = true; - - err = @catch(channel.read_cond.broadcast()) ?: err; - err = @catch(channel.send_cond.broadcast()) ?: err; - err = @catch(channel.mu.unlock()) ?: err; - - if (err) return err?; -} \ No newline at end of file diff --git a/lib7/std/time/clock.c3 b/lib7/std/time/clock.c3 deleted file mode 100644 index e247f2a53..000000000 --- a/lib7/std/time/clock.c3 +++ /dev/null @@ -1,34 +0,0 @@ -module std::time::clock; -import std::time::os; - -fn Clock now() -{ - $if $defined(os::native_clock): - return os::native_clock(); - $else - unreachable("Clock unsupported"); - $endif -} - -fn NanoDuration Clock.mark(&self) -{ - Clock mark = now(); - NanoDuration diff = (NanoDuration)(mark - *self); - *self = mark; - return diff; -} - -fn Clock Clock.add_nano_duration(self, NanoDuration nano) -{ - return (Clock)((NanoDuration)self + nano); -} - -fn Clock Clock.add_duration(self, Duration duration) -{ - return self.add_nano_duration(duration.to_nano()); -} - -fn NanoDuration Clock.to_now(self) -{ - return (NanoDuration)(now() - self); -} \ No newline at end of file diff --git a/lib7/std/time/datetime.c3 b/lib7/std/time/datetime.c3 deleted file mode 100644 index 30dbdf5e9..000000000 --- a/lib7/std/time/datetime.c3 +++ /dev/null @@ -1,254 +0,0 @@ -module std::time::datetime @if(env::LIBC); -import libc; - -fn DateTime now() -{ - return from_time(time::now()); -} - -<* - @require day >= 1 && day < 32 - @require hour >= 0 && hour < 24 - @require min >= 0 && min < 60 - @require sec >= 0 && sec < 60 - @require us >= 0 && us <= 999_999 -*> -fn DateTime from_date(int year, Month month = JANUARY, int day = 1, int hour = 0, int min = 0, int sec = 0, int us = 0) -{ - DateTime dt @noinit; - dt.set_date(year, month, day, hour, min, sec, us) @inline; - return dt; -} - -<* - @require day >= 1 && day < 32 - @require hour >= 0 && hour < 24 - @require min >= 0 && min < 60 - @require sec >= 0 && sec < 60 - @require us >= 0 && us <= 999_999 - @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600 -*> -fn TzDateTime from_date_tz(int year, Month month = JANUARY, int day = 1, int hour = 0, int min = 0, int sec = 0, int us = 0, int gmt_offset = 0) -{ - return from_date(year, month, day, hour, min, sec, us).with_gmt_offset(gmt_offset); -} - -fn TzDateTime DateTime.to_local(&self) -{ - Tm tm @noinit; - Time_t time_t = (Time_t)self.time.to_seconds(); - libc::localtime_r(&time_t, &tm); - TzDateTime dt; - dt.usec = (int)((long)self.time % (long)time::SEC); - dt.sec = (char)tm.tm_sec; - dt.min = (char)tm.tm_min; - dt.hour = (char)tm.tm_hour; - dt.day = (char)tm.tm_mday; - dt.month = Month.from_ordinal(tm.tm_mon); - dt.year = tm.tm_year + 1900; - dt.weekday = !tm.tm_wday ? Weekday.SUNDAY : Weekday.from_ordinal(tm.tm_wday - 1); - dt.year_day = (ushort)tm.tm_yday; - dt.time = self.time; - $if $defined(tm.tm_gmtoff): - dt.gmt_offset = (int)tm.tm_gmtoff; - $else - $assert $defined(libc::_get_timezone); - CLong timezone; - libc::_get_timezone(&timezone); - dt.gmt_offset = (int)-timezone; - $endif - return dt; -} - -<* - Update timestamp to gmt_offset while keeping the date and time - values unchanged. - - @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600 -*> -fn TzDateTime DateTime.with_gmt_offset(self, int gmt_offset) -{ - TzDateTime dt = { self, 0 }; - return dt.with_gmt_offset(gmt_offset); -} - -<* - Update timestamp to gmt_offset while keeping the date and time - values unchanged. - - @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600 -*> -fn TzDateTime TzDateTime.with_gmt_offset(self, int gmt_offset) -{ - self.time -= (Time)(gmt_offset - self.gmt_offset) * (Time)time::SEC; - return { self.date_time, gmt_offset }; -} - -<* - Update the date and time values to gmt_offset while keeping the - timestamp unchanged. - - @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600 - @ensure self.time == return.time -*> -fn TzDateTime DateTime.to_gmt_offset(self, int gmt_offset) -{ - TzDateTime dt = { self, 0 }; - return dt.to_gmt_offset(gmt_offset); -} - -<* - Update the date and time values to gmt_offset while keeping the - timestamp unchanged. - - @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600 - @ensure self.time == return.time -*> -fn TzDateTime TzDateTime.to_gmt_offset(self, int gmt_offset) { - if (self.gmt_offset == gmt_offset) return self; - Time time = self.time + (Time)gmt_offset * (Time)time::SEC; - DateTime dt = from_time(time); - dt.time = self.time; - return { dt, gmt_offset }; -} - -<* - @require day >= 1 && day < 32 - @require hour >= 0 && hour < 24 - @require min >= 0 && min <= 60 - @require sec >= 0 && sec < 60 - @require us >= 0 && us <= 999_999 -*> -fn void DateTime.set_date(&self, int year, Month month = JANUARY, int day = 1, int hour = 0, int min = 0, int sec = 0, int us = 0) -{ - Tm tm; - tm.tm_sec = sec; - tm.tm_min = min; - tm.tm_hour = hour; - tm.tm_mon = month.ordinal; - tm.tm_mday = day; - tm.tm_year = year - 1900; - Time_t time = libc::timegm(&tm); - self.set_time((Time)(time * (long)time::SEC + us)); -} - -fn void DateTime.set_time(&self, Time time) -{ - Tm tm @noinit; - Time_t time_t = (Time_t)time.to_seconds(); - libc::gmtime_r(&time_t, &tm); - self.usec = (int)((long)time % (long)time::SEC); - self.sec = (char)tm.tm_sec; - self.min = (char)tm.tm_min; - self.hour = (char)tm.tm_hour; - self.day = (char)tm.tm_mday; - self.month = Month.from_ordinal(tm.tm_mon); - self.year = tm.tm_year + 1900; - self.weekday = !tm.tm_wday ? Weekday.SUNDAY : Weekday.from_ordinal(tm.tm_wday - 1); - self.year_day = (ushort)tm.tm_yday; - self.time = time; -} - -fn DateTime DateTime.add_seconds(&self, int seconds) => from_time(self.time.add_seconds(seconds)); -fn DateTime DateTime.add_minutes(&self, int minutes) => from_time(self.time.add_minutes(minutes)); -fn DateTime DateTime.add_hours(&self, int hours) => from_time(self.time.add_hours(hours)); -fn DateTime DateTime.add_days(&self, int days) => from_time(self.time.add_days(days)); -fn DateTime DateTime.add_weeks(&self, int weeks) => from_time(self.time.add_weeks(weeks)); - -fn DateTime DateTime.add_years(&self, int years) -{ - if (!years) return *self; - return from_date(self.year + years, self.month, self.day, self.hour, self.min, self.sec, self.usec); -} - -fn DateTime DateTime.add_months(&self, int months) -{ - if (months == 0) return *self; - int year = self.year; - int month = self.month.ordinal; - switch - { - case months % 12 == 0: - year += months / 12; - case months < 0: - month += months % 12; - year += months / 12; - if (month < 0) - { - year--; - month += 12; - } - default: - month += months; - year += month / 12; - month %= 12; - } - return from_date(year, Month.from_ordinal(month), self.day, self.hour, self.min, self.sec, self.usec); -} - - -fn TzDateTime TzDateTime.add_seconds(&self, int seconds) => self.date_time.add_seconds(seconds).to_gmt_offset(self.gmt_offset); -fn TzDateTime TzDateTime.add_minutes(&self, int minutes) => self.date_time.add_minutes(minutes).to_gmt_offset(self.gmt_offset); -fn TzDateTime TzDateTime.add_hours(&self, int hours) => self.date_time.add_hours(hours).to_gmt_offset(self.gmt_offset); -fn TzDateTime TzDateTime.add_days(&self, int days) => self.date_time.add_days(days).to_gmt_offset(self.gmt_offset); -fn TzDateTime TzDateTime.add_weeks(&self, int weeks) => self.date_time.add_weeks(weeks).to_gmt_offset(self.gmt_offset); - -fn TzDateTime TzDateTime.add_years(&self, int years) => self.date_time.add_years(years).with_gmt_offset(self.gmt_offset); -fn TzDateTime TzDateTime.add_months(&self, int months) => self.date_time.add_months(months).with_gmt_offset(self.gmt_offset); - -fn DateTime from_time(Time time) -{ - DateTime dt @noinit; - dt.set_time(time) @inline; - return dt; -} - -<* - @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600 - @ensure time == return.time -*> -fn TzDateTime from_time_tz(Time time, int gmt_offset) -{ - return from_time(time).to_gmt_offset(gmt_offset); -} - -fn Time DateTime.to_time(&self) @inline -{ - return self.time; -} - -fn bool DateTime.after(&self, DateTime compare) @inline -{ - return self.time > compare.time; -} - -fn bool DateTime.before(&self, DateTime compare) @inline -{ - return self.time < compare.time; -} - -fn int DateTime.compare_to(&self, DateTime compare) -{ - return compare_to(self.time, compare.time); -} - -fn int DateTime.diff_years(&self, DateTime from) -{ - int year_diff = self.year - from.year; - switch - { - case year_diff < 0: return -from.diff_years(*self); - case year_diff == 0: return 0; - } - if (self.year_day < from.year_day) year_diff--; - return year_diff; -} - -fn double DateTime.diff_sec(self, DateTime from) -{ - return (double)self.time.diff_us(from.time) / (double)time::SEC; -} -fn Duration DateTime.diff_us(self, DateTime from) -{ - return self.time.diff_us(from.time); -} diff --git a/lib7/std/time/format.c3 b/lib7/std/time/format.c3 deleted file mode 100644 index 41ec56b6f..000000000 --- a/lib7/std/time/format.c3 +++ /dev/null @@ -1,90 +0,0 @@ -module std::time::datetime @if(env::LIBC); - - -enum DateTimeFormat -{ - ANSIC, // "Mon Jan _2 15:04:05 2006" - UNIXDATE, // "Mon Jan _2 15:04:05 GMT 2006" - RUBYDATE, // "Mon Jan 02 15:04:05 -0700 2006" - RFC822, // "02 Jan 06 15:04 GMT" - RFC822Z, // "02 Jan 06 15:04 -0700" - RFC850, // "Monday, 02-Jan-06 15:04:05 GMT" - RFC1123, // "Mon, 02 Jan 2006 15:04:05 GMT" - RFC1123Z, // "Mon, 02 Jan 2006 15:04:05 -0700" - RFC3339, // "2006-01-02T15:04:05Z" - RFC3339Z, // "2006-01-02T15:04:05+07:00" - RFC3339MS, // "2006-01-02T15:04:05.999999Z" - RFC3339ZMS, // "2006-01-02T15:04:05.999999+07:00" - DATETIME, // "2006-01-02 15:04:05" - DATEONLY, // "2006-01-02" - TIMEONLY, // "15:04:05" -} - -fn String format(Allocator allocator, DateTimeFormat type, TzDateTime dt) -{ - switch (type) - { - case ANSIC: - return string::format(allocator, "%s %s %2d %02d:%02d:%02d %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, dt.year); - case UNIXDATE: - return string::format(allocator, "%s %s %2d %02d:%02d:%02d GMT %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, dt.year); - case RUBYDATE: - return string::format(allocator, "%s %s %2d %02d:%02d:%02d %s %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix(dt.gmt_offset), dt.year); - case RFC822: - dt = dt.to_gmt_offset(0); // For named representations of the timezone we always go for GMT, which is required by some RFCs - return string::format(allocator, "%02d %s %02d %02d:%02d GMT", dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min); - case RFC822Z: - return string::format(allocator, "%02d %s %02d %02d:%02d %s", dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min, temp_numeric_tzsuffix(dt.gmt_offset)); - case RFC850: - dt = dt.to_gmt_offset(0); // For named representations of the timezone we always go for GMT, which is required by some RFCs - return string::format(allocator, "%s, %02d-%s-%02d %02d:%02d:%02d GMT", dt.weekday.name, dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min, dt.sec); - case RFC1123: - dt = dt.to_gmt_offset(0); // For named representations of the timezone we always go for GMT, which is required by some RFCs - return string::format(allocator, "%s, %02d %s %d %02d:%02d:%02d GMT", dt.weekday.abbrev, dt.day, dt.month.abbrev, dt.year, dt.hour, dt.min, dt.sec); - case RFC1123Z: - return string::format(allocator, "%s, %02d %s %d %02d:%02d:%02d %s", dt.weekday.abbrev, dt.day, dt.month.abbrev, dt.year, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix(dt.gmt_offset)); - case RFC3339: - dt = dt.to_gmt_offset(0); - return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02dZ", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec); - case RFC3339Z: - return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d%s", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix_colon(dt.gmt_offset)); - case RFC3339MS: - dt = dt.to_gmt_offset(0); - return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d.%dZ", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec); - case RFC3339ZMS: - return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d.%d%s", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec, temp_numeric_tzsuffix_colon(dt.gmt_offset)); - case DATETIME: - return string::format(allocator, "%04d-%02d-%02d %02d:%02d:%02d", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec); - case DATEONLY: - return string::format(allocator, "%04d-%02d-%02d", dt.year, dt.month + 1, dt.day); - case TIMEONLY: - return string::format(allocator, "%02d:%02d:%02d", dt.hour, dt.min, dt.sec); - } -} - -fn String tformat(DateTimeFormat dt_format, TzDateTime dt) => format(tmem(), dt_format, dt); - -fn String TzDateTime.format(self, Allocator allocator, DateTimeFormat dt_format) => format(allocator, dt_format, self); - -// .with_gmt_offset(0) instead of .to_local() is used to avoid surprises when user is formatting to a representation without a timezone -fn String DateTime.format(self, Allocator allocator, DateTimeFormat dt_format) => format(allocator, dt_format, self.with_gmt_offset(0)); - -<* - Returns the timezone offset in the format of "+HHMM" or "-HHMM" - @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600 -*> -fn String temp_numeric_tzsuffix(int gmt_offset) @private @inline -{ - if (gmt_offset == 0) return "-0000"; - return string::tformat("%+03d%02d", gmt_offset / 3600, (gmt_offset % 3600) / 60); -} - -<* - Returns the timezone offset in the format of "+HH:MM" or "-HH:MM" - @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600 -*> -fn String temp_numeric_tzsuffix_colon(int gmt_offset) @private @inline -{ - if (gmt_offset == 0) return "-00:00"; - return string::tformat("%+03d:%02d", gmt_offset / 3600, (gmt_offset % 3600) / 60); -} \ No newline at end of file diff --git a/lib7/std/time/os/time_darwin.c3 b/lib7/std/time/os/time_darwin.c3 deleted file mode 100644 index 049640e43..000000000 --- a/lib7/std/time/os/time_darwin.c3 +++ /dev/null @@ -1,23 +0,0 @@ -module std::time::os @if(env::DARWIN); - -struct Darwin_mach_timebase_info -{ - uint numer; - uint denom; -} - -def Darwin_mach_timebase_info_t = Darwin_mach_timebase_info; -def Darwin_mach_timebase_info_data_t = Darwin_mach_timebase_info; - -extern fn void mach_timebase_info(Darwin_mach_timebase_info_data_t* timebase); -extern fn ulong mach_absolute_time(); - -fn Clock native_clock() -{ - static Darwin_mach_timebase_info_data_t timebase; - if (!timebase.denom) - { - mach_timebase_info(&timebase); - } - return (Clock)(mach_absolute_time() * timebase.numer / timebase.denom); -} diff --git a/lib7/std/time/os/time_posix.c3 b/lib7/std/time/os/time_posix.c3 deleted file mode 100644 index c8431de31..000000000 --- a/lib7/std/time/os/time_posix.c3 +++ /dev/null @@ -1,82 +0,0 @@ -module std::time::os @if(env::POSIX); -import libc; - -extern fn CInt clock_gettime(int type, TimeSpec *time); - -fn Time native_timestamp() -{ - TimeSpec ts; - clock_gettime(CLOCK_REALTIME, &ts); - return (Time)(ts.s * 1_000_000i64 + ts.ns / 1_000i64); -} - -fn Clock native_clock() @if(!env::DARWIN) -{ - TimeSpec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (Clock)((ulong)ts.s * 1_000_000_000u64 + (ulong)ts.ns); -} - -module std::time::os @if(env::OPENBSD); -const CLOCK_REALTIME = 0; -const CLOCK_PROCESS_CPUTIME_ID = 2; -const CLOCK_MONOTONIC = 3; -const CLOCK_THREAD_CPUTIME_ID = 4; -const CLOCK_UPTIME = 5; -const CLOCK_BOOTTIME = 6; - -module std::time::os @if(env::FREEBSD); -const CLOCK_REALTIME = 0; -const CLOCK_VIRTUAL = 1; -const CLOCK_PROF = 2; -const CLOCK_MONOTONIC = 4; -const CLOCK_UPTIME = 5; -const CLOCK_UPTIME_PRECISE = 7; -const CLOCK_UPTIME_FAST = 8; -const CLOCK_REALTIME_PRECISE = 9; -const CLOCK_REALTIME_FAST = 10; -const CLOCK_MONOTONIC_PRECISE = 11; -const CLOCK_MONOTONIC_FAST = 12; -const CLOCK_SECOND = 13; -const CLOCK_THREAD_CPUTIME_ID = 14; -const CLOCK_PROCESS_CPUTIME_ID = 15; -const CLOCK_BOOTTIME = CLOCK_UPTIME; -const CLOCK_REALTIME_COARSE = CLOCK_REALTIME_FAST; -const CLOCK_MONOTONIC_COARSE = CLOCK_MONOTONIC_FAST; - -module std::time::os @if(env::NETBSD); -const CLOCK_REALTIME = 0; -const CLOCK_VIRTUAL = 1; -const CLOCK_PROF = 2; -const CLOCK_MONOTONIC = 3; -const CLOCK_THREAD_CPUTIME_ID = 0x20000000; -const CLOCK_PROCESS_CPUTIME_ID = 0x40000000; - -module std::time::os @if(env::WASI); -// Not implemented -const CLOCK_REALTIME = 0; -const CLOCK_MONOTONIC = 0; - -module std::time::os @if(env::DARWIN); -const CLOCK_REALTIME = 0; -const CLOCK_MONOTONIC = 6; -const CLOCK_MONOTONIC_RAW = 4; -const CLOCK_MONOTONIC_RAW_APPROX = 5; -const CLOCK_UPTIME_RAW = 8; -const CLOCK_UPTIME_RAW_APPROX = 9; -const CLOCK_PROCESS_CPUTIME_ID = 12; -const CLOCK_THREAD_CPUTIME_ID = 16; - -module std::time::os @if(env::LINUX); -const CLOCK_REALTIME = 0; -const CLOCK_MONOTONIC = 1; -const CLOCK_PROCESS_CPUTIME_ID = 2; -const CLOCK_THREAD_CPUTIME_ID = 3; -const CLOCK_MONOTONIC_RAW = 4; -const CLOCK_REALTIME_COARSE = 5; -const CLOCK_MONOTONIC_COARSE = 6; -const CLOCK_BOOTTIME = 7; -const CLOCK_REALTIME_ALARM = 8; -const CLOCK_BOOTTIME_ALARM = 9; -const CLOCK_TAI = 11; - diff --git a/lib7/std/time/os/time_win32.c3 b/lib7/std/time/os/time_win32.c3 deleted file mode 100644 index 998d9dcdb..000000000 --- a/lib7/std/time/os/time_win32.c3 +++ /dev/null @@ -1,33 +0,0 @@ -module std::time::os @if(env::WIN32); -import std::os::win32; -import std::math; - -extern fn void win32_GetSystemTimeAsFileTime(Win32_FILETIME* time) @extern("GetSystemTimeAsFileTime"); -extern fn Win32_BOOL win32_QueryPerformanceFrequency(Win32_LARGE_INTEGER* lpFrequency) @extern("QueryPerformanceFrequency"); -extern fn Win32_BOOL win32_QueryPerformanceCounter(Win32_LARGE_INTEGER* lpPerformanceCount) @extern("QueryPerformanceCounter"); - -const ulong WINDOWS_TICK_US @local = 10; -const ulong WIN_TO_UNIX_EPOCH_US @local = 116444736000000000u64 / WINDOWS_TICK_US; - - -fn Clock native_clock() -{ - static Win32_LARGE_INTEGER freq; - static ulong div = 0; - ulong mult = 0; - if (!freq.quadPart) - { - if (!win32_QueryPerformanceFrequency(&freq)) return 0; - } - Win32_LARGE_INTEGER counter @noinit; - if (!win32_QueryPerformanceCounter(&counter)) return 0; - return (Clock)counter.quadPart.muldiv(1_000_000_000, freq.quadPart); -} - -fn Time native_timestamp() -{ - Win32_FILETIME ft @noinit; - win32_GetSystemTimeAsFileTime(&ft); - ulong result = (ulong)ft.dwHighDateTime << 32 | ft.dwLowDateTime; - return (Time)(result / WINDOWS_TICK_US - WIN_TO_UNIX_EPOCH_US); -} \ No newline at end of file diff --git a/lib7/std/time/time.c3 b/lib7/std/time/time.c3 deleted file mode 100644 index a15218212..000000000 --- a/lib7/std/time/time.c3 +++ /dev/null @@ -1,175 +0,0 @@ -module std::time; -import std::io, std::time::os; - -distinct Time = long; -distinct Duration = long; -distinct Clock = ulong; -distinct NanoDuration (Printable) = long; - -const Time FAR_FUTURE = long.max; -const Time FAR_PAST = long.min; -const Duration US = 1; -const Duration MS = 1_000; -const Duration SEC = 1_000_000; -const Duration MIN = 60 * SEC; -const Duration HOUR = 60 * MIN; -const Duration DAY = 24 * HOUR; -const Duration WEEK = 7 * DAY; -const Duration MONTH = 30 * DAY; -const Duration YEAR = 36525 * DAY / 100; -const Duration FOREVER = long.max; - -fn Duration us(long l) @inline => (Duration)l * US; -fn Duration ms(long l) @inline => (Duration)l * MS; -fn Duration sec(long l) @inline => (Duration)l * SEC; -fn Duration min(long l) @inline => (Duration)l * MIN; -fn Duration hour(long l) @inline => (Duration)l * HOUR; -fn Duration from_float(double s) @inline => (Duration)(s * (double)SEC); - -struct DateTime -{ - int usec; - char sec; - char min; - char hour; - char day; - Month month; - Weekday weekday; - int year; - ushort year_day; - Time time; -} - -struct TzDateTime -{ - inline DateTime date_time; - int gmt_offset; -} - -enum Weekday : char (String name, String abbrev) -{ - MONDAY = { "Monday", "Mon" }, - TUESDAY = { "Tuesday", "Tue" }, - WEDNESDAY = { "Wednesday", "Wed" }, - THURSDAY = { "Thursday", "Thu" }, - FRIDAY = { "Friday", "Fri" }, - SATURDAY = { "Saturday", "Sat" }, - SUNDAY = { "Sunday", "Sun" }, -} - -enum Month : char (String name, String abbrev, int days, bool leap) -{ - JANUARY = { "January", "Jan", 31, false }, - FEBRUARY = { "February", "Feb", 28, true }, - MARCH = { "March", "Mar", 31, false }, - APRIL = { "April", "Apr", 30, false }, - MAY = { "May", "May", 31, false }, - JUNE = { "June", "Jun", 30, false }, - JULY = { "July", "Jul", 31, false }, - AUGUST = { "August", "Aug", 31, false }, - SEPTEMBER = { "September", "Sep", 30, false }, - OCTOBER = { "October", "Oct", 31, false }, - NOVEMBER = { "November", "Nov", 30, false }, - DECEMBER = { "December", "Dec", 31, false } -} - -fn Time now() -{ - $if $defined(native_timestamp): - return os::native_timestamp(); - $else - return 0; - $endif -} - -fn Time Time.add_seconds(time, long seconds) => time + (Time)(seconds * (long)SEC); -fn Time Time.add_minutes(time, long minutes) => time + (Time)(minutes * (long)MIN); -fn Time Time.add_hours(time, long hours) => time + (Time)(hours * (long)HOUR); -fn Time Time.add_days(time, long days) => time + (Time)(days * (long)DAY); -fn Time Time.add_weeks(time, long weeks) => time + (Time)(weeks * (long)WEEK); -fn Time Time.add_duration(time, Duration duration) => time + (Time)duration; - -fn int Time.compare_to(time, Time other) -{ - if (time == other) return 0; - return time > other ? 1 : -1; -} - -fn double Time.to_seconds(time) => (long)time / (double)SEC; -fn Duration Time.diff_us(time, Time other) => (Duration)(time - other); -fn double Time.diff_sec(time, Time other) => (long)time.diff_us(other) / (double)SEC; -fn double Time.diff_min(time, Time other) => (long)time.diff_us(other) / (double)MIN; -fn double Time.diff_hour(time, Time other) => (long)time.diff_us(other) / (double)HOUR; -fn double Time.diff_days(time, Time other) => (long)time.diff_us(other) / (double)DAY; -fn double Time.diff_weeks(time, Time other) => (long)time.diff_us(other) / (double)WEEK; - -fn double NanoDuration.to_sec(nd) => (double)nd / 1_000_000_000.0; -fn long NanoDuration.to_ms(nd) => (long)nd / 1_000_000; -fn Duration NanoDuration.to_duration(nd) => (Duration)nd / 1_000; -fn NanoDuration Duration.to_nano(td) => (NanoDuration)td * 1_000; -fn long Duration.to_ms(td) => (long)td / 1_000; - -fn usz! NanoDuration.to_format(&self, Formatter* formatter) @dynamic -{ - NanoDuration nd = *self; - if (nd == 0) - { - return formatter.printf("0s")!; - } - - bool neg = nd < 0; - if (neg) nd = -nd; - - DString str = dstring::temp_with_capacity(64); - if (nd < 1_000_000_000) - { - // Less than 1s: print milliseconds, microseconds and nanoseconds. - NanoDuration ms = nd / 1_000_000; - if (ms > 0) - { - str.appendf("%dms", ms); - nd -= ms * 1_000_000; - } - NanoDuration us = nd / 1000; - if (us > 0) - { - str.appendf("%dµs", us); - nd -= us * 1000; - } - if (nd > 0) - { - str.appendf("%dns", nd); - } - } - else - { - // More than 1s: print hours, minutes and seconds. - NanoDuration ms = (nd - nd / 1_000_000_000 * 1_000_000_000) / 1_000_000; - - nd /= 1_000_000_000; - NanoDuration hour = nd / 3600; - if (hour > 0) - { - str.appendf("%dh", hour); - nd -= hour * 3600; - } - NanoDuration min = nd / 60; - if (min > 0) - { - str.appendf("%dm", min); - nd -= min * 60; - } - NanoDuration sec = nd; - if (ms > 0) - { - // Ignore trailing zeroes. - while (ms / 10 * 10 == ms) ms /= 10; - str.appendf("%d.%ds", sec, ms); - } - else - { - str.appendf("%ds", sec); - } - } - return formatter.printf(str.str_view())!; -} \ No newline at end of file diff --git a/releasenotes.md b/releasenotes.md index 040ab9e58..c0f62b31f 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -1,5 +1,20 @@ # C3C Release Notes +## 0.7.0 Change list + +### Changes / improvements +- Removed `Foo { 1, 2 }` initializer. +- Changed `Foo()` to `Foo {int}`. +- Removed `{| |}` expression blocks. +- Removed macro `&ref` and `$varef` parameters. +- Removed `$vaexpr(0)` syntax in favour of `$vaexpr[0]` +- Enum does not cast to/from an integer (its ordinal). +- Removed use of `void!` for main, test and benchmark functions. +- Removed `$or`, `$and`, `$concat` compile time functions. +- Removed `@adhoc` attribute. +- Disallow inline use of nested generics (e.g. `List{List{int}}`. +- Remove `.allocator = allocator` syntax for functions. + ## 0.6.8 Change list ### Changes / improvements diff --git a/resources/examples/contextfree/boolerr.c3 b/resources/examples/contextfree/boolerr.c3 index a581e697b..164fe2bd6 100644 --- a/resources/examples/contextfree/boolerr.c3 +++ b/resources/examples/contextfree/boolerr.c3 @@ -3,10 +3,10 @@ import libc; import std::io; import std::collections::maybe; -def MaybeString = Maybe(); -def MaybeHead = Maybe(); -def new_head_val = maybe::value(); -def new_string_val = maybe::value(); +def MaybeString = Maybe{String}; +def MaybeHead = Maybe{Head}; +def new_head_val = maybe::value{Head}; +def new_string_val = maybe::value{String}; fault TitleResult { diff --git a/resources/examples/map.c3 b/resources/examples/map.c3 index 764a54857..2118aeb11 100644 --- a/resources/examples/map.c3 +++ b/resources/examples/map.c3 @@ -1,4 +1,4 @@ -module std::container::map(); +module std::container::map{Key, Type}; import std::core::builtin; import std::io; fault MapResult diff --git a/resources/grammar/c3.l b/resources/grammar/c3.l index 8609a1d99..b6f86518a 100644 --- a/resources/grammar/c3.l +++ b/resources/grammar/c3.l @@ -249,8 +249,6 @@ b64\`{B64}\` { return(BYTES); } "=>" { return(IMPLIES); } "[<" { return(LVEC); } ">]" { return(RVEC); } -"(<" { return(LGENPAR); } -">)" { return(RGENPAR); } "$$" { return(BUILTIN); } ";" { return(';'); } ("{") { return('{'); } @@ -276,8 +274,6 @@ b64\`{B64}\` { return(BYTES); } "^" { return('^'); } "|" { return('|'); } "?" { return('?'); } -"{|" { return(LBRAPIPE); } -"|}" { return(RBRAPIPE); } [ \t\v\n\f] { } . { /* ignore bad characters */ } diff --git a/resources/grammar/grammar.y b/resources/grammar/grammar.y index ae26b3797..c1b289aaf 100644 --- a/resources/grammar/grammar.y +++ b/resources/grammar/grammar.y @@ -18,7 +18,6 @@ void yyerror(YYLTYPE * yylloc_param , yyscan_t yyscanner, const char *yymsgp); %token CT_AND_OP CT_OR_OP CT_CONCAT_OP CT_EXEC %token INC_OP DEC_OP SHL_OP SHR_OP LE_OP GE_OP EQ_OP NE_OP %token AND_OP OR_OP MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN -%token LGENPAR RGENPAR %token SUB_ASSIGN SHL_ASSIGN SHR_ASSIGN AND_ASSIGN %token XOR_ASSIGN OR_ASSIGN VAR NUL ELVIS NEXTCASE ANYFAULT %token MODULE IMPORT DEF EXTERN @@ -36,7 +35,7 @@ void yyerror(YYLTYPE * yylloc_param , yyscan_t yyscanner, const char *yymsgp); %token CT_SIZEOF CT_STRINGIFY CT_QNAMEOF CT_OFFSETOF CT_VAEXPR CT_FEATURE %token CT_EXTNAMEOF CT_EVAL CT_DEFINED CT_ALIGNOF ASSERT %token ASM CHAR_LITERAL REAL TRUE FALSE CT_CONST_IDENT -%token LBRAPIPE RBRAPIPE HASH_CONST_IDENT CT_ASSIGNABLE CT_AND CT_IS_CONST +%token HASH_CONST_IDENT CT_ASSIGNABLE CT_AND CT_IS_CONST %start translation_unit %% @@ -119,10 +118,6 @@ bytes_expr | bytes_expr BYTES ; -expr_block - : LBRAPIPE opt_stmt_list RBRAPIPE - ; - base_expr : string_expr | INTEGER @@ -141,11 +136,9 @@ base_expr_assignable | path ident_expr | ident_expr | local_ident_expr - | type initializer_list | type '.' access_ident | type '.' CONST_IDENT | '(' expr ')' - | expr_block | ct_call '(' flat_path ')' | ct_vaarg '[' expr ']' | ct_analyse '(' expression_list ')' @@ -159,6 +152,7 @@ base_expr_assignable primary_expr : base_expr | initializer_list + | base_expr generic_expr ; range_loc @@ -194,13 +188,16 @@ access_ident | TYPEID ; +opt_compound_statement + : /* Empty */ + | compound_statement + ; + call_trailing : '[' range_loc ']' | '[' range_expr ']' - | call_invocation - | call_invocation compound_statement + | call_invocation opt_compound_statement | '.' access_ident - | generic_expr | INC_OP | DEC_OP | '!' @@ -517,7 +514,7 @@ enum_param_decl | INLINE type IDENT ; -base_type +base_type_no_user_defined : VOID | BOOL | CHAR @@ -542,26 +539,42 @@ base_type | ANYFAULT | ANY | TYPEID - | TYPE_IDENT opt_generic_parameters - | path TYPE_IDENT opt_generic_parameters | CT_TYPE_IDENT | CT_TYPEOF '(' expr ')' | CT_TYPEFROM '(' constant_expr ')' | CT_VATYPE '[' constant_expr ']' | CT_EVALTYPE '(' constant_expr ')' ; +base_type + : base_type_no_user_defined + | TYPE_IDENT opt_generic_parameters + | path TYPE_IDENT opt_generic_parameters + ; +base_type_no_generics + : base_type_no_user_defined + | TYPE_IDENT + | path TYPE_IDENT + ; + +type_no_generics + : base_type_no_generics + | type_no_generics type_suffix + ; + +type_suffix + : '*' + | '[' constant_expr ']' + | '[' ']' + | '[' '*' ']' + | '[' '?' ']' + | LVEC constant_expr RVEC + | LVEC '*' RVEC + | LVEC '?' RVEC + ; type : base_type - | type '*' - | type '[' constant_expr ']' - | type '[' ']' - | type '[' '*' ']' - | type '[' '?' ']' - | type LVEC constant_expr RVEC - | type LVEC '*' RVEC - | type LVEC '?' RVEC - ; + | type type_suffix; optional_type : type @@ -932,7 +945,7 @@ ct_echo_stmt ; bitstruct_declaration - : BITSTRUCT TYPE_IDENT opt_interface_impl ':' type opt_attributes bitstruct_body + : BITSTRUCT TYPE_IDENT opt_interface_impl ':' type_no_generics opt_attributes bitstruct_body ; bitstruct_body @@ -1042,19 +1055,19 @@ struct_member_decl : type identifier_list opt_attributes ';' | struct_or_union IDENT opt_attributes struct_body | struct_or_union opt_attributes struct_body - | BITSTRUCT ':' type opt_attributes bitstruct_body - | BITSTRUCT IDENT ':' type opt_attributes bitstruct_body + | BITSTRUCT ':' type_no_generics opt_attributes bitstruct_body + | BITSTRUCT IDENT ':' type_no_generics opt_attributes bitstruct_body | INLINE type IDENT opt_attributes ';' | INLINE type opt_attributes ';' ; enum_spec - : ':' base_type '(' enum_params ')' - | ':' INLINE base_type '(' enum_params ')' - | ':' base_type - | ':' INLINE base_type - | ':' '(' enum_params ')' + : ':' base_type_no_generics '(' enum_params ')' + | ':' INLINE base_type_no_generics '(' enum_params ')' + | ':' base_type_no_generics + | ':' INLINE base_type_no_generics + | ':' '(' enum_params ')' ; enum_declaration @@ -1181,7 +1194,7 @@ define_attribute ; generic_expr - : LGENPAR generic_parameters RGENPAR + : '{' generic_parameters '}' ; opt_generic_parameters @@ -1237,7 +1250,7 @@ module_params module : MODULE path_ident opt_attributes ';' - | MODULE path_ident LGENPAR module_params RGENPAR opt_attributes ';' + | MODULE path_ident '{' module_params '}' opt_attributes ';' ; import_paths diff --git a/resources/testfragments/demo1.c3 b/resources/testfragments/demo1.c3 index 967160da6..b3fc51529 100644 --- a/resources/testfragments/demo1.c3 +++ b/resources/testfragments/demo1.c3 @@ -117,6 +117,7 @@ fn void testArrays() printf("Pointer to array: %p\nPointer to slice: %p\nPointer to first element of slice: %p\n", z, &y, &y[0]); } +/* fn void testExpressionBlocks() { int x = {| @@ -142,10 +143,11 @@ fn void testExpressionBlocks() puts("In if"); } } +*/ fn void main() { testStruct(); testArrays(); - testExpressionBlocks(); +// testExpressionBlocks(); } diff --git a/resources/testfragments/diagnostic.c3 b/resources/testfragments/diagnostic.c3 index 175203e87..db0f8ed15 100644 --- a/resources/testfragments/diagnostic.c3 +++ b/resources/testfragments/diagnostic.c3 @@ -4,7 +4,7 @@ struct Overalign double[<33>] x; } -def Olist = List(); +def Olist = List{Overalign}; fn void main() { diff --git a/src/build/build.h b/src/build/build.h index 6c5790d40..7ff1fb33c 100644 --- a/src/build/build.h +++ b/src/build/build.h @@ -225,12 +225,6 @@ typedef enum SHOW_BACKTRACE_ON = 1 } ShowBacktrace; -typedef enum -{ - OLD_TEST_NOT_SET = -1, - OLD_TEST_OFF = 0, - OLD_TEST_ON = 1 -} OldTest; typedef enum { @@ -327,11 +321,6 @@ typedef enum WIN_CRT_STATIC_DEBUG = 4, } WinCrtLinking; -typedef enum -{ - VECTOR_CONV_DEFAULT = 0, - VECTOR_CONV_OLD = 1, -} VectorConv; typedef enum { @@ -468,8 +457,6 @@ typedef struct BuildOptions_ const char* linker_libs[MAX_BUILD_LIB_DIRS]; size_t linker_lib_count; const char* std_lib_dir; - VectorConv vector_conv; - bool enable_new_generics; struct { const char *sdk; @@ -535,7 +522,6 @@ typedef struct BuildOptions_ OptimizationSetting optsetting; DebugInfo debug_info_override; ShowBacktrace show_backtrace; - OldTest old_test; ArchOsTarget arch_os_target_override; SafetyLevel safety_level; PanicLevel panic_level; @@ -686,8 +672,6 @@ typedef struct TrustLevel trust_level; OptimizationSetting optsetting; OptimizationLevel optlevel; - VectorConv vector_conv; - bool enable_new_generics; MemoryEnvironment memory_environment; SizeOptimizationLevel optsize; SingleModule single_module; @@ -696,7 +680,6 @@ typedef struct EmitStdlib emit_stdlib; LinkLibc link_libc; ShowBacktrace show_backtrace; - OldTest old_test; StripUnused strip_unused; DebugInfo debug_info; MergeFunctions merge_functions; @@ -781,7 +764,6 @@ static BuildTarget default_build_target = { .arch_os_target = ARCH_OS_TARGET_DEFAULT, .debug_info = DEBUG_INFO_NOT_SET, .show_backtrace = SHOW_BACKTRACE_NOT_SET, - .old_test = OLD_TEST_NOT_SET, .use_stdlib = USE_STDLIB_NOT_SET, .link_libc = LINK_LIBC_NOT_SET, .emit_stdlib = EMIT_STDLIB_NOT_SET, diff --git a/src/build/build_internal.h b/src/build/build_internal.h index 7af6ad0ca..ef56168d3 100644 --- a/src/build/build_internal.h +++ b/src/build/build_internal.h @@ -44,11 +44,6 @@ static const char *wincrt_linking[5] = { [WIN_CRT_STATIC_DEBUG] = "static-debug", }; -static const char *vector_conv[2] = { - [VECTOR_CONV_DEFAULT] = "default", - [VECTOR_CONV_OLD] = "old", -}; - static const char *optsizes[3] = { [SIZE_OPTIMIZATION_NONE] = "none", [SIZE_OPTIMIZATION_SMALL] = "small", diff --git a/src/build/build_options.c b/src/build/build_options.c index bb6ae32c5..0bea7e55b 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -126,7 +126,6 @@ static void usage(bool full) print_opt("--single-module=", "Compile all modules together, enables more inlining."); print_opt("--show-backtrace=", "Show detailed backtrace on segfaults."); print_opt("--lsp", "Emit data about errors suitable for a LSP."); - print_opt("--old-test-bench=", "Allow benchmarks and tests to use the deprecated 'void!' returns."); } PRINTF(""); print_opt("-g", "Emit debug info."); @@ -198,8 +197,6 @@ static void usage(bool full) print_opt("--linux-crt ", "Set the directory to use for finding crt1.o and related files."); print_opt("--linux-crtbegin ", "Set the directory to use for finding crtbegin.o and related files."); PRINTF(""); - print_opt("--enable-new-generics", "Enable Foo{int} generics, this will disable the old Foo { ... } initializers."); - print_opt("--vector-conv=