First 0.7 update, removing all deprecated features.

This commit is contained in:
Christoffer Lerno
2025-02-27 14:16:36 +01:00
committed by Christoffer Lerno
parent cff6697818
commit 2a895ec7be
1589 changed files with 2635 additions and 115363 deletions

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2023 Eduardo José Gómez Hernández. All rights reserved. // Copyright (c) 2023 Eduardo José Gómez Hernández. All rights reserved.
// Use of this source code is governed by the MIT license // Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file. // a copy of which can be found in the LICENSE_STDLIB file.
module std::atomic::types(<Type>); module std::atomic::types{Type};
struct Atomic struct Atomic
{ {

View File

@@ -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 // Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file. // a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::anylist; 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 [&inout] allocator "The allocator to use"
@param initial_capacity "The initial capacity to reserve" @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; 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. 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) 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 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. 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. Pop the last value and allocate the copy using the given allocator.
@return! IteratorResult.NO_MORE_ELEMENT @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?; if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]); defer self.free_element(self.entries[self.size]);
return allocator::clone_any(allocator, 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 Pop the last value and allocate the copy using the temp allocator
@return! IteratorResult.NO_MORE_ELEMENT @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() 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]; 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()) 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. Same as temp_pop() but pops the first value instead.
*> *>
fn any! AnyList.tcopy_pop_first(&self) => self.copy_pop_first(allocator::temp()); fn any! AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem());
<*
Same as temp_pop() but pops the first value instead.
@deprecated `use tcopy_pop_first`
*>
fn any! AnyList.temp_pop_first(&self) => self.new_pop_first(allocator::temp());
<* <*
@require index < self.size @require index < self.size
@@ -477,7 +413,7 @@ fn void AnyList.reserve(&self, usz min_capacity)
{ {
if (!min_capacity) return; if (!min_capacity) return;
if (self.capacity >= 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); min_capacity = math::next_power_of_2(min_capacity);
self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity); self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity);
self.capacity = min_capacity; self.capacity = min_capacity;

View File

@@ -1,7 +1,7 @@
<* <*
@require SIZE > 0 @require SIZE > 0
*> *>
module std::collections::bitset(<SIZE>); module std::collections::bitset{SIZE};
def Type = uint; def Type = uint;
@@ -70,28 +70,18 @@ fn void BitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline
<* <*
@require Type.kindof == UNSIGNED_INT @require Type.kindof == UNSIGNED_INT
*> *>
module std::collections::growablebitset(<Type>); module std::collections::growablebitset{Type};
import std::collections::list; import std::collections::list;
const BITS = Type.sizeof * 8; const BITS = Type.sizeof * 8;
def GrowableBitSetList = List(<Type>); def GrowableBitSetList = List{Type};
struct GrowableBitSet struct GrowableBitSet
{ {
GrowableBitSetList data; 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 initial_capacity
@param [&inout] allocator "The allocator to use, defaults to the heap allocator" @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; 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) 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) fn void GrowableBitSet.free(&self)

View File

@@ -4,7 +4,7 @@
<* <*
@require MAX_SIZE >= 1 `The size must be at least 1 element big.` @require MAX_SIZE >= 1 `The size must be at least 1 element big.`
*> *>
module std::collections::elastic_array(<Type, MAX_SIZE>); module std::collections::elastic_array{Type, MAX_SIZE};
import std::io, std::math, std::collections::list_common; import std::io, std::math, std::collections::list_common;
def ElementPredicate = fn bool(Type *type); 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) fn String ElasticArray.to_tstring(&self)
{ {
return string::tformat("%s", *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. IMPORTANT The returned array must be freed using free_aligned.
*> *>
fn Type[] ElasticArray.to_aligned_array(&self, Allocator allocator) fn Type[] ElasticArray.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[] ElasticArray.to_new_array(&self)
{
return list_common::list_to_array(Type, self, allocator::heap());
} }
<* <*
@@ -189,15 +163,15 @@ macro Type[] ElasticArray.to_new_array(&self)
*> *>
macro Type[] ElasticArray.to_array(&self, Allocator allocator) 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) fn Type[] ElasticArray.to_tarray(&self)
{ {
$if type_is_overaligned(): $if type_is_overaligned():
return self.to_aligned_array(allocator::temp()); return self.to_aligned_array(tmem());
$else $else
return self.to_array(allocator::temp()); return self.to_array(tmem());
$endif; $endif;
} }

View File

@@ -1,7 +1,7 @@
<* <*
@require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enummap" @require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enummap"
*> *>
module std::collections::enummap(<Enum, ValueType>); module std::collections::enummap{Enum, ValueType};
import std::io; import std::io;
struct EnumMap (Printable) struct EnumMap (Printable)
{ {
@@ -28,21 +28,6 @@ fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic
return n; 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" @return "The total size of this map, which is the same as the number of enum values"
@pure @pure

View File

@@ -5,10 +5,10 @@
<* <*
@require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enumset" @require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enumset"
*> *>
module std::collections::enumset(<Enum>); module std::collections::enumset{Enum};
import std::io; 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; const IS_CHAR_ARRAY = Enum.elements > 128;
distinct EnumSet (Printable) = EnumSetType; distinct EnumSet (Printable) = EnumSetType;
@@ -141,24 +141,7 @@ fn usz! EnumSet.to_format(&set, Formatter* formatter) @dynamic
return n; return n;
} }
fn String EnumSet.to_new_string(&set, Allocator allocator = allocator::heap()) @dynamic macro typeid type_for_enum_elements(usz $elements) @local
{
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)
{ {
$switch $switch
$case ($elements > 128): $case ($elements > 128):

View File

@@ -4,10 +4,25 @@
<* <*
@require $defined((Key){}.hash()) `No .hash function found on the key` @require $defined((Key){}.hash()) `No .hash function found on the key`
*> *>
module std::collections::map(<Key, Value>); module std::collections::map{Key, Value};
import std::math; import std::math;
import std::io @norecurse; 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) struct HashMap (Printable)
{ {
Entry*[] table; Entry*[] table;
@@ -17,17 +32,6 @@ struct HashMap (Printable)
float load_factor; 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" @param [&inout] allocator "The allocator to use"
@@ -46,17 +50,6 @@ fn HashMap* HashMap.init(&self, Allocator allocator, uint capacity = DEFAULT_INI
return self; 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 capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0" @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) 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,46 +69,9 @@ fn HashMap* HashMap.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float
@require !self.allocator "Map was already initialized" @require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" @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); 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) $for (var $i = 0; $i < $vacount; $i += 2)
self.set($vaarg[$i], $vaarg[$i + 1]); self.set($vaarg[$i], $vaarg[$i + 1]);
$endfor $endfor
@@ -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) macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{ {
self.tinit(capacity, load_factor); return self.tinit_with_key_values(tmem(), capacity, load_factor);
$for (var $i = 0; $i < $vacount; $i += 2)
self.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return self;
} }
<* <*
@@ -148,10 +100,10 @@ macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT
@require !self.allocator "Map was already initialized" @require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" @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); assert(keys.len == values.len);
self.tinit(capacity, load_factor); self.init(allocator, capacity, load_factor);
for (usz i = 0; i < keys.len; i++) for (usz i = 0; i < keys.len; i++)
{ {
self.set(keys[i], values[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; return self;
} }
<* <*
@param [in] keys "The keys for the HashMap entries" @param [in] keys "The keys for the HashMap entries"
@param [in] values "The values 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 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 capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0" @require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized" @require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" @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); return self.init_from_keys_and_values(tmem(), keys, values, capacity, load_factor);
self.tinit(capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
self.set(keys[i], values[i]);
}
return self;
} }
<* <*
@@ -191,14 +137,6 @@ fn bool HashMap.is_initialized(&map)
return (bool)map.allocator; 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 [&inout] allocator "The allocator to use"
@param [&in] other_map "The map to copy from." @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; 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." @param [&in] other_map "The map to copy from."
*> *>
fn HashMap* HashMap.tinit_from_map(&map, HashMap* other_map) 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 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 the map isn't initialized, use the defaults to initialize it.
if (!map.allocator) if (!map.allocator)
{ {
map.init(allocator::heap()); map.tinit();
} }
uint hash = rehash(key.hash()); uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len); uint index = index_for(hash, map.table.len);
@@ -345,31 +275,18 @@ fn void HashMap.free(&map)
map.table = {}; 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 {};
}
<* Key[] list = allocator::alloc_array(allocator, Key, self.count);
@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);
usz index = 0; usz index = 0;
foreach (Entry* entry : map.table) foreach (Entry* entry : self.table)
{ {
while (entry) while (entry)
{ {
@@ -386,15 +303,15 @@ fn Key[] HashMap.copy_keys(&map, Allocator allocator = allocator::heap())
macro HashMap.@each(map; @body(key, value)) macro HashMap.@each(map; @body(key, value))
{ {
map.@each_entry(; Entry* entry) { map.@each_entry(; Entry* entry)
{
@body(entry.key, entry.value); @body(entry.key, entry.value);
}; };
} }
macro HashMap.@each_entry(map; @body(entry)) 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)
@@ -404,35 +321,18 @@ macro HashMap.@each_entry(map; @body(entry))
} }
} }
} }
fn Value[] HashMap.tvalues(&map)
{
return map.values(tmem()) @inline;
} }
<* fn Value[] HashMap.values(&self, Allocator allocator)
@deprecated `use tcopy_values`
*>
fn Value[] HashMap.value_tlist(&map)
{ {
return map.copy_values(allocator::temp()) @inline; if (!self.count) return {};
} Value[] list = allocator::alloc_array(allocator, Value, self.count);
fn Value[] HashMap.tcopy_values(&map)
{
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);
usz index = 0; usz index = 0;
foreach (Entry* entry : map.table) foreach (Entry* entry : self.table)
{ {
while (entry) 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 HashMapValueIterator.len(self) @operator(len) => self.map.count;
fn usz HashMapKeyIterator.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 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);
}

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved. // Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license // Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file. // a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::linkedlist(<Type>); module std::collections::linkedlist{Type};
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type); const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
@@ -30,23 +30,9 @@ fn LinkedList* LinkedList.init(&self, Allocator allocator)
return self; 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) 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 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); return allocator::alloc(self.allocator, Node);
} }

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved. // Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license // Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file. // a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::list(<Type>); module std::collections::list{Type};
import std::io, std::math, std::collections::list_common; import std::io, std::math, std::collections::list_common;
def ElementPredicate = fn bool(Type *type); def ElementPredicate = fn bool(Type *type);
@@ -33,29 +33,6 @@ fn List* List.init(&self, Allocator allocator, usz initial_capacity = 16)
return self; 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. 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) fn List* List.tinit(&self, usz initial_capacity = 16)
{ {
return self.init(allocator::temp(), initial_capacity) @inline; 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.new_init_with_array(&self, Type[] values, Allocator allocator = allocator::heap()) @deprecated("Use init_with_array(mem)")
{
return self.init_with_array(allocator, values);
} }
<* <*
@@ -91,19 +57,6 @@ fn List* List.init_with_array(&self, Allocator allocator, Type[] values)
return self; 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. 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" @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.allocator = allocator;
self.capacity = types.len; 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 fn void List.push(&self, Type element) @inline
{ {
self.reserve(1); 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. 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" @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) fn Type[] List.to_tarray(&self)
{ {
$if type_is_overaligned(): $if type_is_overaligned():
return self.to_new_aligned_array(allocator::temp()); return self.to_aligned_array(tmem());
$else $else
return self.to_new_array(allocator::temp()); return self.to_array(tmem());
$endif; $endif;
} }
@@ -398,7 +341,7 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
{ {
if (!min_capacity) return; if (!min_capacity) return;
if (self.capacity >= 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 self.pre_free(); // Remove sanitizer annotation

View File

@@ -3,7 +3,7 @@ module std::collections::list_common;
<* <*
IMPORTANT The returned array must be freed using free_aligned. 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[]){}; if (!self.size) return ($Type[]){};
$Type[] result = allocator::alloc_array_aligned(allocator, $Type, self.size); $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; return result;
} }
macro list_to_new_array($Type, self, Allocator allocator) macro list_to_array($Type, self, Allocator allocator)
{ {
if (!self.size) return ($Type[]){}; if (!self.size) return ($Type[]){};
$Type[] result = allocator::alloc_array(allocator, $Type, self.size); $Type[] result = allocator::alloc_array(allocator, $Type, self.size);

View File

@@ -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(<Key, Value>);
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;
}

View File

@@ -1,4 +1,4 @@
module std::collections::maybe(<Type>); module std::collections::maybe{Type};
import std::io; import std::io;
struct Maybe (Printable) struct Maybe (Printable)
@@ -28,16 +28,6 @@ fn Maybe value(Type val)
return { .value = val, .has_value = true }; 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 = { }; const Maybe EMPTY = { };
macro Type! Maybe.get(self) macro Type! Maybe.get(self)

View File

@@ -50,7 +50,7 @@ fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
usz n = formatter.printf("{")!; usz n = formatter.printf("{")!;
@stack_mem(1024; Allocator mem) @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(",")!; if (i > 0) n += formatter.printf(",")!;
n += formatter.printf(`"%s":`, key)!; n += formatter.printf(`"%s":`, key)!;
@@ -462,7 +462,7 @@ fn Object* Object.get_or_create_obj(&self, String key)
return container; return container;
} }
def ObjectInternalMap = HashMap(<String, Object*>) @private; def ObjectInternalMap = HashMap{String, Object*} @private;
def ObjectInternalList = List(<Object*>) @private; def ObjectInternalList = List{Object*} @private;
def ObjectInternalMapEntry = Entry(<String, Object*>) @private; def ObjectInternalMapEntry = Entry{String, Object*} @private;

View File

@@ -1,7 +1,7 @@
// priorityqueue.c3 // priorityqueue.c3
// A priority queue using a classic binary heap for 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 // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // 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, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
module std::collections::priorityqueue(<Type>); module std::collections::priorityqueue{Type};
import std::collections::priorityqueue::private; import std::collections::priorityqueue::private;
distinct PriorityQueue = inline PrivatePriorityQueue(<Type, false>); distinct PriorityQueue = inline PrivatePriorityQueue{Type, false};
distinct PriorityQueueMax = inline PrivatePriorityQueue(<Type, true>); distinct PriorityQueueMax = inline PrivatePriorityQueue{Type, true};
module std::collections::priorityqueue::private(<Type, MAX>); module std::collections::priorityqueue::private{Type, MAX};
import std::collections::list, std::io; import std::collections::list, std::io;
def Heap = List(<Type>);
struct PrivatePriorityQueue (Printable) 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); 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); self.init(tmem(), initial_capacity);
} return self;
fn void PrivatePriorityQueue.temp_init(&self, usz initial_capacity = 16) @inline
{
self.heap.init(allocator::temp(), initial_capacity) @inline;
} }
@@ -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) fn void PrivatePriorityQueue.remove_at(&self, usz index)
{ {
if (index == 0) if (index == 0)
@@ -124,8 +122,7 @@ fn Type! PrivatePriorityQueue.pop(&self)
fn Type! PrivatePriorityQueue.first(&self) fn Type! PrivatePriorityQueue.first(&self)
{ {
if (!self.len()) return IteratorResult.NO_MORE_ELEMENT?; return self.heap.first();
return self.heap.get(0);
} }
fn void PrivatePriorityQueue.free(&self) fn void PrivatePriorityQueue.free(&self)
@@ -135,12 +132,12 @@ fn void PrivatePriorityQueue.free(&self)
fn usz PrivatePriorityQueue.len(&self) @operator(len) fn usz PrivatePriorityQueue.len(&self) @operator(len)
{ {
return self.heap.len(); return self.heap.len() @inline;
} }
fn bool PrivatePriorityQueue.is_empty(&self) 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); 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);
}

