mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
First 0.7 update, removing all deprecated features.
This commit is contained in:
committed by
Christoffer Lerno
parent
cff6697818
commit
2a895ec7be
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
*>
|
*>
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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?;
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
<*
|
<*
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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);
|
|
||||||
|
|
||||||
|
|
||||||
<*
|
<*
|
||||||
|
|||||||
@@ -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));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -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?;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
<*
|
<*
|
||||||
|
|||||||
@@ -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?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
<*
|
<*
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
*>
|
*>
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<*
|
<*
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|
||||||
<*
|
<*
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
module std::math::complex(<Real>);
|
module std::math::complex{Real};
|
||||||
|
|
||||||
union Complex
|
union Complex
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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());
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 {};
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|
||||||
|
|||||||
@@ -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)!;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
module std::thread::pool(<SIZE>);
|
module std::thread::pool{SIZE};
|
||||||
import std::thread;
|
import std::thread;
|
||||||
|
|
||||||
struct ThreadPool
|
struct ThreadPool
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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);
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
171
lib7/std/bits.c3
171
lib7/std/bits.c3
@@ -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);
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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--;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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?;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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
Reference in New Issue
Block a user