View File

@@ -1,7 +1,7 @@
<* <*
@require Type.is_ordered : "The type must be ordered" @require Type.is_ordered : "The type must be ordered"
*> *>
module std::collections::range(<Type>); module std::collections::range{Type};
import std::io; import std::io;
struct Range (Printable) struct Range (Printable)
@@ -29,21 +29,6 @@ fn Type Range.get(&self, usz index) @operator([])
return (Type)(self.start + (usz)index); 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 fn usz! Range.to_format(&self, Formatter* formatter) @dynamic
{ {
return formatter.printf("[%s..%s]", self.start, self.end)!; 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)!; 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" @require index < self.len() : "Can't index into an empty range"
*> *>

View File

@@ -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(<Type, SIZE>); 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 written;
usz head; usz head;
} }
@@ -15,9 +18,9 @@ fn void RingBuffer.init(&self) @inline
*self = {}; *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.buf[self.written] = c;
self.written++; self.written++;
@@ -25,14 +28,14 @@ fn void RingBuffer.push(&self, Type c)
else else
{ {
self.buf[self.head] = c; 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; index %= self.buf.len;
usz avail = SIZE - self.head; usz avail = self.buf.len - self.head;
if (index < avail) if (index < avail)
{ {
return self.buf[self.head + index]; return self.buf[self.head + index];
@@ -40,25 +43,31 @@ fn Type RingBuffer.get(&self, usz index) @operator([])
return self.buf[index - avail]; return self.buf[index - avail];
} }
fn Type! RingBuffer.pop(&self) fn Element! RingBuffer.pop(&self)
{ {
switch switch
{ {
case self.written == 0: case self.written == 0:
return SearchResult.MISSING?; return SearchResult.MISSING?;
case self.written < SIZE: case self.written < self.buf.len:
self.written--; self.written--;
return self.buf[self.written]; return self.buf[self.written];
default: default:
self.head = (self.head - 1) % SIZE; self.head = (self.head - 1) % self.buf.len;
return self.buf[self.head]; 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; // Improve this?
if (self.written < SIZE) 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; if (index >= self.written) return 0;
usz end = self.written - index; usz end = self.written - index;
@@ -66,7 +75,7 @@ fn usz RingBuffer.read(&self, usz index, Type[] buffer)
buffer[:n] = self.buf[index:n]; buffer[:n] = self.buf[index:n];
return n; return n;
} }
usz end = SIZE - self.head; usz end = self.buf.len - self.head;
if (index >= end) if (index >= end)
{ {
index -= end; index -= end;
@@ -75,13 +84,13 @@ fn usz RingBuffer.read(&self, usz index, Type[] buffer)
buffer[:n] = self.buf[index:n]; buffer[:n] = self.buf[index:n];
return n; return n;
} }
if (buffer.len <= SIZE - index) if (buffer.len <= self.buf.len - index)
{ {
usz n = buffer.len; usz n = buffer.len;
buffer[:n] = self.buf[self.head + index:n]; buffer[:n] = self.buf[self.head + index:n];
return n; return n;
} }
usz n1 = SIZE - index; usz n1 = self.buf.len - index;
buffer[:n1] = self.buf[self.head + index:n1]; buffer[:n1] = self.buf[self.head + index:n1];
buffer = buffer[n1..]; buffer = buffer[n1..];
index -= n1; index -= n1;
@@ -90,10 +99,10 @@ fn usz RingBuffer.read(&self, usz index, Type[] buffer)
return n1 + n2; return n1 + n2;
} }
fn void RingBuffer.write(&self, Type[] buffer) fn void RingBuffer.write(&self, Element[] buffer)
{ {
usz i; 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.buf[self.written] = buffer[i++];
self.written++; self.written++;
@@ -101,6 +110,6 @@ fn void RingBuffer.write(&self, Type[] buffer)
foreach (c : buffer[i..]) foreach (c : buffer[i..])
{ {
self.buf[self.head] = c; self.buf[self.head] = c;
self.head = (self.head + 1) % SIZE; self.head = (self.head + 1) % self.buf.len;
} }
} }

View File

@@ -1,4 +1,4 @@
module std::collections::tuple(<Type1, Type2>); module std::collections::tuple{Type1, Type2};
struct Tuple struct Tuple
{ {
@@ -6,7 +6,7 @@ struct Tuple
Type2 second; Type2 second;
} }
module std::collections::triple(<Type1, Type2, Type3>); module std::collections::triple{Type1, Type2, Type3};
struct Triple struct Triple
{ {

View File

@@ -74,18 +74,10 @@ import std::io;
fn usz! write(String filename, char[] input, QOIDesc* desc) => @pool() fn usz! write(String filename, char[] input, QOIDesc* desc) => @pool()
{ {
// encode data // encode data
char[] output = new_encode(input, desc, allocator: allocator::temp())!; char[] output = encode(tmem(), input, desc)!;
// open file file::save(filename, output)!;
File! f = file::open(filename, "wb"); return output.len;
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;
} }
@@ -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 [&out] desc `The descriptor to fill with the image's info`
@param channels `The channels to be used` @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 // read file
char[] data = file::load_temp(filename) ?? QOIError.FILE_OPEN_FAILED?!; char[] data = file::load_temp(filename) ?? QOIError.FILE_OPEN_FAILED?!;
// pass data to decode function // 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 // Back to basic non-stdio mode
module std::compression::qoi; module std::compression::qoi;
import std::bits; 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. 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] input `The raw RGB or RGBA pixels to encode`
@param [&in] desc `The descriptor of the image` @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 // check info in desc
if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_PARAMETERS?; 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. 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 [&out] desc `The descriptor to fill with the image's info`
@param channels `The channels to be used` @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 // check input data
if (data.len < Header.sizeof + END_OF_STREAM.len) return QOIError.INVALID_DATA?; if (data.len < Header.sizeof + END_OF_STREAM.len) return QOIError.INVALID_DATA?;

View File

@@ -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 // Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file. // a copy of which can be found in the LICENSE_STDLIB file.

View File

@@ -13,7 +13,7 @@ struct Allocation
void*[MAX_BACKTRACE] backtrace; void*[MAX_BACKTRACE] backtrace;
} }
def AllocMap = HashMap(<uptr, Allocation>); def AllocMap = HashMap { uptr, Allocation };
// A simple tracking allocator. // A simple tracking allocator.
// It tracks allocations using a hash map but // It tracks allocations using a hash map but
@@ -52,7 +52,7 @@ fn void TrackingAllocator.free(&self)
fn usz TrackingAllocator.allocated(&self) => @pool() fn usz TrackingAllocator.allocated(&self) => @pool()
{ {
usz allocated = 0; usz allocated = 0;
foreach (&allocation : self.map.value_tlist()) allocated += allocation.size; foreach (&allocation : self.map.tvalues()) allocated += allocation.size;
return allocated; return allocated;
} }
@@ -68,7 +68,7 @@ fn usz TrackingAllocator.total_allocation_count(&self) => self.allocs_total;
fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator allocator) 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; usz entries = 0;
bool leaks = false; bool leaks = false;
Allocation[] allocs = self.map.value_tlist(); Allocation[] allocs = self.map.tvalues();
if (allocs.len) if (allocs.len)
{ {
if (!allocs[0].backtrace[0]) if (!allocs[0].backtrace[0])
@@ -155,7 +155,7 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream out) => @pool()
Backtrace trace = backtrace::BACKTRACE_UNKNOWN; Backtrace trace = backtrace::BACKTRACE_UNKNOWN;
if (allocation.backtrace[3]) 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; if (trace.function.len) leaks = true;
io::fprintfn(out, "%13s %p %s:%d", allocation.size, io::fprintfn(out, "%13s %p %s:%d", allocation.size,
@@ -194,7 +194,7 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream out) => @pool()
break; 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)!; io::fprintfn(out, "Allocation %d (%d bytes): ", i + 1, allocation.size)!;
foreach (trace : list) foreach (trace : list)
{ {

View File

@@ -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 (xlen < 1) xlen = $typeof((*array_ptr)[0]).len + xlen;
if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen; if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen;
var $ElementType = $typeof((*array_ptr)[0][0]); 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" @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
@ensure result.len == arr1.len + arr2.len @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]); var $Type = $typeof(arr1[0]);
$Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len); $Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len);
@@ -70,22 +70,6 @@ macro concat(arr1, arr2, Allocator allocator) @nodiscard
} }
return result; 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, Concatenate two arrays or slices, returning a slice containing the concatenation of them,
allocated using the temp allocator. 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" @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
@ensure return.len == arr1.len + arr2.len @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(<Type>); module std::core::array::slice{Type};
struct Slice2d struct Slice2d
{ {

View File

@@ -4,6 +4,31 @@
module std::core::builtin; module std::core::builtin;
import libc, std::hash, std::io, std::os::backtrace; 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. 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*[256] buffer;
void*[] backtraces = backtrace::capture_current(&buffer); void*[] backtraces = backtrace::capture_current(&buffer);
backtraces_to_ignore++; backtraces_to_ignore++;
BacktraceList! backtrace = backtrace::symbolize_backtrace(backtraces, allocator::temp()); BacktraceList! backtrace = backtrace::symbolize_backtrace(tmem(), backtraces);
if (catch backtrace) return false; if (catch backtrace) return false;
if (backtrace.len() <= backtraces_to_ignore) return false; if (backtrace.len() <= backtraces_to_ignore) return false;
io::eprint("\nERROR: '"); 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 char[].hash(char[] c) => (uint)fnv32a::encode(c);
macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr)); 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; const MAX_FRAMEADDRESS = 128;
<* <*

View File

@@ -6,19 +6,6 @@ distinct DStringOpaque = void;
const usz MIN_CAPACITY @private = 16; 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" @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; 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" @require !self.data() "String already initialized"
*> *>
fn DString DString.tinit(&self, usz capacity = MIN_CAPACITY) fn DString DString.tinit(&self, usz capacity = MIN_CAPACITY)
{ {
self.init(allocator::temp(), capacity) @inline; return self.init(tmem(), capacity) @inline;
return *self;
} }
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); 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; usz len = c.len;
StringData* data = (StringData*)new_with_capacity(len, allocator); StringData* data = (StringData*)new_with_capacity(allocator, len);
if (len) if (len)
{ {
data.len = len; data.len = len;
@@ -69,7 +46,7 @@ fn DString new(String c = "", Allocator allocator = allocator::heap())
return (DString)data; 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) 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) fn DString DString.concat(self, Allocator allocator, DString b)
{ {
DString string; DString string;
@@ -139,7 +107,7 @@ fn DString DString.concat(self, Allocator allocator, DString b)
return string; 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) fn ZString DString.zstr_view(&self)
{ {
@@ -249,23 +217,18 @@ fn usz DString.append_char32(&self, Char32 c)
return n; 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 (!self) return new(allocator);
{
if (allocator) return new_with_capacity(0, allocator);
return (DString)null;
}
StringData* data = self.data(); StringData* data = self.data();
if (!allocator) allocator = allocator::heap(); DString new_string = new_with_capacity(allocator, data.capacity);
DString new_string = new_with_capacity(data.capacity, allocator);
mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len); mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len);
return new_string; 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(); usz str_len = self.len();
if (!str_len) if (!str_len)
@@ -279,12 +242,12 @@ fn ZString DString.copy_zstr(self, Allocator allocator = allocator::heap())
return (ZString)zstr; 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()]; 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) 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 (!other_len) return;
if (!*self) if (!*self)
{ {
*self = new(str); *self = temp(str);
return; return;
} }
self.reserve(other_len); self.reserve(other_len);
@@ -343,9 +306,9 @@ fn void DString.append_chars(&self, String str)
data.len += other_len; 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) fn void DString.append_string(&self, DString str)
@@ -376,7 +339,7 @@ fn void DString.append_char(&self, char c)
{ {
if (!*self) if (!*self)
{ {
*self = new_with_capacity(MIN_CAPACITY); *self = temp_with_capacity(MIN_CAPACITY);
} }
self.reserve(1); self.reserve(1);
StringData* data = self.data(); 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 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) @pool(self.data().allocator)
{ {
Formatter formatter; Formatter formatter;
@@ -585,7 +548,7 @@ fn usz! DString.appendf(&self, String format, args...) @maydiscard
fn usz! DString.appendfn(&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) @pool(self.data().allocator)
{ {
Formatter formatter; 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; usz total_size = joiner.len * s.len;
foreach (String* &str : s) foreach (String* &str : s)
{ {
total_size += str.len; total_size += str.len;
} }
DString res = new_with_capacity(total_size, allocator); DString res = new_with_capacity(allocator, total_size);
res.append(s[0]); res.append(s[0]);
foreach (String* &str : s[1..]) foreach (String* &str : s[1..])
{ {
@@ -644,7 +607,7 @@ fn void DString.reserve(&self, usz addition)
StringData* data = self.data(); StringData* data = self.data();
if (!data) if (!data)
{ {
*self = dstring::new_with_capacity(addition); *self = dstring::temp_with_capacity(addition);
return; return;
} }
usz len = data.len + addition; usz len = data.len + addition;

View File

@@ -401,6 +401,15 @@ macro TempAllocator* temp()
return thread_temp_allocator; 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 fn void init_default_temp_allocators() @private
{ {
temp_allocator_pair[0] = create_default_sized_temp_allocator(temp_base_allocator); temp_allocator_pair[0] = create_default_sized_temp_allocator(temp_base_allocator);

View File

@@ -80,7 +80,7 @@ macro String[] wargs_strings(int argc, Char16** argv) @private
{ {
Char16* arg = argv[i]; Char16* arg = argv[i];
Char16[] argstring = arg[:_strlen(arg)]; 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]; return list[:argc];
} }

View File

@@ -1,8 +1,7 @@
module std::core::runtime; module std::core::runtime;
import libc, std::time, std::io, std::sort; import libc, std::time, std::io, std::sort;
def BenchmarkFn = fn void!() @if($$OLD_TEST); def BenchmarkFn = fn void();
def BenchmarkFn = fn void() @if(!$$OLD_TEST);
struct BenchmarkUnit struct BenchmarkUnit
{ {
@@ -10,7 +9,7 @@ struct BenchmarkUnit
BenchmarkFn func; BenchmarkFn func;
} }
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator = allocator::heap()) fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator)
{ {
BenchmarkFn[] fns = $$BENCHMARK_FNS; BenchmarkFn[] fns = $$BENCHMARK_FNS;
String[] names = $$BENCHMARK_NAMES; String[] names = $$BENCHMARK_NAMES;
@@ -39,80 +38,7 @@ fn void set_benchmark_max_iterations(uint value) @builtin
benchmark_max_iterations = value; benchmark_max_iterations = value;
} }
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if($$OLD_TEST) fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
{
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; usz max_name;
@@ -170,5 +96,5 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if(!$$OLD_TEST)
fn bool default_benchmark_runner(String[] args) => @pool() fn bool default_benchmark_runner(String[] args) => @pool()
{ {
return run_benchmarks(benchmark_collection_create(allocator::temp())); return run_benchmarks(benchmark_collection_create(tmem()));
} }

View File

@@ -7,8 +7,7 @@ import std::core::mem::allocator @public;
import libc, std::time, std::io, std::sort; import libc, std::time, std::io, std::sort;
import std::os::env; import std::os::env;
def TestFn = fn void!() @if($$OLD_TEST); def TestFn = fn void();
def TestFn = fn void() @if(!$$OLD_TEST);
TestContext* test_context @private; TestContext* test_context @private;
@@ -47,7 +46,7 @@ struct TestUnit
TestFn func; TestFn func;
} }
fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap()) fn TestUnit[] test_collection_create(Allocator allocator)
{ {
TestFn[] fns = $$TEST_FNS; TestFn[] fns = $$TEST_FNS;
String[] names = $$TEST_NAMES; 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() 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; 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(); mute_output();
mem.clear(); mem.clear();
if (check_leaks) allocator::thread_allocator = &mem; if (check_leaks) allocator::thread_allocator = &mem;
$if(!$$OLD_TEST):
unit.func(); 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 // track cleanup that may take place in teardown_fn
if (context.teardown_fn) if (context.teardown_fn)
{ {
@@ -337,6 +329,6 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
fn bool default_test_runner(String[] args) => @pool() fn bool default_test_runner(String[] args) => @pool()
{ {
assert(test_context == null, "test suite is already running"); 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()));
} }

View File

@@ -52,20 +52,13 @@ fn ZString tformat_zstr(String fmt, args...)
@param [inout] allocator `The allocator to use` @param [inout] allocator `The allocator to use`
@param [in] fmt `The formatting string` @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); DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
str.appendf(fmt, ...args); str.appendf(fmt, ...args);
return str.copy_str(allocator); 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. Return a temporary String created using the formatting function.
@@ -78,19 +71,6 @@ fn String tformat(String fmt, args...)
return str.str_view(); 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. Check if a character is in a set.
@@ -105,7 +85,7 @@ macro bool char_in_set(char c, String set)
return false; 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) 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" @require needle.len > 0 "The needle must be at least 1 character long"
@ensure return.len > 0 @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 capacity = 16;
usz i = 0; usz i = 0;
@@ -281,18 +261,6 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = al
return holder[:i]; 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 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 max "Max number of elements, 0 means no limit, defaults to 0"
@param skip_empty "True to skip empty elements" @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 } 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; usz len = s.len;
char* str = allocator::malloc(allocator, len + 1); char* str = allocator::malloc(allocator, len + 1);
@@ -530,7 +498,7 @@ fn ZString String.zstr_copy(s, Allocator allocator = allocator::heap())
return (ZString)str; 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; usz full_len = s1.len + s2.len;
char* str = allocator::malloc(allocator, full_len + 1); 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]; 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 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 sentinel, so that it safely can be converted to a ZString by a
cast. cast.
*> *>
fn String String.copy(s, Allocator allocator = allocator::heap()) fn String String.copy(s, Allocator allocator)
{ {
usz len = s.len; usz len = s.len;
char* str = allocator::malloc(allocator, len + 1); char* str = allocator::malloc(allocator, len + 1);
@@ -560,23 +528,23 @@ fn String String.copy(s, Allocator allocator = allocator::heap())
return (String)str[:len]; return (String)str[:len];
} }
fn void String.free(&s, Allocator allocator = allocator::heap()) fn void String.free(&s, Allocator allocator)
{ {
if (!s.ptr) return; if (!s.ptr) return;
allocator::free(allocator, s.ptr); allocator::free(allocator, s.ptr);
*s = ""; *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; return z.str_view().copy(allocator) @inline;
} }
fn String ZString.tcopy(z) 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! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
@return! AllocationFailure "If allocation of the string fails" @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); usz len16 = conv::utf16len_for_utf8(s);
Char16* data = allocator::alloc_array_try(allocator, Char16, len16 + 1)!; 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]; return data[:len16];
} }
<* fn Char16[]! String.to_utf16_tcopy(s) => s.to_utf16_copy(tmem());
Convert an UTF-8 string to UTF-16
@return "The UTF-16 string as a slice, allocated using the given allocator" fn WString! String.to_wstring_copy(s, Allocator 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)
{ {
return s.to_new_utf16(allocator::temp()); return (WString)s.to_utf16_copy(allocator).ptr;
} }
fn WString! String.to_wstring(s, Allocator allocator) fn WString! String.to_wstring_tcopy(s) => s.to_wstring_copy(tmem());
{
return (WString)s.to_new_utf16(allocator).ptr;
}
fn WString! String.to_temp_wstring(s) => s.to_wstring(allocator::temp()); fn Char32[]! String.to_utf32_copy(s, Allocator allocator)
fn WString! String.to_new_wstring(s) => s.to_wstring(allocator::heap());
fn Char32[]! String.to_utf32(s, Allocator allocator)
{ {
usz codepoints = conv::utf8_codepoints(s); usz codepoints = conv::utf8_codepoints(s);
Char32* data = allocator::alloc_array_try(allocator, Char32, codepoints + 1)!; 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]; 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 @param [inout] s
@pure @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'; 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); String copy = s.copy(allocator);
copy.convert_ascii_to_lower(); copy.convert_to_lower();
return copy; 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 @param [inout] s
@pure @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'; 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.` @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); String copy = s.copy(allocator);
copy.convert_ascii_to_upper(); copy.convert_to_upper();
return copy; return copy;
} }
@@ -683,12 +638,12 @@ fn StringIterator String.iterator(s)
@param [in] s @param [in] s
@return `a temporary String converted to ASCII upper case.` @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); usz len = conv::utf8len_for_utf32(utf32);
char* data = allocator::malloc_try(allocator, len + 1)!; 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]; 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); usz len = conv::utf8len_for_utf16(utf16);
char* data = allocator::malloc_try(allocator, len + 1)!; 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]; 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; usz utf16_len;
while (wstring[utf16_len] != 0) utf16_len++; while (wstring[utf16_len] != 0) utf16_len++;
Char16[] utf16 = wstring[: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_wstring(WString wstring) => new_from_wstring(tmem(), wstring) @inline;
fn String! temp_from_utf16(Char16[] utf16) => new_from_utf16(utf16, allocator::temp()) @inline; fn String! temp_from_utf16(Char16[] utf16) => new_from_utf16(tmem(), utf16) @inline;
fn usz String.utf8_codepoints(s) 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; DString s;
@stack_mem(512; Allocator mem) @stack_mem(512; Allocator mem)
{ {
s.init(mem); s.init(allocator: mem);
io::fprint(&s, x)!!; io::fprint(&s, x)!!;
return s.copy_str(allocator); 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);

View File

@@ -20,7 +20,7 @@ const char DEFAULT_PAD = '=';
@require padding < 0xFF "Invalid padding character" @require padding < 0xFF "Invalid padding character"
@return "The encoded string." @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)); char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding));
return encode_buffer(src, dst, padding, alphabet); 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" @require padding < 0xFF "Invalid padding character"
@return "The decoded data." @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)); char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding));
return decode_buffer(src, dst, padding, alphabet); 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! tencode(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(tmem(), code, padding, alphabet);
fn String! encode_temp(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet); fn char[]! tdecode(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(tmem(), code, 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);
<* <*
Calculate the length in bytes of the decoded data. Calculate the length in bytes of the decoded data.

View File

@@ -43,22 +43,20 @@ const Base64Alphabet URL = {
const STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const URL_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)); char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding));
return encode_buffer(src, dst, padding, alphabet); 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))!; char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding))!;
return decode_buffer(src, dst, padding, alphabet); 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 tencode(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(tmem(), code, padding, alphabet);
fn String encode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet); fn char[]! tdecode(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(tmem(), code, 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);
<* <*

View File

@@ -38,26 +38,20 @@ fn void CsvReader.init(&self, InStream stream, String separator = ",")
self.stream = stream; self.stream = stream;
self.separator = separator; self.separator = separator;
} }
fn CsvRow! CsvReader.read_new_row(self)
{
return self.read_row(allocator::heap()) @inline;
}
<* <*
@param [&inout] allocator @param [&inout] allocator
*> *>
fn CsvRow! CsvReader.read_row(self, Allocator 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); 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 }; 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) @stack_mem(512; Allocator mem)
{ {
String! s = io::readline(stream, mem); String! s = io::readline(mem, stream);
if (catch err = s) if (catch err = s)
{ {
if (err == IoError.EOF) return; if (err == IoError.EOF) return;
return err?; return err?;
} }
@body(s.split(sep, allocator: mem)); @body(s.split(mem, sep));
}; };
} }
} }

View File

@@ -13,22 +13,21 @@ fn char[]! decode_buffer(char[] code, char[] buffer)
return buffer[:decode_bytes(code, 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)); char[] data = allocator::alloc_array(allocator, char, encode_len(code.len));
return (String)data[:encode_bytes(code, data)]; 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)); char[] data = allocator::alloc_array(allocator, char, decode_len(code.len));
return data[:decode_bytes(code, data)!]; return data[:decode_bytes(code, data)!];
} }
fn String encode_new(char[] code) @inline => encode(code, allocator::heap()); fn String tencode(char[] code) @inline => encode(tmem(), code);
fn String encode_temp(char[] code) @inline => encode(code, allocator::temp()); fn char[]! tdecode(char[] code) @inline => decode(tmem(), code);
fn char[]! decode_new(char[] code) @inline => decode(code, allocator::heap());
fn char[]! decode_temp(char[] code) @inline => decode(code, allocator::temp());
<* <*
Calculate the size of the encoded data. Calculate the size of the encoded data.

View File

@@ -15,21 +15,21 @@ fault JsonParsingError
INVALID_NUMBER, 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) @pool(allocator)
{ {
return parse_any(&context); 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 -- // -- Implementation follows --
@@ -106,7 +106,7 @@ fn JsonTokenType! lex_number(JsonContext *context, char c) @local
{ {
@stack_mem(256; Allocator mem) @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 == '-'; bool negate = c == '-';
if (negate) if (negate)
{ {
@@ -159,7 +159,7 @@ fn Object*! parse_map(JsonContext* context) @local
@stack_mem(256; Allocator mem) @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) while (token != JsonTokenType.RBRACE)
{ {
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?; if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?;

View File

@@ -1,4 +1,4 @@
module std::experimental::scheduler(<Event>); module std::experimental::scheduler{Event};
import std::collections, std::thread, std::time; import std::collections, std::thread, std::time;
struct DelayedSchedulerEvent @local struct DelayedSchedulerEvent @local
@@ -19,9 +19,9 @@ fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other) @loca
struct FrameScheduler struct FrameScheduler
{ {
PriorityQueue(<DelayedSchedulerEvent>) delayed_events; PriorityQueue{DelayedSchedulerEvent} delayed_events;
List(<Event>) events; List{Event} events;
List(<Event>) pending_events; List{Event} pending_events;
bool pending; bool pending;
Mutex mtx; Mutex mtx;
} }

View File

@@ -1,4 +1,4 @@
module std::hash::hmac(<HashAlg, HASH_BYTES, BLOCK_BYTES>); module std::hash::hmac{HashAlg, HASH_BYTES, BLOCK_BYTES};
import std::crypto; import std::crypto;
struct Hmac struct Hmac

View File

@@ -13,9 +13,9 @@ struct Md5
uint[16] block; uint[16] block;
} }
def HmacMd5 = Hmac(<Md5, HASH_BYTES, BLOCK_BYTES>); def HmacMd5 = Hmac{Md5, HASH_BYTES, BLOCK_BYTES};
def hmac = hmac::hash(<Md5, HASH_BYTES, BLOCK_BYTES>); def hmac = hmac::hash{Md5, HASH_BYTES, BLOCK_BYTES};
def pbkdf2 = hmac::pbkdf2(<Md5, HASH_BYTES, BLOCK_BYTES>); def pbkdf2 = hmac::pbkdf2{Md5, HASH_BYTES, BLOCK_BYTES};
fn char[HASH_BYTES] hash(char[] data) fn char[HASH_BYTES] hash(char[] data)
{ {

View File

@@ -18,9 +18,9 @@ struct Sha1
char[BLOCK_BYTES] buffer; char[BLOCK_BYTES] buffer;
} }
def HmacSha1 = Hmac(<Sha1, HASH_BYTES, BLOCK_BYTES>); def HmacSha1 = Hmac{Sha1, HASH_BYTES, BLOCK_BYTES};
def hmac = hmac::hash(<Sha1, HASH_BYTES, BLOCK_BYTES>); def hmac = hmac::hash{Sha1, HASH_BYTES, BLOCK_BYTES};
def pbkdf2 = hmac::pbkdf2(<Sha1, HASH_BYTES, BLOCK_BYTES>); def pbkdf2 = hmac::pbkdf2{Sha1, HASH_BYTES, BLOCK_BYTES};
fn char[HASH_BYTES] hash(char[] data) fn char[HASH_BYTES] hash(char[] data)
{ {

View File

@@ -34,9 +34,9 @@ struct Sha256
char[BLOCK_SIZE] buffer; char[BLOCK_SIZE] buffer;
} }
def HmacSha256 = Hmac(<Sha256, HASH_SIZE, BLOCK_SIZE>); def HmacSha256 = Hmac{Sha256, HASH_SIZE, BLOCK_SIZE};
def hmac = hmac::hash(<Sha256, HASH_SIZE, BLOCK_SIZE>); def hmac = hmac::hash{Sha256, HASH_SIZE, BLOCK_SIZE};
def pbkdf2 = hmac::pbkdf2(<Sha256, HASH_SIZE, BLOCK_SIZE>); def pbkdf2 = hmac::pbkdf2{Sha256, HASH_SIZE, BLOCK_SIZE};
fn char[HASH_SIZE] hash(char[] data) fn char[HASH_SIZE] hash(char[] data)
{ {

View File

@@ -21,7 +21,7 @@ fn File! open_path(Path path, String mode)
fn bool exists(String file) => @pool() 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) fn File from_handle(CFile file)
@@ -34,12 +34,20 @@ fn bool is_file(String path)
return os::native_is_file(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) fn usz! get_size(String path)
{ {
return os::native_file_size(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;
}
<* <*

View File

@@ -6,8 +6,7 @@ const int PRINTF_NTOA_BUFFER_SIZE = 256;
interface Printable interface Printable
{ {
fn String to_string(Allocator allocator) @optional; fn String to_constant_string() @optional;
fn String to_new_string(Allocator allocator) @optional @deprecated("Use to_string");
fn usz! to_format(Formatter* formatter) @optional; fn usz! to_format(Formatter* formatter) @optional;
} }
@@ -28,8 +27,7 @@ macro bool is_struct_with_default_print($Type)
{ {
return $Type.kindof == STRUCT return $Type.kindof == STRUCT
&&& !$defined($Type.to_format) &&& !$defined($Type.to_format)
&&& !$defined($Type.to_new_string) &&& !$defined($Type.to_constant_string);
&&& !$defined($Type.to_string);
} }
<* <*
@@ -125,7 +123,7 @@ fn usz! Formatter.print_with_function(&self, Printable arg)
if (!arg) return self.out_substr("(null)"); if (!arg) return self.out_substr("(null)");
return arg.to_format(self); return arg.to_format(self);
} }
if (&arg.to_string) if (&arg.to_constant_string)
{ {
PrintFlags old = self.flags; PrintFlags old = self.flags;
uint old_width = self.width; uint old_width = self.width;
@@ -137,10 +135,7 @@ fn usz! Formatter.print_with_function(&self, Printable arg)
self.prec = old_prec; self.prec = old_prec;
} }
if (!arg) return self.out_substr("(null)"); if (!arg) return self.out_substr("(null)");
@stack_mem(1024; Allocator mem) return self.out_substr(arg.to_constant_string());
{
return self.out_substr(arg.to_string(mem));
};
} }
return SearchResult.MISSING?; return SearchResult.MISSING?;
} }

View File

@@ -55,7 +55,7 @@ fault IoError
@param [inout] allocator `the allocator to use.` @param [inout] allocator `the allocator to use.`
@return `The string containing the data read.` @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); bool $is_stream = @typeis(stream, InStream);
$if $is_stream: $if $is_stream:
@@ -100,7 +100,7 @@ macro String! readline(stream = io::stdin(), Allocator allocator = allocator::he
*> *>
macro String! treadline(stream = io::stdin()) macro String! treadline(stream = io::stdin())
{ {
return readline(stream, allocator::temp()) @inline; return readline(tmem(), stream) @inline;
} }
<* <*

View File

@@ -21,7 +21,7 @@ macro void! native_chdir(Path path)
@pool() @pool()
{ {
// TODO improve with better error handling. // 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?; return IoError.GENERAL_ERROR?;
$default: $default:

View File

@@ -8,7 +8,7 @@ import libc;
fn void*! native_fopen(String filename, String mode) @inline => @pool() fn void*! native_fopen(String filename, String mode) @inline => @pool()
{ {
$if env::WIN32: $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 $else
void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy()); void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy());
$endif $endif
@@ -18,7 +18,7 @@ fn void*! native_fopen(String filename, String mode) @inline => @pool()
fn void! native_remove(String filename) => @pool() fn void! native_remove(String filename) => @pool()
{ {
$if env::WIN32: $if env::WIN32:
CInt result = libc::_wremove(filename.to_temp_wstring())!; CInt result = libc::_wremove(filename.to_wstring_tcopy())!;
$else $else
CInt result = libc::remove(filename.zstr_tcopy()); CInt result = libc::remove(filename.zstr_tcopy());
$endif $endif
@@ -42,7 +42,7 @@ fn void! native_remove(String filename) => @pool()
fn void*! native_freopen(void* file, String filename, String mode) @inline => @pool() fn void*! native_freopen(void* file, String filename, String mode) @inline => @pool()
{ {
$if env::WIN32: $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 $else
file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file); file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file);
$endif $endif

View File

@@ -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() fn usz! native_file_size(String path) @if(env::WIN32) => @pool()
{ {
Win32_FILE_ATTRIBUTE_DATA data; 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; Win32_LARGE_INTEGER size;
size.lowPart = data.nFileSizeLow; size.lowPart = data.nFileSizeLow;
size.highPart = data.nFileSizeHigh; size.highPart = data.nFileSizeHigh;
@@ -74,7 +74,7 @@ fn bool native_file_or_dir_exists(String path)
$case env::WIN32: $case env::WIN32:
@pool() @pool()
{ {
return (bool)win32::pathFileExistsW(path.to_temp_utf16()) ?? false; return (bool)win32::pathFileExistsW(path.to_utf16_tcopy()) ?? false;
}; };
$case env::POSIX: $case env::POSIX:
@pool() @pool()

View File

@@ -17,7 +17,7 @@ macro String! getcwd(Allocator allocator = allocator::heap())
free = true; free = true;
} }
Char16[] str16 = res[:win32::wcslen(res)]; Char16[] str16 = res[:win32::wcslen(res)];
return string::new_from_utf16(str16, allocator); return string::new_from_utf16(allocator, str16);
$case env::POSIX: $case env::POSIX:
const usz DEFAULT_BUFFER = 256; const usz DEFAULT_BUFFER = 256;

View File

@@ -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 (!name || name == "." || name == "..") continue;
if (entry.d_type == posix::DT_LNK && no_symlinks) continue; if (entry.d_type == posix::DT_LNK && no_symlinks) continue;
if (entry.d_type == posix::DT_DIR && no_dirs) 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); list.push(path);
} }
return list; return list;
@@ -31,7 +31,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
@pool(allocator) @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_WIN32_FIND_DATAW find_data;
Win32_HANDLE find = win32::findFirstFileW(result, &find_data); Win32_HANDLE find = win32::findFirstFileW(result, &find_data);
if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?; 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)!; String filename = string::temp_from_wstring((WString)&find_data.cFileName)!;
if (filename == ".." || filename == ".") continue; if (filename == ".." || filename == ".") continue;
list.push(path::new(filename, allocator)!); list.push(path::new(allocator, filename)!);
}; };
} while (win32::findNextFileW(find, &find_data)); } while (win32::findNextFileW(find, &find_data));
return list; return list;

View File

@@ -29,7 +29,7 @@ macro bool! native_mkdir(Path path, MkdirPermissions permissions)
@pool() @pool()
{ {
// TODO security attributes // 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()) switch (win32::getLastError())
{ {
case win32::ERROR_ACCESS_DENIED: case win32::ERROR_ACCESS_DENIED:

View File

@@ -26,7 +26,7 @@ macro bool! native_rmdir(Path path)
$case env::WIN32: $case env::WIN32:
@pool() @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()) switch (win32::getLastError())
{ {
case win32::ERROR_ACCESS_DENIED: case win32::ERROR_ACCESS_DENIED:

View File

@@ -16,7 +16,7 @@ fn void! native_rmtree(Path dir)
{ {
String name = ((ZString)&entry.name).str_view(); String name = ((ZString)&entry.name).str_view();
if (!name || name == "." || name == "..") continue; 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) if (entry.d_type == posix::DT_DIR)
{ {
native_rmtree(new_path)!; native_rmtree(new_path)!;
@@ -39,7 +39,7 @@ fn void! native_rmtree(Path path)
{ {
Win32_WIN32_FIND_DATAW find_data; Win32_WIN32_FIND_DATAW find_data;
String s = path.str_view().tconcat("\\*"); 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?; if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?;
defer win32::findClose(find); defer win32::findClose(find);
@@ -47,16 +47,16 @@ fn void! native_rmtree(Path path)
{ {
@pool() @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; 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) if (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY)
{ {
native_rmtree(file_path)!; native_rmtree(file_path)!;
} }
else 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); } while (win32::findNextFileW(find, &find_data) != 0);

View File

@@ -1,23 +1,23 @@
module std::io::os @if(env::LIBC); module std::io::os @if(env::LIBC);
import std::io::path, std::os; 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" }) foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
{ {
String tmpdir = env::get_var(env) ?? ""; String tmpdir = env::tget_var(env) ?? "";
if (tmpdir) return path::new(tmpdir, allocator); 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); Win32_DWORD len = win32::getTempPathW(0, null);
if (!len) return IoError.GENERAL_ERROR?; if (!len) return IoError.GENERAL_ERROR?;
Char16[] buff = mem::temp_alloc_array(Char16, len + (usz)1); Char16[] buff = mem::temp_alloc_array(Char16, len + (usz)1);
if (!win32::getTempPathW(len, buff)) return IoError.GENERAL_ERROR?; 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); module std::io::os @if(env::NO_LIBC);

View File

@@ -2,12 +2,12 @@ module std::io::path;
import std::collections::list, std::io::os; import std::collections::list, std::io::os;
import std::os::win32; 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_WIN32 = '\\';
const char PREFERRED_SEPARATOR_POSIX = '/'; const char PREFERRED_SEPARATOR_POSIX = '/';
const char PREFERRED_SEPARATOR = env::WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX; const char PREFERRED_SEPARATOR = env::WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
def PathList = List(<Path>); def PathList = List { Path };
fault PathResult fault PathResult
{ {
@@ -21,6 +21,7 @@ struct PathImp (Printable)
{ {
String path_string; String path_string;
PathEnv env; PathEnv env;
Allocator allocator;
} }
enum PathEnv enum PathEnv
@@ -29,16 +30,11 @@ enum PathEnv
POSIX POSIX
} }
fn Path! new_cwd(Allocator allocator = allocator::heap()) => @pool(allocator) fn Path! cwd(Allocator allocator)
{
return new(os::getcwd(allocator::temp()), allocator);
}
fn Path! getcwd(Allocator allocator = allocator::heap()) @deprecated("Use new_cwd()")
{ {
@pool(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 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 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 bool exists(Path path) => os::native_file_or_dir_exists(path.str_view());
fn Path! temp_cwd() => new_cwd(allocator::temp()) @inline; fn Path! tcwd() => cwd(tmem()) @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); @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; 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); return c == '/' || (c == '\\' && path_env == PathEnv.WIN32);
} }
macro bool is_posix_separator(char c) macro bool is_posix_separator(char c) => c == '/';
{ macro bool is_win32_separator(char c) => c == '/' || c == '\\';
return c == '/' || c == '\\';
}
macro bool is_win32_separator(char c) fn PathList! ls(Allocator allocator, Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "")
{
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())
{ {
$if $defined(os::native_ls): $if $defined(os::native_ls):
return os::native_ls(dir, no_dirs, no_symlinks, mask, allocator); 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. Create a directory on a given path, optionally recursive.
@param path `The path to create` @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 recursive `If directories in between should be created if they're missing, defaults to false`
@param permissions `The permissions to set on the directory` @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 @typeis(path, String):
if (is_dir(path)) return false; @pool() { return _mkdir(temp(path), recursive, permissions); };
if (exists(path)) return IoError.FILE_NOT_DIR?; $else
return _mkdir(path, recursive, permissions);
if (recursive) $endif
{
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);
}
<* <*
Tries to delete directory, which must be empty. Tries to delete directory, which must be empty.
@param path `The path to delete` @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 `true if there was a directory to delete, false otherwise`
@return! PathResult.INVALID_PATH `if the path was invalid` @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?; $if @typeis(path, String):
return os::native_rmdir(path); @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` @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` @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) 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; 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. Append the string to the current path.
@param [in] filename @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)); assert(!is_separator(self.path_string[^1], self.env));
@pool(allocator) @pool(allocator)
@@ -202,35 +193,37 @@ fn Path! Path.new_append(self, String filename, Allocator allocator = allocator:
dstr.append(self.path_string); dstr.append(self.path_string);
dstr.append(PREFERRED_SEPARATOR); dstr.append(PREFERRED_SEPARATOR);
dstr.append(filename); 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! start_of_base_name(String str, PathEnv path_env) @local
fn usz Path.start_of_base_name(self) @local
{ {
String path_str = self.path_string; if (!str.len) return 0;
if (!path_str.len) return 0; usz! start_slash = str.rindex_of_char('/');
if (self.env == PathEnv.WIN32) 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('\\'))
{ {
if (try start_slash && start_slash > index) return start_slash + 1;
// c:\ style path, we're done! // c:\ style path, we're done!
if (path_str[0] != '\\') return index + 1; if (str[0] != '\\') return index + 1;
// Handle \\server\foo // Handle \\server\foo
// Find the \ before "foo" // Find the \ before "foo"
usz last_index = 2 + path_str[2..].index_of_char('\\')!!; usz last_index = 2 + str[2..].index_of_char('\\')!;
// If they don't match, we're done // 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 PathResult.INVALID_PATH?;
if (last_index != index) return index + 1; if (last_index != index) return index + 1;
// Otherwise just default to the volume length. // Otherwise just default to the volume length.
} }
return volume_name_len(path_str, self.env)!!; return volume_name_len(str, path_env)!!;
} }
return path_str.rindex_of_char('/') + 1 ?? 0;
fn bool! String.is_absolute_path(self) => @pool()
{
return temp(self).is_absolute();
} }
fn bool! Path.is_absolute(self) 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); 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(); String path_str = self.str_view();
if (!path_str.len) return PathResult.INVALID_PATH?; 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 == ".") if (path_str == ".")
{ {
@pool(allocator) @pool(allocator)
{ {
String cwd = os::getcwd(allocator::temp())!; String cwd = os::getcwd(tmem())!;
return new(cwd, allocator, self.env); return new(allocator, cwd, self.env);
}; };
} }
$if DEFAULT_PATH_ENV == WIN32: $if DEFAULT_ENV == WIN32:
@pool(allocator) @pool(allocator)
{ {
const usz BUFFER_LEN = 4096; const usz BUFFER_LEN = 4096;
WString buffer = (WString)mem::temp_alloc_array(Char16, BUFFER_LEN); 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?; if (!buffer) return PathResult.INVALID_PATH?;
return { string::new_from_wstring(buffer, allocator), WIN32 }; return { string::new_from_wstring(allocator, buffer), WIN32, allocator };
}; };
$else $else
String cwd = os::getcwd(allocator::temp())!; String cwd = os::getcwd(tmem())!;
return (Path){ cwd, self.env }.new_append(path_str, allocator)!; return (Path){ cwd, self.env, tmem() }.append(allocator, path_str)!;
$endif $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) 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; String path_str = self.path_string;
if (basename_start == path_str.len) return ""; if (basename_start == path_str.len) return "";
return path_str[basename_start..]; 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) fn String Path.dirname(self)
{ {
usz basename_start = self.start_of_base_name();
String path_str = self.path_string; String path_str = self.path_string;
usz basename_start = start_of_base_name(path_str, self.env)!!;
if (basename_start == 0) return "."; if (basename_start == 0) return ".";
usz start = volume_name_len(path_str, self.env)!!; usz start = volume_name_len(path_str, self.env)!!;
if (basename_start <= start + 1) if (basename_start <= start + 1)
@@ -304,6 +311,7 @@ fn String Path.dirname(self)
return path_str[:basename_start - 1]; return path_str[:basename_start - 1];
} }
<* <*
Test if the path has the given extension, so given the path /foo/bar.c3 Test if the path has the given extension, so given the path /foo/bar.c3
this would be true matching the extension "c3" this would be true matching the extension "c3"
@@ -337,6 +345,16 @@ fn String Path.volume_name(self)
return self.path_string[:len]; 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 fn usz! volume_name_len(String path, PathEnv path_env) @local
{ {
usz len = path.len; usz len = path.len;
@@ -387,13 +405,13 @@ fn Path! Path.parent(self)
{ {
if (is_separator(c, self.env)) 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?; 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; if (!path_str.len) return path_str;
usz path_start = volume_name_len(path_str, path_env)!; 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 Walk the path recursively. PathWalker is run on every file and
directory found. Return true to abort the walk. 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) fn bool! Path.walk(self, PathWalker w, void* data)
{ {
const PATH_MAX = 512; const PATH_MAX = 512;
@stack_mem(PATH_MAX; Allocator allocator) @stack_mem(PATH_MAX; Allocator allocator)
{ {
Path abs = self.new_absolute(allocator)!; Path abs = self.absolute(allocator)!;
PathList files = new_ls(abs, allocator: allocator)!; PathList files = ls(allocator, abs)!;
foreach (f : files) foreach (f : files)
{ {
if (f.str_view() == "." || f.str_view() == "..") continue; 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); bool is_directory = is_dir(f);
if (w(f, is_directory, data)!) return true; if (w(f, is_directory, data)!) return true;
if (is_directory && f.walk(w, 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; 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 fn String Path.str_view(self) @inline
{ {
return self.path_string; return self.path_string;
@@ -576,26 +623,19 @@ fn bool Path.has_suffix(self, String str)
return self.str_view().ends_with(str); return self.str_view().ends_with(str);
} }
fn void Path.free_with_allocator(self, Allocator allocator) <*
{ @require self.allocator != null : "This Path should never be freed"
allocator::free(allocator, self.path_string.ptr); *>
}
fn void Path.free(self) 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 fn usz! Path.to_format(&self, Formatter* formatter) @dynamic
{ {
return formatter.print(self.str_view()); 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 = { const bool[256] RESERVED_PATH_CHAR_POSIX = {
[0] = true, [0] = true,
@@ -619,9 +659,29 @@ macro bool is_reserved_win32_path_char(char c)
return RESERVED_PATH_CHAR_WIN32[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 return path_env == PathEnv.WIN32
? RESERVED_PATH_CHAR_WIN32[c] ? RESERVED_PATH_CHAR_WIN32[c]
: RESERVED_PATH_CHAR_POSIX[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);
}

View File

@@ -77,22 +77,6 @@ macro usz! read_all(stream, char[] buffer)
return n; 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) @require @is_instream(stream)
*> *>

View File

@@ -24,27 +24,9 @@ fn ByteBuffer* ByteBuffer.init(&self, Allocator allocator, usz max_read, usz ini
return self; 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) fn ByteBuffer* ByteBuffer.tinit(&self, usz max_read, usz initial_capacity = 16)
{ {
return self.init(allocator::temp(), max_read, initial_capacity); return self.init(tmem(), 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);
} }
<* <*

View File

@@ -8,18 +8,6 @@ struct ByteWriter (OutStream)
Allocator allocator; 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] self
@param [&inout] allocator @param [&inout] allocator
@@ -39,17 +27,7 @@ fn ByteWriter* ByteWriter.init(&self, Allocator allocator)
*> *>
fn ByteWriter* ByteWriter.tinit(&self) fn ByteWriter* ByteWriter.tinit(&self)
{ {
return self.init(allocator::temp()) @inline; return self.init(tmem()) @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;
} }
fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data) fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data)

View File

@@ -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] self
@param [&inout] allocator @param [&inout] allocator
@@ -40,16 +26,6 @@ fn MultiReader* MultiReader.init(&self, Allocator allocator, InStream... readers
return self; 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 @param [&inout] self
@require self.readers.len == 0 "Init may not run on already initialized data" @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) fn MultiReader* MultiReader.tinit(&self, InStream... readers)
{ {
return self.init(allocator::temp(), ...readers); return self.init(tmem(), ...readers);
} }
fn void MultiReader.free(&self) fn void MultiReader.free(&self)

View File

@@ -23,30 +23,6 @@ fn MultiWriter* MultiWriter.init(&self, Allocator allocator, OutStream... writer
return self; 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 @param [&inout] self
@require writers.len > 0 @require writers.len > 0
@@ -54,7 +30,7 @@ fn MultiWriter* MultiWriter.temp_init(&self, OutStream... writers) @deprecated("
*> *>
fn MultiWriter* MultiWriter.tinit(&self, OutStream... writers) fn MultiWriter* MultiWriter.tinit(&self, OutStream... writers)
{ {
return self.init(allocator::temp(), ...writers); return self.init(tmem(), ...writers);
} }
fn void MultiWriter.free(&self) fn void MultiWriter.free(&self)

View File

@@ -90,33 +90,33 @@ fault MatrixError
MATRIX_INVERSE_DOESNT_EXIST, MATRIX_INVERSE_DOESNT_EXIST,
} }
def Complexf = Complex(<float>); def Complexf = Complex{float};
def Complex = Complex(<double>); def Complex = Complex{double};
def COMPLEX_IDENTITY = complex::IDENTITY(<double>) @builtin; def COMPLEX_IDENTITY = complex::IDENTITY{double} @builtin;
def COMPLEXF_IDENTITY = complex::IDENTITY(<float>) @builtin; def COMPLEXF_IDENTITY = complex::IDENTITY{float} @builtin;
def Quaternionf = Quaternion(<float>); def Quaternionf = Quaternion{float};
def Quaternion = Quaternion(<double>); def Quaternion = Quaternion{double};
def QUATERNION_IDENTITY = quaternion::IDENTITY(<double>) @builtin; def QUATERNION_IDENTITY = quaternion::IDENTITY{double} @builtin;
def QUATERNIONF_IDENTITY = quaternion::IDENTITY(<float>) @builtin; def QUATERNIONF_IDENTITY = quaternion::IDENTITY{float} @builtin;
def Matrix2f = Matrix2x2(<float>); def Matrix2f = Matrix2x2{float};
def Matrix2 = Matrix2x2(<double>); def Matrix2 = Matrix2x2{double};
def Matrix3f = Matrix3x3(<float>); def Matrix3f = Matrix3x3{float};
def Matrix3 = Matrix3x3(<double>); def Matrix3 = Matrix3x3{double};
def Matrix4f = Matrix4x4(<float>); def Matrix4f = Matrix4x4{float};
def Matrix4 = Matrix4x4(<double>); def Matrix4 = Matrix4x4{double};
def matrix4_ortho = matrix::ortho(<double>) @builtin; def matrix4_ortho = matrix::ortho{double} @builtin;
def matrix4_perspective = matrix::perspective(<double>) @builtin; def matrix4_perspective = matrix::perspective{double} @builtin;
def matrix4f_ortho = matrix::ortho(<float>) @builtin; def matrix4f_ortho = matrix::ortho{float} @builtin;
def matrix4f_perspective = matrix::perspective(<float>) @builtin; def matrix4f_perspective = matrix::perspective{float} @builtin;
def MATRIX2_IDENTITY = matrix::IDENTITY2(<double>) @builtin; def MATRIX2_IDENTITY = matrix::IDENTITY2{double} @builtin;
def MATRIX2F_IDENTITY = matrix::IDENTITY2(<float>) @builtin; def MATRIX2F_IDENTITY = matrix::IDENTITY2{float} @builtin;
def MATRIX3_IDENTITY = matrix::IDENTITY3(<double>) @builtin; def MATRIX3_IDENTITY = matrix::IDENTITY3{double} @builtin;
def MATRIX3F_IDENTITY = matrix::IDENTITY3(<float>) @builtin; def MATRIX3F_IDENTITY = matrix::IDENTITY3{float} @builtin;
def MATRIX4_IDENTITY = matrix::IDENTITY4(<double>) @builtin; def MATRIX4_IDENTITY = matrix::IDENTITY4{double} @builtin;
def MATRIX4F_IDENTITY = matrix::IDENTITY4(<float>) @builtin; def MATRIX4F_IDENTITY = matrix::IDENTITY4{float} @builtin;
<* <*

View File

@@ -1,4 +1,4 @@
module std::math::complex(<Real>); module std::math::complex{Real};
union Complex union Complex
{ {

View File

@@ -1,4 +1,4 @@
module std::math::matrix(<Real>); module std::math::matrix{Real};
import std::math::vector; import std::math::vector;
struct Matrix2x2 struct Matrix2x2

View File

@@ -1,4 +1,4 @@
module std::math::quaternion(<Real>); module std::math::quaternion{Real};
import std::math::vector; import std::math::vector;
union Quaternion union Quaternion
{ {

View File

@@ -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_normalize(Vec3f* v1, Vec3f* v2) => ortho_normalize3(v1, v2);
fn void ortho_normalized(Vec3* v1, Vec3* 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 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 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 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 Vec3 Vec3.rotate_quat(self, Quaternion q) => rotate_by_quat3(self, q);

View File

@@ -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]); (*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);
} }

View File

@@ -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)!; 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) return string::format(allocator, "%s", *self);
{
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)!!; fn String InetAddress.to_tstring(&self)
return res.copy(allocator); {
return string::format(tmem(), "%s", *self);
} }
fn InetAddress! ipv6_from_str(String s) fn InetAddress! ipv6_from_str(String s)

View File

@@ -58,14 +58,9 @@ fn uint! ipv4toint(String s)
return out; 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; 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)!; String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)!;
return res.copy(allocator); return res.copy(allocator);
} }
fn String! int_to_temp_ipv4(uint val)
{
return int_to_new_ipv4(val, allocator::temp());
}

View File

@@ -49,7 +49,7 @@ struct Url(Printable)
@require url_string.len > 0 "the url_string must be len 1 or more" @require url_string.len > 0 "the url_string must be len 1 or more"
@return "the parsed Url" @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. 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" @require url_string.len > 0 "the url_string must be len 1 or more"
@return "the parsed Url" @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(); url_string = url_string.trim();
if (!url_string) return UrlParsingResult.EMPTY?; 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:' // Handle schemes without authority like 'mailto:'
if (!pos) return UrlParsingResult.INVALID_SCHEME?; if (!pos) return UrlParsingResult.INVALID_SCHEME?;
url.scheme = url_string[:pos].copy(allocator); 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; return url;
} }
@@ -98,8 +98,8 @@ fn Url! new_parse(String url_string, Allocator allocator = allocator::heap())
if (!username.len) return UrlParsingResult.INVALID_USER?; if (!username.len) return UrlParsingResult.INVALID_USER?;
url.host = url.host =
url.username = decode(username, HOST, allocator) ?? UrlParsingResult.INVALID_USER?!; url.username = decode(allocator, username, HOST) ?? UrlParsingResult.INVALID_USER?!;
if (userpass.len) url.password = decode(userpass[1], USERPASS, allocator) ?? UrlParsingResult.INVALID_PASSWORD?!; if (userpass.len) url.password = decode(allocator, userpass[1], USERPASS) ?? UrlParsingResult.INVALID_PASSWORD?!;
}; };
authority = authority[userinfo.len + 1 ..]; 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 ..]; 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)) if (@ok(query_index) || @ok(fragment_index))
{ {
usz path_end = min(query_index ?? url_string.len, fragment_index ?? url_string.len); 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 ..]; url_string = url_string[path_end ..];
} }
else else
{ {
url.path = decode(url_string, PATH, allocator) ?? UrlParsingResult.INVALID_PATH?!; url.path = decode(allocator, url_string, PATH) ?? UrlParsingResult.INVALID_PATH?!;
url_string = ""; url_string = "";
} }
@@ -165,86 +165,86 @@ fn Url! new_parse(String url_string, Allocator allocator = allocator::heap())
// Parse fragment // Parse fragment
if (url_string.starts_with("#")) 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; return url;
} }
<* fn usz! Url.to_format(&self, Formatter* f) @dynamic
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)
{ {
DString builder = dstring::temp_new(); usz len;
// Add scheme if it exists // Add scheme if it exists
if (self.scheme != "") if (self.scheme != "")
{ {
builder.append_chars(self.scheme); len += f.print(self.scheme)!;
builder.append_char(':'); len += f.print(":")!;
if (self.host.len > 0) builder.append_chars("//"); if (self.host.len > 0) len += f.print("//")!;
} }
// Add username and password if they exist // Add username and password if they exist
if (self.username != "") if (self.username)
{ {
String username = temp_encode(self.username, USERPASS); @stack_mem(64; Allocator smem)
builder.append_chars(username);
if (self.password != "")
{ {
builder.append_char(':'); len += f.print(encode(smem, self.username, USERPASS))!;
};
String password = temp_encode(self.password, USERPASS); if (self.password)
builder.append_chars(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 // Add host
String host = temp_encode(self.host, HOST); @stack_mem(128; Allocator smem)
builder.append_chars(host); {
len += f.print(encode(smem, self.host, HOST))!;
};
// Add port // Add port
if (self.port != 0) if (self.port) len += f.printf(":%d", self.port)!;
{
builder.append_char(':');
builder.appendf("%d", self.port);
}
// Add path // Add path
String path = temp_encode(self.path, PATH); @stack_mem(256; Allocator smem)
builder.append_chars(path); {
len += f.print(encode(smem, self.path, PATH))!;
};
// Add query if it exists (note that `query` is expected to // Add query if it exists (note that `query` is expected to
// be already properly encoded). // be already properly encoded).
if (self.query != "") if (self.query)
{ {
builder.append_char('?'); len += f.print("?")!;
builder.append_chars(self.query); len += f.print(self.query)!;
} }
// Add fragment if it exists // Add fragment if it exists
if (self.fragment != "") if (self.fragment)
{ {
builder.append_char('#'); @stack_mem(256; Allocator smem)
{
String fragment = temp_encode(self.fragment, FRAGMENT); len += f.print("#")!;
builder.append_chars(fragment); len += f.print(encode(smem, self.fragment, FRAGMENT))!;
};
}
return len;
} }
return builder.copy_str(allocator); fn String Url.to_string(&self, Allocator allocator)
{
return string::format(allocator, "%s", *self);
} }
def UrlQueryValueList = List(<String>);
def UrlQueryValueList = List{String};
struct UrlQueryValues struct UrlQueryValues
{ {
inline HashMap(<String, UrlQueryValueList>) map; inline HashMap{String, UrlQueryValueList} map;
UrlQueryValueList key_order; UrlQueryValueList key_order;
} }
@@ -254,15 +254,7 @@ struct UrlQueryValues
@param [in] query @param [in] query
@return "a UrlQueryValues HashMap" @return "a UrlQueryValues HashMap"
*> *>
fn UrlQueryValues temp_parse_query(String query) => parse_query(query, allocator::temp()); 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
@return "a UrlQueryValues HashMap"
*>
fn UrlQueryValues new_parse_query(String query) => parse_query(query, allocator::heap());
<* <*
Parse the query parameters of the Url into a UrlQueryValues map. 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 @param [inout] allocator
@return "a UrlQueryValues HashMap" @return "a UrlQueryValues HashMap"
*> *>
fn UrlQueryValues parse_query(String query, Allocator allocator) fn UrlQueryValues parse_query(Allocator allocator, String query)
{ {
UrlQueryValues vals; UrlQueryValues vals;
vals.map.init(allocator); vals.map.init(allocator);
@@ -283,8 +275,8 @@ fn UrlQueryValues parse_query(String query, Allocator allocator)
@pool(allocator) @pool(allocator)
{ {
String[] parts = rv.tsplit("=", 2); String[] parts = rv.tsplit("=", 2);
String key = temp_decode(parts[0], QUERY) ?? parts[0]; String key = tdecode(parts[0], QUERY) ?? parts[0];
vals.add(key, parts.len == 1 ? key : (temp_decode(parts[1], QUERY) ?? parts[1])); vals.add(key, parts.len == 1 ? key : (tdecode(parts[1], QUERY) ?? parts[1]));
}; };
} }
return vals; return vals;
@@ -317,40 +309,34 @@ fn UrlQueryValues* UrlQueryValues.add(&self, String key, String value)
} }
<*
Stringify UrlQueryValues into an encoded query string.
@param [in] self fn usz! UrlQueryValues.to_format(&self, Formatter* f) @dynamic
@param [inout] allocator
@return "a percent-encoded query string"
*>
fn String UrlQueryValues.to_string(&self, Allocator allocator = allocator::heap()) @dynamic => @pool(allocator)
{ {
DString builder = dstring::temp_new(); usz len;
usz i; usz i;
foreach (key: self.key_order) foreach (key: self.key_order)
{ {
String encoded_key = temp_encode(key, QUERY); @stack_mem(128; Allocator mem)
{
String encoded_key = encode(mem, key, QUERY);
UrlQueryValueList! values = self.map.get(key); UrlQueryValueList! values = self.map.get(key);
if (catch values) continue; if (catch values) continue;
foreach (value : values) foreach (value : values)
{ {
if (i > 0) builder.append_char('&'); if (i > 0) len += f.print("&")!;
len += f.print(encoded_key)!;
builder.append_chars(encoded_key); len += f.print("=")!;
builder.append_char('='); @stack_mem(256; Allocator smem)
{
String encoded_value = temp_encode(value, QUERY); len += f.print(encode(smem, value, QUERY))!;
builder.append_chars(encoded_value); };
i++; i++;
} }
}; };
return builder.copy_str(allocator);
} }
return len;
}
fn void UrlQueryValues.free(&self) fn void UrlQueryValues.free(&self)
{ {

View File

@@ -67,7 +67,7 @@ fn usz encode_len(String s, UrlEncodingMode mode) @inline
@param [inout] allocator @param [inout] allocator
@return "Percent-encoded String" @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); usz n = encode_len(s, mode);
DString builder = dstring::temp_with_capacity(n); 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 // add encoded char
case should_encode(c, mode): case should_encode(c, mode):
builder.append_char('%'); builder.append_char('%');
String hex = hex::encode_temp(s[i:1]); String hex = hex::tencode(s[i:1]);
builder.append(hex.temp_ascii_to_upper()); builder.append(hex.to_upper_tcopy());
// use char, no encoding needed // use char, no encoding needed
default: default:
@@ -95,15 +95,6 @@ fn String encode(String s, UrlEncodingMode mode, Allocator allocator) => @pool(a
return builder.copy_str(allocator); 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. 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" @param mode "Url encoding mode"
@return "Percent-encoded String" @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. Calculate the length of the percent-decoded string.
@@ -143,7 +134,7 @@ fn usz! decode_len(String s, UrlEncodingMode mode) @inline
@param [inout] allocator @param [inout] allocator
@return "Percent-decoded String" @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)!; usz n = decode_len(s, mode)!;
DString builder = dstring::temp_with_capacity(n); DString builder = dstring::temp_with_capacity(n);
@@ -154,7 +145,7 @@ fn String! decode(String s, UrlEncodingMode mode, Allocator allocator) => @pool
{ {
// decode encoded char // decode encoded char
case '%': case '%':
char[] hex = hex::decode_temp(s[i+1:2])!; char[] hex = hex::tdecode(s[i+1:2])!;
builder.append(hex); builder.append(hex);
i += 2; i += 2;
@@ -171,15 +162,6 @@ fn String! decode(String s, UrlEncodingMode mode, Allocator allocator) => @pool
return builder.copy_str(allocator); 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. 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" @param mode "Url encoding mode"
@return "Percent-decoded String" @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);

View File

@@ -55,7 +55,7 @@ fn void Backtrace.free(&self)
allocator::free(self.allocator, self.file); 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) if (!allocator)
{ {
@@ -91,13 +91,13 @@ fn void*[] capture_current(void*[] buffer)
$endswitch $endswitch
} }
def BacktraceList = List(<Backtrace>); def BacktraceList = List{Backtrace};
def symbolize_backtrace = linux::symbolize_backtrace @if(env::LINUX); def symbolize_backtrace = linux::symbolize_backtrace @if(env::LINUX);
def symbolize_backtrace = win32::symbolize_backtrace @if(env::WIN32); def symbolize_backtrace = win32::symbolize_backtrace @if(env::WIN32);
def symbolize_backtrace = darwin::symbolize_backtrace @if(env::DARWIN); 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 {}; return {};
} }

View File

@@ -9,7 +9,7 @@ import std::io::path, libc, std::os;
@require name.len > 0 @require name.len > 0
@return! SearchResult.MISSING @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 $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 // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getenvironmentvariable
const usz BUFSIZE = 1024; const usz BUFSIZE = 1024;
WString buff = (WString)tcalloc(BUFSIZE * 2 + 2); 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); usz len = win32::getEnvironmentVariableW(wstr, buff, BUFSIZE);
if (len == 0) return SearchResult.MISSING?; if (len == 0) return SearchResult.MISSING?;
if (len > BUFSIZE) if (len > BUFSIZE)
@@ -28,15 +28,15 @@ fn String! get_var(String name, Allocator allocator = allocator::heap()) => @poo
buff = (WString)tmalloc(len * 2 + 2); buff = (WString)tmalloc(len * 2 + 2);
win32::getEnvironmentVariableW(wstr, buff, (Win32_DWORD)len); win32::getEnvironmentVariableW(wstr, buff, (Win32_DWORD)len);
} }
return string::new_from_wstring(buff, allocator); return string::new_from_wstring(allocator, buff);
$default: $default:
return ""; return "";
$endswitch $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 $switch
$case env::WIN32: $case env::WIN32:
WString wname = name.to_temp_wstring()!!; WString wname = name.to_wstring_tcopy()!!;
if (!overwrite) if (!overwrite)
{ {
Char16[8] buff; Char16[8] buff;
if (win32::getEnvironmentVariableW(wname, &buff, 8) > 0) return true; if (win32::getEnvironmentVariableW(wname, &buff, 8) > 0) return true;
} }
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setenvironmentvariable // 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: $case env::LIBC && !env::WIN32:
return libc::setenv(name.zstr_tcopy(), value.zstr_tcopy(), (int)overwrite) == 0; return libc::setenv(name.zstr_tcopy(), value.zstr_tcopy(), (int)overwrite) == 0;
$default: $default:
@@ -74,30 +74,26 @@ fn String! get_home_dir(Allocator using = allocator::heap())
$else $else
home = "USERPROFILE"; home = "USERPROFILE";
$endif $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. 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: $if env::WIN32:
return path::new(get_var_temp("AppData"), allocator); return path::new(allocator, tget_var("AppData"));
$else $else
$if env::DARWIN: $if env::DARWIN:
String s = get_var_temp("HOME")!; String s = tget_var("HOME")!;
const DIR = "Library/Application Support"; const DIR = "Library/Application Support";
$else $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"; const DIR = ".config";
$endif $endif
return path::temp_new(s).new_append(DIR, allocator: allocator); return path::temp(s).append(allocator, DIR);
$endif $endif
} }
@@ -110,7 +106,7 @@ fn bool clear_var(String name) => @pool()
{ {
$switch $switch
$case env::WIN32: $case env::WIN32:
WString wname = name.to_temp_wstring()!!; WString wname = name.to_wstring_tcopy()!!;
return win32::setEnvironmentVariableW(wname, null) == 0; return win32::setEnvironmentVariableW(wname, null) == 0;
$case env::LIBC && !env::WIN32: $case env::LIBC && !env::WIN32:
return libc::unsetenv(name.zstr_tcopy()) == 0; return libc::unsetenv(name.zstr_tcopy()) == 0;
@@ -119,12 +115,7 @@ fn bool clear_var(String name) => @pool()
$endswitch $endswitch
} }
fn String! executable_path(Allocator allocator = allocator::heap()) @deprecated("use new_executable_path()") fn String! executable_path(Allocator allocator)
{
return new_executable_path(allocator) @inline;
}
fn String! new_executable_path(Allocator allocator = allocator::heap())
{ {
$if env::DARWIN: $if env::DARWIN:
return darwin::executable_path(allocator); return darwin::executable_path(allocator);

View File

@@ -128,17 +128,17 @@ fn ulong! elf_module_image_base(String path) @local
return 0; 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); 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 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 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)})!; 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); 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; ZString obj_path = info.dli_fname;
String sname = info.dli_sname ? info.dli_sname.str_view() : "???"; 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)})!; 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 "); String[] parts = string.trim().tsplit(" at ");
if (parts.len != 2) return SearchResult.MISSING?; 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 .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)"); String[] inline_parts = addr2line.tsplit("(inlined by)");
usz last = inline_parts.len - 1; usz last = inline_parts.len - 1;
foreach (i, part : inline_parts) foreach (i, part : inline_parts)
{ {
bool is_inline = i != last; 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) if (catch trace)
{ {
list.push({ 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) if (!addr)
{ {
@@ -209,13 +209,13 @@ fn void! backtrace_add_element(BacktraceList *list, void* addr, Allocator alloca
Linux_Dl_info info; Linux_Dl_info info;
if (dladdr(addr, &info) == 0) 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; BacktraceList list;
list.init(allocator, backtrace.len); list.init(allocator, backtrace.len);
@@ -231,7 +231,7 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
{ {
foreach (addr : backtrace) foreach (addr : backtrace)
{ {
backtrace_add_element(&list, addr, allocator)!; backtrace_add_element(allocator, &list, addr)!;
} }
}; };
return list; return list;

View File

@@ -80,7 +80,7 @@ fn uptr! load_address() @local
{ {
Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT"); Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT");
if (!cmd) return BacktraceFault.SEGMENT_NOT_FOUND?; 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(); uint dyld_count = darwin::_dyld_image_count();
for (uint i = 0; i < dyld_count; i++) 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()!; void *load_addr = (void *)load_address()!;
BacktraceList list; BacktraceList list;

View File

@@ -109,7 +109,7 @@ fn WString convert_command_line_win32(String[] command_line) @inline @if(env::WI
str.append('"'); str.append('"');
} }
str.append('\0'); 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");
} }
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); int fd = win32::_open_osfhandle((iptr)wr, 0);
if (fd != -1) if (fd != -1)

View File

@@ -154,7 +154,7 @@ struct Symbol
Win32_DWORD64 displacement; Win32_DWORD64 displacement;
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator) fn BacktraceList! symbolize_backtrace(Allocator allocator, void*[] backtrace)
{ {
BacktraceList list; BacktraceList list;
list.init(allocator, backtrace.len); list.init(allocator, backtrace.len);
@@ -163,12 +163,12 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
defer symCleanup(process); defer symCleanup(process);
foreach (addr : backtrace) 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; 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; Symbol symbol;
//Win32_DWORD image_type = load_modules()!; //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; ZString zname = (ZString)&name;
if (!symGetLineFromAddr64(process, (Win32_ULONG64)addr - 1, &offset, &line)) 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; return backtrace;
} }
String filename = ((ZString)line.fileName).str_view(); 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; return backtrace;
} }

View File

@@ -11,20 +11,20 @@ Sort list using the counting sort algorithm.
macro countingsort(list, key_fn = EMPTY_MACRO_SLOT) @builtin macro countingsort(list, key_fn = EMPTY_MACRO_SLOT) @builtin
{ {
usz len = sort::len_from_list(list); 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 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 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(<Type, KeyFn>); module std::sort::cs{Type, KeyFn};
def Counts = usz[256] @private; def Counts = usz[256] @private;
def Ranges = usz[257] @private; def Ranges = usz[257] @private;

View File

@@ -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): $if @typekind(list) == POINTER &&& (@typekind(*list) == ARRAY || @typekind(*list) == VECTOR):
$typeof((*list)[0])[] list2 = list; $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 $else
usz len = sort::len_from_list(list); 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 $endif
} }
module std::sort::is(<Type, CmpFn, Context>); module std::sort::is{Type, CmpFn, Context};
def ElementType = $typeof(((Type){})[0]); def ElementType = $typeof(((Type){})[0]);

View File

@@ -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): $if @typekind(list) == POINTER &&& (@typekind(*list) == ARRAY || @typekind(*list) == VECTOR):
$typeof((*list)[0])[] list2 = list; $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 $else
usz len = sort::len_from_list(list); 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 $endif
} }
@@ -30,10 +30,10 @@ list will be partially sorted.
macro quickselect(list, isz k, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin macro quickselect(list, isz k, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
{ {
usz len = sort::len_from_list(list); 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(<Type, CmpFn, Context>); module std::sort::qs{Type, CmpFn, Context};
def ElementType = $typeof(((Type){})[0]); def ElementType = $typeof(((Type){})[0]);

View File

@@ -1,4 +1,4 @@
module std::thread::channel(<Type>); module std::thread::channel{Type};
distinct BufferedChannel = void*; distinct BufferedChannel = void*;
@@ -21,11 +21,6 @@ struct BufferedChannelImpl @private
Type[?] buf; 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) fn void! BufferedChannel.init(&self, Allocator allocator, usz size = 1)
{ {
BufferedChannelImpl* channel = allocator::new_with_padding(allocator, BufferedChannelImpl, Type.sizeof * size)!; BufferedChannelImpl* channel = allocator::new_with_padding(allocator, BufferedChannelImpl, Type.sizeof * size)!;

View File

@@ -1,4 +1,4 @@
module std::thread::pool(<SIZE>); module std::thread::pool{SIZE};
import std::thread; import std::thread;
struct ThreadPool struct ThreadPool

View File

@@ -1,4 +1,4 @@
module std::thread::channel(<Type>); module std::thread::channel{Type};
distinct UnbufferedChannel = void*; distinct UnbufferedChannel = void*;
@@ -18,8 +18,6 @@ struct UnbufferedChannelImpl @private
ConditionVariable read_cond; ConditionVariable read_cond;
} }
fn void! UnbufferedChannel.new_init(&self) @deprecated("Use init") => self.init(allocator::heap());
fn void! UnbufferedChannel.init(&self, Allocator allocator) fn void! UnbufferedChannel.init(&self, Allocator allocator)
{ {
UnbufferedChannelImpl* channel = allocator::alloc(allocator, UnbufferedChannelImpl); UnbufferedChannelImpl* channel = allocator::alloc(allocator, UnbufferedChannelImpl);

View File

@@ -20,59 +20,54 @@ enum DateTimeFormat
TIMEONLY, // "15:04:05" TIMEONLY, // "15:04:05"
} }
fn String format(DateTimeFormat type, TzDateTime dt, Allocator allocator) fn String format(Allocator allocator, DateTimeFormat type, TzDateTime dt)
{ {
switch (type) switch (type)
{ {
case ANSIC: 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: 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: 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: 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 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: 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: 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 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: 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 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: 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: case RFC3339:
dt = dt.to_gmt_offset(0); 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: 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: case RFC3339MS:
dt = dt.to_gmt_offset(0); 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: 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: 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: 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: 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 tformat(DateTimeFormat dt_format, TzDateTime dt) => format(tmem(), dt_format, dt);
fn String temp_format(DateTimeFormat dt_format, TzDateTime dt) => format(dt_format, dt, allocator::temp());
fn String TzDateTime.format(self, DateTimeFormat dt_format, Allocator allocator) => format(dt_format, self, allocator); fn String TzDateTime.format(self, Allocator allocator, DateTimeFormat dt_format) => format(allocator, dt_format, self);
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());
// .with_gmt_offset(0) instead of .to_local() is used to avoid surprises when user is formatting to a representation without a timezone // .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.format(self, Allocator allocator, DateTimeFormat dt_format) => format(allocator, dt_format, self.with_gmt_offset(0));
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());
<* <*
Returns the timezone offset in the format of "+HHMM" or "-HHMM" Returns the timezone offset in the format of "+HHMM" or "-HHMM"

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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);
}

View File

@@ -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--;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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?;
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}
}

Some files were not shown because too many files have changed in this diff Show More