First 0.7 update, removing all deprecated features.

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

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2023 Eduardo José Gómez Hernández. All rights reserved.
// 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>);
module std::atomic::types{Type};
struct Atomic
{

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2024 Christoffer Lerno. All rights reserved.
// Copyright (c) 2024-2025 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::anylist;
@@ -16,16 +16,6 @@ struct AnyList (Printable)
}
<*
Use `init` for to use a custom allocator.
@param initial_capacity "The initial capacity to reserve"
*>
fn AnyList* AnyList.new_init(&self, usz initial_capacity = 16, Allocator allocator = null) @deprecated("Use init(mem)")
{
return self.init(allocator ?: allocator::heap(), initial_capacity) @inline;
}
<*
@param [&inout] allocator "The allocator to use"
@param initial_capacity "The initial capacity to reserve"
@@ -47,17 +37,6 @@ fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16)
return self;
}
<*
Initialize the list using the temp allocator.
@param initial_capacity "The initial capacity to reserve"
*>
fn AnyList* AnyList.temp_init(&self, usz initial_capacity = 16) @deprecated("Use tinit")
{
return self.init(allocator::temp(), initial_capacity) @inline;
}
<*
Initialize the list using the temp allocator.
@@ -65,7 +44,7 @@ fn AnyList* AnyList.temp_init(&self, usz initial_capacity = 16) @deprecated("Use
*>
fn AnyList* AnyList.tinit(&self, usz initial_capacity = 16)
{
return self.init(allocator::temp(), initial_capacity) @inline;
return self.init(tmem(), initial_capacity) @inline;
}
fn usz! AnyList.to_format(&self, Formatter* formatter) @dynamic
@@ -88,19 +67,6 @@ fn usz! AnyList.to_format(&self, Formatter* formatter) @dynamic
}
}
fn String AnyList.to_new_string(&self, Allocator allocator = null) @dynamic
{
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
}
fn String AnyList.to_string(&self, Allocator allocator) @dynamic
{
return string::format("%s", *self, allocator: allocator);
}
fn String AnyList.to_tstring(&self) => string::tformat("%s", *self);
<*
Push an element on the list by cloning it.
*>
@@ -141,35 +107,19 @@ macro AnyList.pop(&self, $Type)
Pop the last value and allocate the copy using the given allocator.
@return! IteratorResult.NO_MORE_ELEMENT
*>
fn any! AnyList.copy_pop(&self, Allocator allocator = allocator::heap())
fn any! AnyList.copy_pop(&self, Allocator allocator)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
return allocator::clone_any(allocator, self.entries[--self.size]);
}
<*
Pop the last value and allocate the copy using the given allocator.
@return! IteratorResult.NO_MORE_ELEMENT
@deprecated `use copy_pop`
*>
fn any! AnyList.new_pop(&self, Allocator allocator = allocator::heap())
{
return self.copy_pop(allocator);
}
<*
Pop the last value and allocate the copy using the temp allocator
@return! IteratorResult.NO_MORE_ELEMENT
@deprecated `use tcopy_pop`
*>
fn any! AnyList.temp_pop(&self) => self.copy_pop(allocator::temp());
<*
Pop the last value and allocate the copy using the temp allocator
@return! IteratorResult.NO_MORE_ELEMENT
*>
fn any! AnyList.tcopy_pop(&self) => self.copy_pop(allocator::temp());
fn any! AnyList.tcopy_pop(&self) => self.copy_pop(tmem());
<*
Pop the last value. It must later be released using list.free_element()
@@ -210,17 +160,9 @@ fn any! AnyList.pop_first_retained(&self)
return self.entries[0];
}
<*
Same as new_pop() but pops the first value instead.
@deprecated `use copy_pop_first`
*>
fn any! AnyList.new_pop_first(&self, Allocator allocator = allocator::heap())
{
return self.copy_pop_first(allocator) @inline;
}
<*
Same as new_pop() but pops the first value instead.
Same as copy_pop() but pops the first value instead.
*>
fn any! AnyList.copy_pop_first(&self, Allocator allocator = allocator::heap())
{
@@ -233,13 +175,7 @@ fn any! AnyList.copy_pop_first(&self, Allocator allocator = allocator::heap())
<*
Same as temp_pop() but pops the first value instead.
*>
fn any! AnyList.tcopy_pop_first(&self) => self.copy_pop_first(allocator::temp());
<*
Same as temp_pop() but pops the first value instead.
@deprecated `use tcopy_pop_first`
*>
fn any! AnyList.temp_pop_first(&self) => self.new_pop_first(allocator::temp());
fn any! AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem());
<*
@require index < self.size
@@ -477,7 +413,7 @@ fn void AnyList.reserve(&self, usz min_capacity)
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = allocator::heap();
if (!self.allocator) self.allocator = tmem();
min_capacity = math::next_power_of_2(min_capacity);
self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity);
self.capacity = min_capacity;

View File

@@ -1,7 +1,7 @@
<*
@require SIZE > 0
*>
module std::collections::bitset(<SIZE>);
module std::collections::bitset{SIZE};
def Type = uint;
@@ -70,28 +70,18 @@ fn void BitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline
<*
@require Type.kindof == UNSIGNED_INT
*>
module std::collections::growablebitset(<Type>);
module std::collections::growablebitset{Type};
import std::collections::list;
const BITS = Type.sizeof * 8;
def GrowableBitSetList = List(<Type>);
def GrowableBitSetList = List{Type};
struct GrowableBitSet
{
GrowableBitSetList data;
}
<*
@param initial_capacity
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
*>
fn GrowableBitSet* GrowableBitSet.new_init(&self, usz initial_capacity = 1, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
self.data.init(allocator, initial_capacity);
return self;
}
<*
@param initial_capacity
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
@@ -102,14 +92,9 @@ fn GrowableBitSet* GrowableBitSet.init(&self, Allocator allocator, usz initial_c
return self;
}
fn GrowableBitSet* GrowableBitSet.temp_init(&self, usz initial_capacity = 1) @deprecated("Use tinit()")
{
return self.init(allocator::temp(), initial_capacity) @inline;
}
fn GrowableBitSet* GrowableBitSet.tinit(&self, usz initial_capacity = 1)
{
return self.init(allocator::temp(), initial_capacity) @inline;
return self.init(tmem(), initial_capacity) @inline;
}
fn void GrowableBitSet.free(&self)

View File

@@ -4,7 +4,7 @@
<*
@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;
def ElementPredicate = fn bool(Type *type);
@@ -39,16 +39,6 @@ fn usz! ElasticArray.to_format(&self, Formatter* formatter) @dynamic
}
}
fn String ElasticArray.to_string(&self, Allocator allocator) @dynamic
{
return string::format("%s", *self, allocator: allocator);
}
fn String ElasticArray.to_new_string(&self, Allocator allocator = nul) @dynamic
{
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
}
fn String ElasticArray.to_tstring(&self)
{
return string::tformat("%s", *self);
@@ -160,28 +150,12 @@ fn void ElasticArray.add_array(&self, Type[] array)
<*
IMPORTANT The returned array must be freed using free_aligned.
*>
fn Type[] ElasticArray.to_new_aligned_array(&self)
{
return list_common::list_to_new_aligned_array(Type, self, allocator::heap());
}
<*
IMPORTANT The returned array must be freed using free_aligned.
*>
fn Type[] ElasticArray.to_aligned_array(&self, Allocator allocator)
{
return list_common::list_to_new_aligned_array(Type, self, allocator);
}
<*
@require !type_is_overaligned() : "This function is not available on overaligned types"
*>
macro Type[] ElasticArray.to_new_array(&self)
{
return list_common::list_to_array(Type, self, allocator::heap());
return list_common::list_to_aligned_array(Type, self, allocator);
}
<*
@@ -189,15 +163,15 @@ macro Type[] ElasticArray.to_new_array(&self)
*>
macro Type[] ElasticArray.to_array(&self, Allocator allocator)
{
return list_common::list_to_new_array(Type, self, allocator);
return list_common::list_to_array(Type, self, allocator);
}
fn Type[] ElasticArray.to_tarray(&self)
{
$if type_is_overaligned():
return self.to_aligned_array(allocator::temp());
return self.to_aligned_array(tmem());
$else
return self.to_array(allocator::temp());
return self.to_array(tmem());
$endif;
}

View File

@@ -1,7 +1,7 @@
<*
@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;
struct EnumMap (Printable)
{
@@ -28,21 +28,6 @@ fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic
return n;
}
fn String EnumMap.to_string(&self, Allocator allocator) @dynamic
{
return string::format("%s", *self, allocator: allocator);
}
fn String EnumMap.to_new_string(&self, Allocator allocator = null) @dynamic
{
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
}
fn String EnumMap.to_tstring(&self) @dynamic
{
return string::tformat("%s", *self);
}
<*
@return "The total size of this map, which is the same as the number of enum values"
@pure

View File

@@ -5,10 +5,10 @@
<*
@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;
def EnumSetType = $typefrom(private::type_for_enum_elements(Enum.elements)) @private;
def EnumSetType = $typefrom(type_for_enum_elements(Enum.elements)) @private;
const IS_CHAR_ARRAY = Enum.elements > 128;
distinct EnumSet (Printable) = EnumSetType;
@@ -141,24 +141,7 @@ fn usz! EnumSet.to_format(&set, Formatter* formatter) @dynamic
return n;
}
fn String EnumSet.to_new_string(&set, Allocator allocator = allocator::heap()) @dynamic
{
return string::format("%s", *set, allocator: allocator);
}
fn String EnumSet.to_string(&set, Allocator allocator) @dynamic
{
return string::format("%s", *set, allocator: allocator);
}
fn String EnumSet.to_tstring(&set) @dynamic
{
return string::tformat("%s", *set);
}
module std::collections::enumset::private;
macro typeid type_for_enum_elements(usz $elements)
macro typeid type_for_enum_elements(usz $elements) @local
{
$switch
$case ($elements > 128):

View File

@@ -4,10 +4,25 @@
<*
@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::io @norecurse;
const uint DEFAULT_INITIAL_CAPACITY = 16;
const uint MAXIMUM_CAPACITY = 1u << 31;
const float DEFAULT_LOAD_FACTOR = 0.75;
const VALUE_IS_EQUATABLE = Value.is_eq;
const bool COPY_KEYS = types::implements_copy(Key);
struct Entry
{
uint hash;
Key key;
Value value;
Entry* next;
}
struct HashMap (Printable)
{
Entry*[] table;
@@ -17,17 +32,6 @@ struct HashMap (Printable)
float load_factor;
}
<*
@param [&inout] allocator "The allocator to use"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.new_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = null) @deprecated("Use init(mem)")
{
return self.init(allocator ?: allocator::heap(), capacity, load_factor);
}
<*
@param [&inout] allocator "The allocator to use"
@@ -46,17 +50,6 @@ fn HashMap* HashMap.init(&self, Allocator allocator, uint capacity = DEFAULT_INI
return self;
}
<*
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) @deprecated("Use tinit()")
{
return self.init(allocator::temp(), capacity, load_factor) @inline;
}
<*
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@@ -65,7 +58,7 @@ fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, f
*>
fn HashMap* HashMap.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init(allocator::temp(), capacity, load_factor) @inline;
return self.init(tmem(), capacity, load_factor) @inline;
}
<*
@@ -76,48 +69,11 @@ fn HashMap* HashMap.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
macro HashMap* HashMap.new_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Use init_with_key_values(mem)")
macro HashMap* HashMap.init_with_key_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.init(capacity, load_factor, allocator);
$for (var $i = 0; $i < $vacount; $i += 2)
self.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return self;
}
<*
@param [in] keys "The keys for the HashMap entries"
@param [in] values "The values for the HashMap entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.new_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Use init_from_keys_and_values(mem)")
{
assert(keys.len == values.len);
self.init(allocator, capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
self.set(keys[i], values[i]);
}
return self;
}
<*
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
macro HashMap* HashMap.temp_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) @deprecated("Use tinit_with_key_values")
{
self.tinit(capacity, load_factor);
$for (var $i = 0; $i < $vacount; $i += 2)
self.set($vaarg[$i], $vaarg[$i+1]);
self.set($vaarg[$i], $vaarg[$i + 1]);
$endfor
return self;
}
@@ -131,11 +87,7 @@ macro HashMap* HashMap.temp_init_with_key_values(&self, ..., uint capacity = DEF
*>
macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.tinit(capacity, load_factor);
$for (var $i = 0; $i < $vacount; $i += 2)
self.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return self;
return self.tinit_with_key_values(tmem(), capacity, load_factor);
}
<*
@@ -148,10 +100,10 @@ macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.temp_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Use tinit_from_keys_and_values")
fn HashMap* HashMap.init_from_keys_and_values(&self, Allocator allocator, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
assert(keys.len == values.len);
self.tinit(capacity, load_factor);
self.init(allocator, capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
self.set(keys[i], values[i]);
@@ -159,25 +111,19 @@ fn HashMap* HashMap.temp_init_from_keys_and_values(&self, Key[] keys, Value[] va
return self;
}
<*
@param [in] keys "The keys for the HashMap entries"
@param [in] values "The values for the HashMap entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.tinit_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
fn HashMap* HashMap.tinit_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
assert(keys.len == values.len);
self.tinit(capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
self.set(keys[i], values[i]);
}
return self;
return self.init_from_keys_and_values(tmem(), keys, values, capacity, load_factor);
}
<*
@@ -191,14 +137,6 @@ fn bool HashMap.is_initialized(&map)
return (bool)map.allocator;
}
<*
@param [&in] other_map "The map to copy from."
*>
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map) @deprecated("Use init_from_map(mem, map)")
{
return self.init_from_map(allocator::heap(), other_map) @inline;
}
<*
@param [&inout] allocator "The allocator to use"
@param [&in] other_map "The map to copy from."
@@ -210,20 +148,12 @@ fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map
return self;
}
<*
@param [&in] other_map "The map to copy from."
*>
fn HashMap* HashMap.temp_init_from_map(&map, HashMap* other_map) @deprecated("Use tinit_from_map")
{
return map.init_from_map(allocator::temp(), other_map) @inline;
}
<*
@param [&in] other_map "The map to copy from."
*>
fn HashMap* HashMap.tinit_from_map(&map, HashMap* other_map)
{
return map.init_from_map(allocator::temp(), other_map) @inline;
return map.init_from_map(tmem(), other_map) @inline;
}
fn bool HashMap.is_empty(&map) @inline
@@ -296,7 +226,7 @@ fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
// If the map isn't initialized, use the defaults to initialize it.
if (!map.allocator)
{
map.init(allocator::heap());
map.tinit();
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
@@ -345,31 +275,18 @@ fn void HashMap.free(&map)
map.table = {};
}
fn Key[] HashMap.tcopy_keys(&map)
fn Key[] HashMap.tkeys(&self)
{
return map.copy_keys(allocator::temp()) @inline;
return self.keys(tmem()) @inline;
}
fn Key[] HashMap.key_tlist(&map) @deprecated("Use 'tcopy_keys'")
fn Key[] HashMap.keys(&self, Allocator allocator)
{
return map.copy_keys(allocator::temp()) @inline;
}
if (!self.count) return {};
<*
@deprecated "use copy_keys"
*>
fn Key[] HashMap.key_new_list(&map, Allocator allocator = allocator::heap())
{
return map.copy_keys(allocator) @inline;
}
fn Key[] HashMap.copy_keys(&map, Allocator allocator = allocator::heap())
{
if (!map.count) return {};
Key[] list = allocator::alloc_array(allocator, Key, map.count);
Key[] list = allocator::alloc_array(allocator, Key, self.count);
usz index = 0;
foreach (Entry* entry : map.table)
foreach (Entry* entry : self.table)
{
while (entry)
{
@@ -386,53 +303,36 @@ fn Key[] HashMap.copy_keys(&map, Allocator allocator = allocator::heap())
macro HashMap.@each(map; @body(key, value))
{
map.@each_entry(; Entry* entry) {
map.@each_entry(; Entry* entry)
{
@body(entry.key, entry.value);
};
}
macro HashMap.@each_entry(map; @body(entry))
{
if (map.count)
if (!map.count) return;
foreach (Entry* entry : map.table)
{
foreach (Entry* entry : map.table)
while (entry)
{
while (entry)
{
@body(entry);
entry = entry.next;
}
@body(entry);
entry = entry.next;
}
}
}
<*
@deprecated `use tcopy_values`
*>
fn Value[] HashMap.value_tlist(&map)
fn Value[] HashMap.tvalues(&map)
{
return map.copy_values(allocator::temp()) @inline;
return map.values(tmem()) @inline;
}
fn Value[] HashMap.tcopy_values(&map)
fn Value[] HashMap.values(&self, Allocator allocator)
{
return map.copy_values(allocator::temp()) @inline;
}
<*
@deprecated `use copy_values`
*>
fn Value[] HashMap.value_new_list(&map, Allocator allocator = allocator::heap())
{
return map.copy_values(allocator);
}
fn Value[] HashMap.copy_values(&map, Allocator allocator = allocator::heap())
{
if (!map.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, map.count);
if (!self.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, self.count);
usz index = 0;
foreach (Entry* entry : map.table)
foreach (Entry* entry : self.table)
{
while (entry)
{
@@ -667,3 +567,14 @@ fn Key HashMapKeyIterator.get(&self, usz idx) @operator([])
fn usz HashMapValueIterator.len(self) @operator(len) => self.map.count;
fn usz HashMapKeyIterator.len(self) @operator(len) => self.map.count;
fn usz HashMapIterator.len(self) @operator(len) => self.map.count;
fn uint rehash(uint hash) @inline @private
{
hash ^= (hash >> 20) ^ (hash >> 12);
return hash ^ ((hash >> 7) ^ (hash >> 4));
}
macro uint index_for(uint hash, uint capacity) @private
{
return hash & (capacity - 1);
}

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::linkedlist(<Type>);
module std::collections::linkedlist{Type};
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
@@ -30,23 +30,9 @@ fn LinkedList* LinkedList.init(&self, Allocator allocator)
return self;
}
<*
@return "the initialized list"
*>
fn LinkedList* LinkedList.new_init(&self) @deprecated("Use init(mem)")
{
return self.init(allocator::heap()) @inline;
}
fn LinkedList* LinkedList.temp_init(&self) @deprecated("Use tinit()")
{
return self.init(allocator::temp()) @inline;
}
fn LinkedList* LinkedList.tinit(&self)
{
return self.init(allocator::temp()) @inline;
return self.init(tmem()) @inline;
}
<*
@@ -59,7 +45,7 @@ macro void LinkedList.free_node(&self, Node* node) @private
macro Node* LinkedList.alloc_node(&self) @private
{
if (!self.allocator) self.allocator = allocator::heap();
if (!self.allocator) self.allocator = tmem();
return allocator::alloc(self.allocator, Node);
}

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::list(<Type>);
module std::collections::list{Type};
import std::io, std::math, std::collections::list_common;
def ElementPredicate = fn bool(Type *type);
@@ -33,29 +33,6 @@ fn List* List.init(&self, Allocator allocator, usz initial_capacity = 16)
return self;
}
<*
@param initial_capacity "The initial capacity to reserve"
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
*>
fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
self.allocator = allocator;
self.size = 0;
self.capacity = 0;
self.entries = null;
self.reserve(initial_capacity);
return self;
}
<*
Initialize the list using the temp allocator.
@param initial_capacity "The initial capacity to reserve"
*>
fn List* List.temp_init(&self, usz initial_capacity = 16) @deprecated("Use tinit()")
{
return self.init(allocator::temp(), initial_capacity) @inline;
}
<*
Initialize the list using the temp allocator.
@@ -64,18 +41,7 @@ fn List* List.temp_init(&self, usz initial_capacity = 16) @deprecated("Use tinit
*>
fn List* List.tinit(&self, usz initial_capacity = 16)
{
return self.init(allocator::temp(), initial_capacity) @inline;
}
<*
Initialize a new list with an array.
@param [in] values `The values to initialize the list with.`
@require self.size == 0 "The List must be empty"
*>
fn List* List.new_init_with_array(&self, Type[] values, Allocator allocator = allocator::heap()) @deprecated("Use init_with_array(mem)")
{
return self.init_with_array(allocator, values);
return self.init(tmem(), initial_capacity) @inline;
}
<*
@@ -91,19 +57,6 @@ fn List* List.init_with_array(&self, Allocator allocator, Type[] values)
return self;
}
<*
Initialize a temporary list with an array.
@param [in] values `The values to initialize the list with.`
@require self.size == 0 "The List must be empty"
*>
fn List* List.temp_init_with_array(&self, Type[] values) @deprecated("Use tinit_with_array()")
{
self.tinit(values.len) @inline;
self.add_array(values) @inline;
return self;
}
<*
Initialize a temporary list with an array.
@@ -120,7 +73,7 @@ fn List* List.tinit_with_array(&self, Type[] values)
<*
@require self.capacity == 0 "The List must not be allocated"
*>
fn void List.init_wrapping_array(&self, Type[] types, Allocator allocator = allocator::heap())
fn void List.init_wrapping_array(&self, Allocator allocator, Type[] types)
{
self.allocator = allocator;
self.capacity = types.len;
@@ -148,16 +101,6 @@ fn usz! List.to_format(&self, Formatter* formatter) @dynamic
}
}
fn String List.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return string::format("%s", *self, allocator: allocator);
}
fn String List.to_tstring(&self)
{
return string::tformat("%s", *self);
}
fn void List.push(&self, Type element) @inline
{
self.reserve(1);
@@ -208,25 +151,25 @@ fn void List.add_all(&self, List* other_list)
<*
IMPORTANT The returned array must be freed using free_aligned.
*>
fn Type[] List.to_new_aligned_array(&self, Allocator allocator = allocator::heap())
fn Type[] List.to_aligned_array(&self, Allocator allocator)
{
return list_common::list_to_new_aligned_array(Type, self, allocator);
return list_common::list_to_aligned_array(Type, self, allocator);
}
<*
@require !type_is_overaligned() : "This function is not available on overaligned types"
*>
macro Type[] List.to_new_array(&self, Allocator allocator = allocator::heap())
macro Type[] List.to_array(&self, Allocator allocator)
{
return list_common::list_to_new_array(Type, self, allocator);
return list_common::list_to_array(Type, self, allocator);
}
fn Type[] List.to_tarray(&self)
{
$if type_is_overaligned():
return self.to_new_aligned_array(allocator::temp());
return self.to_aligned_array(tmem());
$else
return self.to_new_array(allocator::temp());
return self.to_array(tmem());
$endif;
}
@@ -398,7 +341,7 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = allocator::heap();
if (!self.allocator) self.allocator = tmem();
self.pre_free(); // Remove sanitizer annotation

View File

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

View File

@@ -1,508 +0,0 @@
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::map(<Key, Value>);
import std::math;
const uint DEFAULT_INITIAL_CAPACITY = 16;
const uint MAXIMUM_CAPACITY = 1u << 31;
const float DEFAULT_LOAD_FACTOR = 0.75;
const VALUE_IS_EQUATABLE = Value.is_eq;
const bool COPY_KEYS = types::implements_copy(Key);
distinct Map = void*;
struct MapImpl
{
Entry*[] table;
Allocator allocator;
uint count; // Number of elements
uint threshold; // Resize limit
float load_factor;
}
<*
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn Map new(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
MapImpl* map = allocator::alloc(allocator, MapImpl);
_init(map, capacity, load_factor, allocator);
return (Map)map;
}
<*
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn Map temp(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
MapImpl* map = mem::temp_alloc(MapImpl);
_init(map, capacity, load_factor, allocator::temp());
return (Map)map;
}
<*
@param [&inout] allocator "The allocator to use"
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
macro Map new_init_with_key_values(..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
Map map = new(capacity, load_factor, allocator);
$for (var $i = 0; $i < $vacount; $i += 2)
map.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return map;
}
<*
@param [in] keys "Array of keys for the Map entries"
@param [in] values "Array of values for the Map entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn Map new_init_from_keys_and_values(Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
assert(keys.len == values.len);
Map map = new(capacity, load_factor, allocator);
for (usz i = 0; i < keys.len; i++)
{
map.set(keys[i], values[i]);
}
return map;
}
<*
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
macro Map temp_new_with_key_values(..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
Map map = temp(capacity, load_factor);
$for (var $i = 0; $i < $vacount; $i += 2)
map.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return map;
}
<*
@param [in] keys "The keys for the HashMap entries"
@param [in] values "The values for the HashMap entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn Map temp_init_from_keys_and_values(Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
assert(keys.len == values.len);
Map map = temp(capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
map.set(keys[i], values[i]);
}
return map;
}
<*
@param [&in] other_map "The map to copy from."
*>
fn Map new_from_map(Map other_map, Allocator allocator = null)
{
MapImpl* other_map_impl = (MapImpl*)other_map;
if (!other_map_impl)
{
if (allocator) return new(allocator: allocator);
return null;
}
MapImpl* map = (MapImpl*)new(other_map_impl.table.len, other_map_impl.load_factor, allocator ?: allocator::heap());
if (!other_map_impl.count) return (Map)map;
foreach (Entry *e : other_map_impl.table)
{
while (e)
{
map._put_for_create(e.key, e.value);
e = e.next;
}
}
return (Map)map;
}
<*
@param [&in] other_map "The map to copy from."
*>
fn Map temp_from_map(Map other_map)
{
return new_from_map(other_map, allocator::temp());
}
fn bool Map.is_empty(map) @inline
{
return !map || !((MapImpl*)map).count;
}
fn usz Map.len(map) @inline
{
return map ? ((MapImpl*)map).count : 0;
}
fn Value*! Map.get_ref(self, Key key)
{
MapImpl *map = (MapImpl*)self;
if (!map || !map.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return &e.value;
}
return SearchResult.MISSING?;
}
fn Entry*! Map.get_entry(map, Key key)
{
MapImpl *map_impl = (MapImpl*)map;
if (!map_impl || !map_impl.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map_impl.table[index_for(hash, map_impl.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e;
}
return SearchResult.MISSING?;
}
<*
Get the value or update and
@require $assignable(#expr, Value)
*>
macro Value Map.@get_or_set(&self, Key key, Value #expr)
{
MapImpl *map = (MapImpl*)*self;
if (!map || !map.count)
{
Value val = #expr;
map.set(key, val);
return val;
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e.value;
}
Value val = #expr;
map.add_entry(hash, key, val, index);
return val;
}
fn Value! Map.get(map, Key key) @operator([])
{
return *map.get_ref(key) @inline;
}
fn bool Map.has_key(map, Key key)
{
return @ok(map.get_ref(key));
}
macro Value Map.set_value_return(&map, Key key, Value value) @operator([]=)
{
map.set(key, value);
return value;
}
fn bool Map.set(&self, Key key, Value value)
{
// If the map isn't initialized, use the defaults to initialize it.
if (!*self) *self = new();
MapImpl* map = (MapImpl*)*self;
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return true;
}
}
map._add_entry(hash, key, value, index);
return false;
}
fn void! Map.remove(map, Key key) @maydiscard
{
if (!map || !((MapImpl*)map)._remove_entry_for_key(key)) return SearchResult.MISSING?;
}
fn void Map.clear(self)
{
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return;
foreach (Entry** &entry_ref : map.table)
{
Entry* entry = *entry_ref;
if (!entry) continue;
Entry *next = entry.next;
while (next)
{
Entry *to_delete = next;
next = next.next;
map._free_entry(to_delete);
}
map._free_entry(entry);
*entry_ref = null;
}
map.count = 0;
}
fn void Map.free(self)
{
if (!self) return;
MapImpl* map = (MapImpl*)self;
self.clear();
map._free_internal(map.table.ptr);
map.table = {};
allocator::free(map.allocator, map);
}
fn Key[] Map.temp_keys_list(map)
{
return map.new_keys_list(allocator::temp()) @inline;
}
fn Key[] Map.new_keys_list(self, Allocator allocator = allocator::heap())
{
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return {};
Key[] list = allocator::alloc_array(allocator, Key, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
list[index++] = entry.key;
entry = entry.next;
}
}
return list;
}
macro Map.@each(map; @body(key, value))
{
map.@each_entry(; Entry* entry) {
@body(entry.key, entry.value);
};
}
macro Map.@each_entry(self; @body(entry))
{
MapImpl *map = (MapImpl*)self;
if (!map || !map.count) return;
foreach (Entry* entry : map.table)
{
while (entry)
{
@body(entry);
entry = entry.next;
}
}
}
fn Value[] Map.temp_values_list(map)
{
return map.new_values_list(allocator::temp()) @inline;
}
fn Value[] Map.new_values_list(self, Allocator allocator = allocator::heap())
{
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
list[index++] = entry.value;
entry = entry.next;
}
}
return list;
}
fn bool Map.has_value(self, Value v) @if(VALUE_IS_EQUATABLE)
{
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return false;
foreach (Entry* entry : map.table)
{
while (entry)
{
if (equals(v, entry.value)) return true;
entry = entry.next;
}
}
return false;
}
// --- private methods
fn void MapImpl._add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
{
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
if (map.count++ >= map.threshold)
{
map._resize(map.table.len * 2);
}
}
fn void MapImpl._resize(&map, uint new_capacity) @private
{
Entry*[] old_table = map.table;
uint old_capacity = old_table.len;
if (old_capacity == MAXIMUM_CAPACITY)
{
map.threshold = uint.max;
return;
}
Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity);
map._transfer(new_table);
map.table = new_table;
map._free_internal(old_table.ptr);
map.threshold = (uint)(new_capacity * map.load_factor);
}
fn uint rehash(uint hash) @inline @private
{
hash ^= (hash >> 20) ^ (hash >> 12);
return hash ^ ((hash >> 7) ^ (hash >> 4));
}
macro uint index_for(uint hash, uint capacity) @private
{
return hash & (capacity - 1);
}
fn void MapImpl._transfer(&map, Entry*[] new_table) @private
{
Entry*[] src = map.table;
uint new_capacity = new_table.len;
foreach (uint j, Entry *e : src)
{
if (!e) continue;
do
{
Entry* next = e.next;
uint i = index_for(e.hash, new_capacity);
e.next = new_table[i];
new_table[i] = e;
e = next;
}
while (e);
}
}
fn void _init(MapImpl* impl, uint capacity, float load_factor, Allocator allocator) @private
{
capacity = math::next_power_of_2(capacity);
*impl = {
.allocator = allocator,
.load_factor = load_factor,
.threshold = (uint)(capacity * load_factor),
.table = allocator::new_array(allocator, Entry*, capacity)
};
}
fn void MapImpl._put_for_create(&map, Key key, Value value) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
for (Entry *e = map.table[i]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return;
}
}
map._create_entry(hash, key, value, i);
}
fn void MapImpl._free_internal(&map, void* ptr) @inline @private
{
allocator::free(map.allocator, ptr);
}
fn bool MapImpl._remove_entry_for_key(&map, Key key) @private
{
if (!map.count) return false;
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
Entry* prev = map.table[i];
Entry* e = prev;
while (e)
{
Entry *next = e.next;
if (e.hash == hash && equals(key, e.key))
{
map.count--;
if (prev == e)
{
map.table[i] = next;
}
else
{
prev.next = next;
}
map._free_entry(e);
return true;
}
prev = e;
e = next;
}
return false;
}
fn void MapImpl._create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
{
Entry *e = map.table[bucket_index];
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
map.count++;
}
fn void MapImpl._free_entry(&self, Entry *entry) @local
{
$if COPY_KEYS:
allocator::free(self.allocator, entry.key);
$endif
self._free_internal(entry);
}
struct Entry
{
uint hash;
Key key;
Value value;
Entry* next;
}

View File

@@ -1,4 +1,4 @@
module std::collections::maybe(<Type>);
module std::collections::maybe{Type};
import std::io;
struct Maybe (Printable)
@@ -28,16 +28,6 @@ fn Maybe value(Type val)
return { .value = val, .has_value = true };
}
fn Maybe Maybe.with_value(Type val) @deprecated("Use maybe::value instead.") @operator(construct)
{
return { .value = val, .has_value = true };
}
fn Maybe Maybe.empty() @deprecated("Use maybe::EMPTY instead.") @operator(construct)
{
return { };
}
const Maybe EMPTY = { };
macro Type! Maybe.get(self)

View File

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

View File

@@ -1,7 +1,7 @@
// priorityqueue.c3
// A priority queue using a classic binary heap for C3.
//
// Copyright (c) 2022 David Kopec
// Copyright (c) 2022-2025 David Kopec
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -20,35 +20,30 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
module std::collections::priorityqueue(<Type>);
module std::collections::priorityqueue{Type};
import std::collections::priorityqueue::private;
distinct PriorityQueue = inline PrivatePriorityQueue(<Type, false>);
distinct PriorityQueueMax = inline PrivatePriorityQueue(<Type, true>);
distinct PriorityQueue = inline PrivatePriorityQueue{Type, false};
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;
def Heap = List(<Type>);
struct PrivatePriorityQueue (Printable)
{
Heap heap;
List{Type} heap;
}
fn void PrivatePriorityQueue.init(&self, Allocator allocator, usz initial_capacity = 16, ) @inline
fn PrivatePriorityQueue* PrivatePriorityQueue.init(&self, Allocator allocator, usz initial_capacity = 16, ) @inline
{
self.heap.init(allocator, initial_capacity);
return self;
}
fn void PrivatePriorityQueue.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @inline
fn PrivatePriorityQueue* PrivatePriorityQueue.tinit(&self, usz initial_capacity = 16) @inline
{
self.heap.init(allocator, initial_capacity);
}
fn void PrivatePriorityQueue.temp_init(&self, usz initial_capacity = 16) @inline
{
self.heap.init(allocator::temp(), initial_capacity) @inline;
self.init(tmem(), initial_capacity);
return self;
}
@@ -72,6 +67,9 @@ fn void PrivatePriorityQueue.push(&self, Type element)
}
}
<*
@require index < self.len() : "Index out of range"
*>
fn void PrivatePriorityQueue.remove_at(&self, usz index)
{
if (index == 0)
@@ -124,8 +122,7 @@ fn Type! PrivatePriorityQueue.pop(&self)
fn Type! PrivatePriorityQueue.first(&self)
{
if (!self.len()) return IteratorResult.NO_MORE_ELEMENT?;
return self.heap.get(0);
return self.heap.first();
}
fn void PrivatePriorityQueue.free(&self)
@@ -135,12 +132,12 @@ fn void PrivatePriorityQueue.free(&self)
fn usz PrivatePriorityQueue.len(&self) @operator(len)
{
return self.heap.len();
return self.heap.len() @inline;
}
fn bool PrivatePriorityQueue.is_empty(&self)
{
return self.heap.is_empty();
return self.heap.is_empty() @inline;
}
<*
@@ -156,8 +153,3 @@ fn usz! PrivatePriorityQueue.to_format(&self, Formatter* formatter) @dynamic
return self.heap.to_format(formatter);
}
fn String PrivatePriorityQueue.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return self.heap.to_new_string(allocator);
}

View File

@@ -1,7 +1,7 @@
<*
@require Type.is_ordered : "The type must be ordered"
*>
module std::collections::range(<Type>);
module std::collections::range{Type};
import std::io;
struct Range (Printable)
@@ -29,21 +29,6 @@ fn Type Range.get(&self, usz index) @operator([])
return (Type)(self.start + (usz)index);
}
fn String Range.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic @deprecated
{
return string::format("[%s..%s]", self.start, self.end, allocator: allocator);
}
fn String Range.to_string(&self, Allocator allocator) @dynamic
{
return string::format("[%s..%s]", self.start, self.end, allocator: allocator);
}
fn String Range.to_tstring(&self)
{
return self.to_string(allocator::temp());
}
fn usz! Range.to_format(&self, Formatter* formatter) @dynamic
{
return formatter.printf("[%s..%s]", self.start, self.end)!;
@@ -71,21 +56,6 @@ fn usz! ExclusiveRange.to_format(&self, Formatter* formatter) @dynamic
return formatter.printf("[%s..<%s]", self.start, self.end)!;
}
fn String ExclusiveRange.to_new_string(&self, Allocator allocator = null) @dynamic
{
return self.to_string(allocator ?: allocator::heap());
}
fn String ExclusiveRange.to_string(&self, Allocator allocator) @dynamic
{
return string::format("[%s..<%s]", self.start, self.end, allocator: allocator);
}
fn String ExclusiveRange.to_tstring(&self)
{
return self.to_new_string(allocator::temp());
}
<*
@require index < self.len() : "Can't index into an empty range"
*>

View File

@@ -1,11 +1,14 @@
<*
@require values::@is_int(SIZE) &&& SIZE > 0 "The size must be positive integer"
@require Type.kindof == ARRAY : "Required an array type"
*>
module std::collections::ringbuffer(<Type, SIZE>);
module std::collections::ringbuffer{Type};
import std::io;
struct RingBuffer
def Element = $typeof((Type){}[0]);
struct RingBuffer (Printable)
{
Type[SIZE] buf;
Type buf;
usz written;
usz head;
}
@@ -15,9 +18,9 @@ fn void RingBuffer.init(&self) @inline
*self = {};
}
fn void RingBuffer.push(&self, Type c)
fn void RingBuffer.push(&self, Element c)
{
if (self.written < SIZE)
if (self.written < self.buf.len)
{
self.buf[self.written] = c;
self.written++;
@@ -25,14 +28,14 @@ fn void RingBuffer.push(&self, Type c)
else
{
self.buf[self.head] = c;
self.head = (self.head + 1) % SIZE;
self.head = (self.head + 1) % self.buf.len;
}
}
fn Type RingBuffer.get(&self, usz index) @operator([])
fn Element RingBuffer.get(&self, usz index) @operator([])
{
index %= SIZE;
usz avail = SIZE - self.head;
index %= self.buf.len;
usz avail = self.buf.len - self.head;
if (index < avail)
{
return self.buf[self.head + index];
@@ -40,25 +43,31 @@ fn Type RingBuffer.get(&self, usz index) @operator([])
return self.buf[index - avail];
}
fn Type! RingBuffer.pop(&self)
fn Element! RingBuffer.pop(&self)
{
switch
{
case self.written == 0:
return SearchResult.MISSING?;
case self.written < SIZE:
case self.written < self.buf.len:
self.written--;
return self.buf[self.written];
default:
self.head = (self.head - 1) % SIZE;
self.head = (self.head - 1) % self.buf.len;
return self.buf[self.head];
}
}
fn usz RingBuffer.read(&self, usz index, Type[] buffer)
fn usz! RingBuffer.to_format(&self, Formatter* format) @dynamic
{
index %= SIZE;
if (self.written < SIZE)
// Improve this?
return format.printf("%s", self.buf);
}
fn usz RingBuffer.read(&self, usz index, Element[] buffer)
{
index %= self.buf.len;
if (self.written < self.buf.len)
{
if (index >= self.written) return 0;
usz end = self.written - index;
@@ -66,7 +75,7 @@ fn usz RingBuffer.read(&self, usz index, Type[] buffer)
buffer[:n] = self.buf[index:n];
return n;
}
usz end = SIZE - self.head;
usz end = self.buf.len - self.head;
if (index >= end)
{
index -= end;
@@ -75,13 +84,13 @@ fn usz RingBuffer.read(&self, usz index, Type[] buffer)
buffer[:n] = self.buf[index:n];
return n;
}
if (buffer.len <= SIZE - index)
if (buffer.len <= self.buf.len - index)
{
usz n = buffer.len;
buffer[:n] = self.buf[self.head + index:n];
return n;
}
usz n1 = SIZE - index;
usz n1 = self.buf.len - index;
buffer[:n1] = self.buf[self.head + index:n1];
buffer = buffer[n1..];
index -= n1;
@@ -90,10 +99,10 @@ fn usz RingBuffer.read(&self, usz index, Type[] buffer)
return n1 + n2;
}
fn void RingBuffer.write(&self, Type[] buffer)
fn void RingBuffer.write(&self, Element[] buffer)
{
usz i;
while (self.written < SIZE && i < buffer.len)
while (self.written < self.buf.len && i < buffer.len)
{
self.buf[self.written] = buffer[i++];
self.written++;
@@ -101,6 +110,6 @@ fn void RingBuffer.write(&self, Type[] buffer)
foreach (c : buffer[i..])
{
self.buf[self.head] = c;
self.head = (self.head + 1) % SIZE;
self.head = (self.head + 1) % self.buf.len;
}
}

View File

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

View File

@@ -74,18 +74,10 @@ import std::io;
fn usz! write(String filename, char[] input, QOIDesc* desc) => @pool()
{
// encode data
char[] output = new_encode(input, desc, allocator: allocator::temp())!;
char[] output = encode(tmem(), input, desc)!;
// open file
File! f = file::open(filename, "wb");
if (catch f) return QOIError.FILE_OPEN_FAILED?;
// write data to file and close it
usz! written = f.write(output);
if (catch written) return QOIError.FILE_WRITE_FAILED?;
if (catch f.close()) return QOIError.FILE_WRITE_FAILED?;
return written;
file::save(filename, output)!;
return output.len;
}
@@ -110,29 +102,20 @@ fn usz! write(String filename, char[] input, QOIDesc* desc) => @pool()
@param [&out] desc `The descriptor to fill with the image's info`
@param channels `The channels to be used`
*>
fn char[]! new_read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) => @pool(allocator)
fn char[]! read(Allocator allocator, String filename, QOIDesc* desc, QOIChannels channels = AUTO) => @pool(allocator)
{
// read file
char[] data = file::load_temp(filename) ?? QOIError.FILE_OPEN_FAILED?!;
// pass data to decode function
return new_decode(data, desc, channels, allocator);
return decode(allocator, data, desc, channels);
}
fn char[]! read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) @deprecated("Use new_read")
{
return new_read(filename, desc, channels, allocator);
}
// Back to basic non-stdio mode
module std::compression::qoi;
import std::bits;
fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap()) @deprecated("use encode_new")
{
return new_encode(input, desc, allocator);
}
<*
Encode raw RGB or RGBA pixels into a QOI image in memory.
@@ -146,7 +129,7 @@ fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::
@param [in] input `The raw RGB or RGBA pixels to encode`
@param [&in] desc `The descriptor of the image`
*>
fn char[]! new_encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap()) @nodiscard
fn char[]! encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
{
// check info in desc
if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_PARAMETERS?;
@@ -278,10 +261,6 @@ fn char[]! new_encode(char[] input, QOIDesc* desc, Allocator allocator = allocat
}
fn char[]! decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
{
return new_decode(data, desc, channels, allocator);
}
<*
Decode a QOI image from memory.
@@ -304,7 +283,7 @@ fn char[]! decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Alloc
@param [&out] desc `The descriptor to fill with the image's info`
@param channels `The channels to be used`
*>
fn char[]! new_decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) @nodiscard
fn char[]! decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels channels = AUTO) @nodiscard
{
// check input data
if (data.len < Header.sizeof + END_OF_STREAM.len) return QOIError.INVALID_DATA?;

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-2025 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.

View File

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

View File

@@ -26,7 +26,7 @@ macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0)
if (xlen < 1) xlen = $typeof((*array_ptr)[0]).len + xlen;
if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen;
var $ElementType = $typeof((*array_ptr)[0][0]);
return Slice2d(<$ElementType>) { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
return Slice2d{$ElementType} { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
}
@@ -56,7 +56,7 @@ macro rindex_of(array, element)
@require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
@ensure result.len == arr1.len + arr2.len
*>
macro concat(arr1, arr2, Allocator allocator) @nodiscard
macro concat(Allocator allocator, arr1, arr2) @nodiscard
{
var $Type = $typeof(arr1[0]);
$Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len);
@@ -70,22 +70,6 @@ macro concat(arr1, arr2, Allocator allocator) @nodiscard
}
return result;
}
<*
Concatenate two arrays or slices, returning a slice containing the concatenation of them.
@param [in] arr1
@param [in] arr2
@param [&inout] allocator "The allocator to use, default is the heap allocator"
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
@require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
@require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
@ensure return.len == arr1.len + arr2.len
*>
macro concat_new(arr1, arr2, Allocator allocator = allocator::heap()) @nodiscard
{
return concat(arr1, arr2, allocator);
}
<*
Concatenate two arrays or slices, returning a slice containing the concatenation of them,
allocated using the temp allocator.
@@ -97,9 +81,9 @@ macro concat_new(arr1, arr2, Allocator allocator = allocator::heap()) @nodiscard
@require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
@ensure return.len == arr1.len + arr2.len
*>
macro tconcat(arr1, arr2) @nodiscard => concat(arr1, arr2, allocator::temp());
macro tconcat(arr1, arr2) @nodiscard => concat(allocator::temp(), arr1, arr2);
module std::core::array::slice(<Type>);
module std::core::array::slice{Type};
struct Slice2d
{

View File

@@ -4,6 +4,31 @@
module std::core::builtin;
import libc, std::hash, std::io, std::os::backtrace;
<*
EMPTY_MACRO_SLOT is a value used for implementing optional arguments for macros in an efficient
way. It relies on the fact that distinct types are not implicitly convertable.
You can use `@is_empty_macro_slot()` and `@is_valid_macro_slot()` to figure out whether
the argument has been used or not.
An example:
```c3
macro foo(a, #b = EMPTY_MACRO_SLOT)
{
$if @is_valid_macro_slot(#b):
return invoke_foo2(a, #b);
$else
return invoke_foo1(a);
$endif
}
*>
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
distinct EmptySlot = void*;
macro @is_empty_macro_slot(#arg) @const @builtin => @typeis(#arg, EmptySlot);
macro @is_valid_macro_slot(#arg) @const @builtin => !@typeis(#arg, EmptySlot);
/*
Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
*/
@@ -67,7 +92,7 @@ fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIV
void*[256] buffer;
void*[] backtraces = backtrace::capture_current(&buffer);
backtraces_to_ignore++;
BacktraceList! backtrace = backtrace::symbolize_backtrace(backtraces, allocator::temp());
BacktraceList! backtrace = backtrace::symbolize_backtrace(tmem(), backtraces);
if (catch backtrace) return false;
if (backtrace.len() <= backtraces_to_ignore) return false;
io::eprint("\nERROR: '");
@@ -413,10 +438,6 @@ macro uint String.hash(String c) => (uint)fnv32a::encode(c);
macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);
macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr));
distinct EmptySlot = void*;
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
macro @is_empty_macro_slot(#arg) @builtin => @typeis(#arg, EmptySlot);
macro @is_valid_macro_slot(#arg) @builtin => !@typeis(#arg, EmptySlot);
const MAX_FRAMEADDRESS = 128;
<*

View File

@@ -6,19 +6,6 @@ distinct DStringOpaque = void;
const usz MIN_CAPACITY @private = 16;
<*
@require !self.data() "String already initialized"
*>
fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
StringData* data = allocator::alloc_with_padding(allocator, StringData, capacity)!!;
data.allocator = allocator;
data.len = 0;
data.capacity = capacity;
return *self = (DString)data;
}
<*
@require !self.data() "String already initialized"
*>
@@ -32,35 +19,25 @@ fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY)
return *self = (DString)data;
}
<*
@require !self.data() "String already initialized"
*>
fn DString DString.temp_init(&self, usz capacity = MIN_CAPACITY) @deprecated("Use tinit()")
{
self.init(allocator::temp(), capacity) @inline;
return *self;
}
<*
@require !self.data() "String already initialized"
*>
fn DString DString.tinit(&self, usz capacity = MIN_CAPACITY)
{
self.init(allocator::temp(), capacity) @inline;
return *self;
return self.init(tmem(), capacity) @inline;
}
fn DString new_with_capacity(usz capacity, Allocator allocator = allocator::heap())
fn DString new_with_capacity(Allocator allocator, usz capacity)
{
return (DString){}.init(allocator, capacity);
}
fn DString temp_with_capacity(usz capacity) => new_with_capacity(capacity, allocator::temp()) @inline;
fn DString temp_with_capacity(usz capacity) => new_with_capacity(tmem(), capacity) @inline;
fn DString new(String c = "", Allocator allocator = allocator::heap())
fn DString new(Allocator allocator, String c = "")
{
usz len = c.len;
StringData* data = (StringData*)new_with_capacity(len, allocator);
StringData* data = (StringData*)new_with_capacity(allocator, len);
if (len)
{
data.len = len;
@@ -69,7 +46,7 @@ fn DString new(String c = "", Allocator allocator = allocator::heap())
return (DString)data;
}
fn DString temp_new(String s = "") => new(s, allocator::temp()) @inline;
fn DString temp(String s = "") => new(tmem(), s) @inline;
fn void DString.replace_char(self, char ch, char replacement)
@@ -121,15 +98,6 @@ fn void DString.replace(&self, String needle, String replacement)
};
}
fn DString DString.new_concat(self, DString b, Allocator allocator = allocator::heap()) @deprecated("Use concat(mem)")
{
DString string;
string.init(allocator, self.len() + b.len());
string.append(self);
string.append(b);
return string;
}
fn DString DString.concat(self, Allocator allocator, DString b)
{
DString string;
@@ -139,7 +107,7 @@ fn DString DString.concat(self, Allocator allocator, DString b)
return string;
}
fn DString DString.temp_concat(self, DString b) => self.concat(allocator::temp(), b);
fn DString DString.tconcat(self, DString b) => self.concat(tmem(), b);
fn ZString DString.zstr_view(&self)
{
@@ -249,23 +217,18 @@ fn usz DString.append_char32(&self, Char32 c)
return n;
}
fn DString DString.tcopy(&self) => self.copy(allocator::temp());
fn DString DString.tcopy(&self) => self.copy(tmem());
fn DString DString.copy(self, Allocator allocator = null)
fn DString DString.copy(self, Allocator allocator)
{
if (!self)
{
if (allocator) return new_with_capacity(0, allocator);
return (DString)null;
}
if (!self) return new(allocator);
StringData* data = self.data();
if (!allocator) allocator = allocator::heap();
DString new_string = new_with_capacity(data.capacity, allocator);
DString new_string = new_with_capacity(allocator, data.capacity);
mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len);
return new_string;
}
fn ZString DString.copy_zstr(self, Allocator allocator = allocator::heap())
fn ZString DString.copy_zstr(self, Allocator allocator)
{
usz str_len = self.len();
if (!str_len)
@@ -279,12 +242,12 @@ fn ZString DString.copy_zstr(self, Allocator allocator = allocator::heap())
return (ZString)zstr;
}
fn String DString.copy_str(self, Allocator allocator = allocator::heap())
fn String DString.copy_str(self, Allocator allocator)
{
return (String)self.copy_zstr(allocator)[:self.len()];
}
fn String DString.tcopy_str(self) => self.copy_str(allocator::temp()) @inline;
fn String DString.tcopy_str(self) => self.copy_str(tmem()) @inline;
fn bool DString.equals(self, DString other_string)
{
@@ -334,7 +297,7 @@ fn void DString.append_chars(&self, String str)
if (!other_len) return;
if (!*self)
{
*self = new(str);
*self = temp(str);
return;
}
self.reserve(other_len);
@@ -343,9 +306,9 @@ fn void DString.append_chars(&self, String str)
data.len += other_len;
}
fn Char32[] DString.copy_utf32(&self, Allocator allocator = allocator::heap())
fn Char32[] DString.copy_utf32(&self, Allocator allocator)
{
return self.str_view().to_utf32(allocator) @inline!!;
return self.str_view().to_utf32_copy(allocator) @inline!!;
}
fn void DString.append_string(&self, DString str)
@@ -376,7 +339,7 @@ fn void DString.append_char(&self, char c)
{
if (!*self)
{
*self = new_with_capacity(MIN_CAPACITY);
*self = temp_with_capacity(MIN_CAPACITY);
}
self.reserve(1);
StringData* data = self.data();
@@ -574,7 +537,7 @@ macro void DString.insert_at(&self, usz index, value)
fn usz! DString.appendf(&self, String format, args...) @maydiscard
{
if (!self.data()) self.init(mem, format.len + 20);
if (!self.data()) self.tinit(format.len + 20);
@pool(self.data().allocator)
{
Formatter formatter;
@@ -585,7 +548,7 @@ fn usz! DString.appendf(&self, String format, args...) @maydiscard
fn usz! DString.appendfn(&self, String format, args...) @maydiscard
{
if (!self.data()) self.init(mem, format.len + 20);
if (!self.data()) self.tinit(format.len + 20);
@pool(self.data().allocator)
{
Formatter formatter;
@@ -596,15 +559,15 @@ fn usz! DString.appendfn(&self, String format, args...) @maydiscard
};
}
fn DString new_join(String[] s, String joiner, Allocator allocator = allocator::heap())
fn DString join(Allocator allocator, String[] s, String joiner)
{
if (!s.len) return (DString)null;
if (!s.len) return new(allocator);
usz total_size = joiner.len * s.len;
foreach (String* &str : s)
{
total_size += str.len;
}
DString res = new_with_capacity(total_size, allocator);
DString res = new_with_capacity(allocator, total_size);
res.append(s[0]);
foreach (String* &str : s[1..])
{
@@ -644,7 +607,7 @@ fn void DString.reserve(&self, usz addition)
StringData* data = self.data();
if (!data)
{
*self = dstring::new_with_capacity(addition);
*self = dstring::temp_with_capacity(addition);
return;
}
usz len = data.len + addition;

View File

@@ -401,6 +401,15 @@ macro TempAllocator* temp()
return thread_temp_allocator;
}
macro TempAllocator* tmem() @builtin
{
if (!thread_temp_allocator)
{
init_default_temp_allocators();
}
return thread_temp_allocator;
}
fn void init_default_temp_allocators() @private
{
temp_allocator_pair[0] = create_default_sized_temp_allocator(temp_base_allocator);

View File

@@ -80,7 +80,7 @@ macro String[] wargs_strings(int argc, Char16** argv) @private
{
Char16* arg = argv[i];
Char16[] argstring = arg[:_strlen(arg)];
list[i] = string::new_from_utf16(argstring) ?? "?".copy();
list[i] = string::new_from_utf16(mem, argstring) ?? "?".copy(mem);
}
return list[:argc];
}

View File

@@ -1,8 +1,7 @@
module std::core::runtime;
import libc, std::time, std::io, std::sort;
def BenchmarkFn = fn void!() @if($$OLD_TEST);
def BenchmarkFn = fn void() @if(!$$OLD_TEST);
def BenchmarkFn = fn void();
struct BenchmarkUnit
{
@@ -10,7 +9,7 @@ struct BenchmarkUnit
BenchmarkFn func;
}
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator = allocator::heap())
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator)
{
BenchmarkFn[] fns = $$BENCHMARK_FNS;
String[] names = $$BENCHMARK_NAMES;
@@ -39,80 +38,7 @@ fn void set_benchmark_max_iterations(uint value) @builtin
benchmark_max_iterations = value;
}
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if($$OLD_TEST)
{
int benchmarks_passed = 0;
int benchmark_count = benchmarks.len;
usz max_name;
foreach (&unit : benchmarks)
{
if (max_name < unit.name.len) max_name = unit.name.len;
}
usz len = max_name + 9;
DString name = dstring::temp_with_capacity(64);
name.append_repeat('-', len / 2);
name.append(" BENCHMARKS ");
name.append_repeat('-', len - len / 2);
io::printn(name);
name.clear();
long sys_clock_started;
long sys_clock_finished;
long sys_clocks;
Clock clock;
anyfault err;
foreach(unit : benchmarks)
{
defer name.clear();
name.appendf("Benchmarking %s ", unit.name);
name.append_repeat('.', max_name - unit.name.len + 2);
io::printf("%s ", name.str_view());
for (uint i = 0; i < benchmark_warmup_iterations; i++)
{
err = @catch(unit.func()) @inline;
@volatile_load(err);
}
clock = std::time::clock::now();
sys_clock_started = $$sysclock();
for (uint i = 0; i < benchmark_max_iterations; i++)
{
err = @catch(unit.func()) @inline;
@volatile_load(err);
}
sys_clock_finished = $$sysclock();
NanoDuration nano_seconds = clock.mark();
sys_clocks = sys_clock_finished - sys_clock_started;
if (err)
{
io::printfn("[failed] Failed due to: %s", err);
continue;
}
io::printfn("[ok] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
benchmarks_passed++;
}
io::printfn("\n%d benchmark%s run.\n", benchmark_count, benchmark_count > 1 ? "s" : "");
io::printfn("Benchmarks Result: %s. %d passed, %d failed.",
benchmarks_passed < benchmark_count ? "FAILED" : "ok",
benchmarks_passed,
benchmark_count - benchmarks_passed);
return benchmark_count == benchmarks_passed;
}
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if(!$$OLD_TEST)
fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
{
usz max_name;
@@ -170,5 +96,5 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if(!$$OLD_TEST)
fn bool default_benchmark_runner(String[] args) => @pool()
{
return run_benchmarks(benchmark_collection_create(allocator::temp()));
return run_benchmarks(benchmark_collection_create(tmem()));
}

View File

@@ -7,8 +7,7 @@ import std::core::mem::allocator @public;
import libc, std::time, std::io, std::sort;
import std::os::env;
def TestFn = fn void!() @if($$OLD_TEST);
def TestFn = fn void() @if(!$$OLD_TEST);
def TestFn = fn void();
TestContext* test_context @private;
@@ -47,7 +46,7 @@ struct TestUnit
TestFn func;
}
fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap())
fn TestUnit[] test_collection_create(Allocator allocator)
{
TestFn[] fns = $$TEST_FNS;
String[] names = $$TEST_NAMES;
@@ -75,7 +74,8 @@ fn int cmp_test_unit(TestUnit a, TestUnit b)
fn bool terminal_has_ansi_codes() @local => @pool()
{
if (try v = env::get_var_temp("TERM"))
if (try v = env::tget_var("TERM"))
{
if (v.contains("xterm") || v.contains("vt100") || v.contains("screen")) return true;
}
@@ -278,15 +278,7 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
mute_output();
mem.clear();
if (check_leaks) allocator::thread_allocator = &mem;
$if(!$$OLD_TEST):
unit.func();
$else
if (catch err = unit.func())
{
io::printf("[FAIL] Failed due to: %s", err);
continue;
}
$endif
unit.func();
// track cleanup that may take place in teardown_fn
if (context.teardown_fn)
{
@@ -297,7 +289,7 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
unmute_output(false); // all good, discard output
if (mem.has_leaks())
{
if(context.is_quiet_mode) io::printf("\n%s ", context.current_test_name);
if (context.is_quiet_mode) io::printf("\n%s ", context.current_test_name);
io::print(context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]");
io::printn(" LEAKS DETECTED!");
mem.print_report();
@@ -337,6 +329,6 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
fn bool default_test_runner(String[] args) => @pool()
{
assert(test_context == null, "test suite is already running");
return run_tests(args, test_collection_create(allocator::temp()));
return run_tests(args, test_collection_create(tmem()));
}

View File

@@ -52,20 +52,13 @@ fn ZString tformat_zstr(String fmt, args...)
@param [inout] allocator `The allocator to use`
@param [in] fmt `The formatting string`
*>
fn String format(String fmt, args..., Allocator allocator) => @pool(allocator)
fn String format(Allocator allocator, String fmt, args...) => @pool(allocator)
{
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
str.appendf(fmt, ...args);
return str.copy_str(allocator);
}
<*
Return a heap allocated String created using the formatting function.
@param [in] fmt `The formatting string`
*>
fn String new_format(String fmt, args..., Allocator allocator = null) => format(fmt, ...args, allocator: allocator ?: allocator::heap());
<*
Return a temporary String created using the formatting function.
@@ -78,19 +71,6 @@ fn String tformat(String fmt, args...)
return str.str_view();
}
<*
Return a new ZString created using the formatting function.
@param [in] fmt `The formatting string`
@param [inout] allocator `The allocator to use`
*>
fn ZString new_format_zstr(String fmt, args..., Allocator allocator = allocator::heap()) => @pool(allocator)
{
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
str.appendf(fmt, ...args);
return str.copy_zstr(allocator);
}
<*
Check if a character is in a set.
@@ -105,7 +85,7 @@ macro bool char_in_set(char c, String set)
return false;
}
fn String join_new(String[] s, String joiner, Allocator allocator = allocator::heap())
fn String join(Allocator allocator, String[] s, String joiner)
{
if (!s)
{
@@ -246,7 +226,7 @@ fn String String.strip_end(string, String needle)
@require needle.len > 0 "The needle must be at least 1 character long"
@ensure return.len > 0
*>
fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = allocator::heap(), bool skip_empty = false)
fn String[] String.split(s, Allocator allocator, String needle, usz max = 0, bool skip_empty = false)
{
usz capacity = 16;
usz i = 0;
@@ -281,18 +261,6 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = al
return holder[:i];
}
<*
Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }, using the heap allocator
to store the parts.
@param [in] s
@param [in] needle
@param max "Max number of elements, 0 means no limit, defaults to 0"
@param skip_empty "True to skip empty elements"
@require needle.len > 0 "The needle must be at least 1 character long"
@ensure return.len > 0
*>
fn String[] String.new_split(s, String needle, usz max = 0, bool skip_empty) => s.split(needle, max, allocator::heap(), skip_empty) @inline;
<*
This function is identical to String.split, but implicitly uses the
@@ -303,7 +271,7 @@ fn String[] String.new_split(s, String needle, usz max = 0, bool skip_empty) =>
@param max "Max number of elements, 0 means no limit, defaults to 0"
@param skip_empty "True to skip empty elements"
*>
fn String[] String.tsplit(s, String needle, usz max = 0, bool skip_empty = false) => s.split(needle, max, allocator::temp(), skip_empty) @inline;
fn String[] String.tsplit(s, String needle, usz max = 0, bool skip_empty = false) => s.split(tmem(), needle, max, skip_empty) @inline;
fault SplitResult { BUFFER_EXCEEDED }
@@ -521,7 +489,7 @@ fn usz ZString.len(str)
}
fn ZString String.zstr_copy(s, Allocator allocator = allocator::heap())
fn ZString String.zstr_copy(s, Allocator allocator)
{
usz len = s.len;
char* str = allocator::malloc(allocator, len + 1);
@@ -530,7 +498,7 @@ fn ZString String.zstr_copy(s, Allocator allocator = allocator::heap())
return (ZString)str;
}
fn String String.concat(s1, String s2, Allocator allocator = allocator::heap())
fn String String.concat(s1, Allocator allocator, String s2)
{
usz full_len = s1.len + s2.len;
char* str = allocator::malloc(allocator, full_len + 1);
@@ -541,17 +509,17 @@ fn String String.concat(s1, String s2, Allocator allocator = allocator::heap())
return (String)str[:full_len];
}
fn String String.tconcat(s1, String s2) => s1.concat(s2, allocator::temp());
fn String String.tconcat(s1, String s2) => s1.concat(tmem(), s2);
fn ZString String.zstr_tcopy(s) => s.zstr_copy(allocator::temp()) @inline;
fn ZString String.zstr_tcopy(s) => s.zstr_copy(tmem()) @inline;
<*
Copy this string, by duplicating the string, always adding a zero byte
sentinel, so that it safely can be converted to a ZString by a
cast.
*>
fn String String.copy(s, Allocator allocator = allocator::heap())
fn String String.copy(s, Allocator allocator)
{
usz len = s.len;
char* str = allocator::malloc(allocator, len + 1);
@@ -560,23 +528,23 @@ fn String String.copy(s, Allocator allocator = allocator::heap())
return (String)str[:len];
}
fn void String.free(&s, Allocator allocator = allocator::heap())
fn void String.free(&s, Allocator allocator)
{
if (!s.ptr) return;
allocator::free(allocator, s.ptr);
*s = "";
}
fn String String.tcopy(s) => s.copy(allocator::temp()) @inline;
fn String String.tcopy(s) => s.copy(tmem()) @inline;
fn String ZString.copy(z, Allocator allocator = allocator::heap())
fn String ZString.copy(z, Allocator allocator)
{
return z.str_view().copy(allocator) @inline;
}
fn String ZString.tcopy(z)
{
return z.str_view().copy(allocator::temp()) @inline;
return z.str_view().copy(tmem()) @inline;
}
<*
@@ -585,7 +553,7 @@ fn String ZString.tcopy(z)
@return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
@return! AllocationFailure "If allocation of the string fails"
*>
fn Char16[]! String.to_new_utf16(s, Allocator allocator = allocator::heap())
fn Char16[]! String.to_utf16_copy(s, Allocator allocator)
{
usz len16 = conv::utf16len_for_utf8(s);
Char16* data = allocator::alloc_array_try(allocator, Char16, len16 + 1)!;
@@ -594,26 +562,16 @@ fn Char16[]! String.to_new_utf16(s, Allocator allocator = allocator::heap())
return data[:len16];
}
<*
Convert an UTF-8 string to UTF-16
@return "The UTF-16 string as a slice, allocated using the given allocator"
@return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
@return! AllocationFailure "If allocation of the string fails"
*>
fn Char16[]! String.to_temp_utf16(s)
fn Char16[]! String.to_utf16_tcopy(s) => s.to_utf16_copy(tmem());
fn WString! String.to_wstring_copy(s, Allocator allocator)
{
return s.to_new_utf16(allocator::temp());
return (WString)s.to_utf16_copy(allocator).ptr;
}
fn WString! String.to_wstring(s, Allocator allocator)
{
return (WString)s.to_new_utf16(allocator).ptr;
}
fn WString! String.to_wstring_tcopy(s) => s.to_wstring_copy(tmem());
fn WString! String.to_temp_wstring(s) => s.to_wstring(allocator::temp());
fn WString! String.to_new_wstring(s) => s.to_wstring(allocator::heap());
fn Char32[]! String.to_utf32(s, Allocator allocator)
fn Char32[]! String.to_utf32_copy(s, Allocator allocator)
{
usz codepoints = conv::utf8_codepoints(s);
Char32* data = allocator::alloc_array_try(allocator, Char32, codepoints + 1)!;
@@ -622,30 +580,27 @@ fn Char32[]! String.to_utf32(s, Allocator allocator)
return data[:codepoints];
}
fn Char32[]! String.to_new_utf32(s) => s.to_utf32(allocator::heap()) @inline;
fn Char32[]! String.to_temp_utf32(s) => s.to_utf32(allocator::temp()) @inline;
<*
Convert a string to ASCII lower case.
Convert a string to ASCII lower case in place.
@param [inout] s
@pure
*>
fn void String.convert_ascii_to_lower(s)
fn void String.convert_to_lower(s)
{
foreach (&c : s) if (c.is_upper() @pure) *c += 'a' - 'A';
}
fn String String.new_ascii_to_lower(s, Allocator allocator = allocator::heap())
fn String String.to_lower_copy(s, Allocator allocator)
{
String copy = s.copy(allocator);
copy.convert_ascii_to_lower();
copy.convert_to_lower();
return copy;
}
fn String String.temp_ascii_to_lower(s)
fn String String.to_lower_tcopy(s)
{
return s.new_ascii_to_lower(allocator::temp());
return s.to_lower_copy(tmem());
}
<*
@@ -654,7 +609,7 @@ fn String String.temp_ascii_to_lower(s)
@param [inout] s
@pure
*>
fn void String.convert_ascii_to_upper(s)
fn void String.convert_to_upper(s)
{
foreach (&c : s) if (c.is_lower() @pure) *c -= 'a' - 'A';
}
@@ -667,10 +622,10 @@ fn void String.convert_ascii_to_upper(s)
@return `a new String converted to ASCII upper case.`
*>
fn String String.new_ascii_to_upper(s, Allocator allocator = allocator::heap())
fn String String.to_upper_copy(s, Allocator allocator)
{
String copy = s.copy(allocator);
copy.convert_ascii_to_upper();
copy.convert_to_upper();
return copy;
}
@@ -683,12 +638,12 @@ fn StringIterator String.iterator(s)
@param [in] s
@return `a temporary String converted to ASCII upper case.`
*>
fn String String.temp_ascii_to_upper(s)
fn String String.to_upper_tcopy(s)
{
return s.new_ascii_to_upper(allocator::temp());
return s.to_upper_copy(tmem());
}
fn String! new_from_utf32(Char32[] utf32, Allocator allocator = allocator::heap())
fn String! new_from_utf32(Allocator allocator, Char32[] utf32)
{
usz len = conv::utf8len_for_utf32(utf32);
char* data = allocator::malloc_try(allocator, len + 1)!;
@@ -698,7 +653,7 @@ fn String! new_from_utf32(Char32[] utf32, Allocator allocator = allocator::heap(
return (String)data[:len];
}
fn String! new_from_utf16(Char16[] utf16, Allocator allocator = allocator::heap())
fn String! new_from_utf16(Allocator allocator, Char16[] utf16)
{
usz len = conv::utf8len_for_utf16(utf16);
char* data = allocator::malloc_try(allocator, len + 1)!;
@@ -708,16 +663,16 @@ fn String! new_from_utf16(Char16[] utf16, Allocator allocator = allocator::heap(
return (String)data[:len];
}
fn String! new_from_wstring(WString wstring, Allocator allocator = allocator::heap())
fn String! new_from_wstring(Allocator allocator, WString wstring)
{
usz utf16_len;
while (wstring[utf16_len] != 0) utf16_len++;
Char16[] utf16 = wstring[:utf16_len];
return new_from_utf16(utf16, allocator);
return new_from_utf16(allocator, utf16);
}
fn String! temp_from_wstring(WString wstring) => new_from_wstring(wstring, allocator::temp()) @inline;
fn String! temp_from_utf16(Char16[] utf16) => new_from_utf16(utf16, allocator::temp()) @inline;
fn String! temp_from_wstring(WString wstring) => new_from_wstring(tmem(), wstring) @inline;
fn String! temp_from_utf16(Char16[] utf16) => new_from_utf16(tmem(), utf16) @inline;
fn usz String.utf8_codepoints(s)
{
@@ -865,15 +820,15 @@ fn String! Splitter.next(&self)
}
}
macro String new_struct_to_str(x, Allocator allocator = allocator::heap())
macro String new_from_struct(Allocator allocator, x)
{
DString s;
@stack_mem(512; Allocator mem)
{
s.init(mem);
s.init(allocator: mem);
io::fprint(&s, x)!!;
return s.copy_str(allocator);
};
}
macro String temp_struct_to_str(x) => new_struct_to_str(x, allocator::temp());
macro String temp_from_struct(x) => new_from_struct(tmem(), x);

View File

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

View File

@@ -43,22 +43,20 @@ const Base64Alphabet URL = {
const STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
fn String encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
fn String encode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
{
char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding));
return encode_buffer(src, dst, padding, alphabet);
}
fn char[]! decode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
fn char[]! decode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
{
char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding))!;
return decode_buffer(src, dst, padding, alphabet);
}
fn String encode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::heap(), padding, alphabet);
fn String encode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet);
fn char[]! decode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::heap(), padding, alphabet);
fn char[]! decode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::temp(), padding, alphabet);
fn String tencode(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(tmem(), code, padding, alphabet);
fn char[]! tdecode(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(tmem(), code, padding, alphabet);
<*

View File

@@ -38,26 +38,20 @@ fn void CsvReader.init(&self, InStream stream, String separator = ",")
self.stream = stream;
self.separator = separator;
}
fn CsvRow! CsvReader.read_new_row(self)
{
return self.read_row(allocator::heap()) @inline;
}
<*
@param [&inout] allocator
*>
fn CsvRow! CsvReader.read_row(self, Allocator allocator)
{
String row = io::readline(self.stream, allocator: allocator)!;
String row = io::readline(allocator, self.stream)!;
defer catch allocator::free(allocator, row);
String[] list = row.split(self.separator, allocator: allocator);
String[] list = row.split(allocator, self.separator);
return { list, row, allocator };
}
fn CsvRow! CsvReader.read_temp_row(self)
fn CsvRow! CsvReader.tread_row(self)
{
return self.read_row(allocator::temp()) @inline;
return self.read_row(tmem()) @inline;
}
<*
@@ -83,13 +77,13 @@ macro void! CsvReader.@each_row(self, int rows = int.max; @body(String[] row)) @
{
@stack_mem(512; Allocator mem)
{
String! s = io::readline(stream, mem);
String! s = io::readline(mem, stream);
if (catch err = s)
{
if (err == IoError.EOF) return;
return err?;
}
@body(s.split(sep, allocator: mem));
@body(s.split(mem, sep));
};
}
}

View File

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

View File

@@ -15,21 +15,21 @@ fault JsonParsingError
INVALID_NUMBER,
}
fn Object*! parse_string(String s, Allocator allocator = allocator::heap())
fn Object*! parse_string(Allocator allocator, String s)
{
return parse((ByteReader){}.init(s), allocator);
return parse(allocator, (ByteReader){}.init(s));
}
fn Object*! temp_parse_string(String s)
fn Object*! tparse_string(String s)
{
return parse((ByteReader){}.init(s), allocator::temp());
return parse(tmem(), (ByteReader){}.init(s));
}
fn Object*! parse(InStream s, Allocator allocator = allocator::heap())
fn Object*! parse(Allocator allocator, InStream s)
{
@stack_mem(512; Allocator mem)
@stack_mem(512; Allocator smem)
{
JsonContext context = { .last_string = dstring::new_with_capacity(64, mem), .stream = s, .allocator = allocator };
JsonContext context = { .last_string = dstring::new_with_capacity(smem, 64), .stream = s, .allocator = allocator };
@pool(allocator)
{
return parse_any(&context);
@@ -37,9 +37,9 @@ fn Object*! parse(InStream s, Allocator allocator = allocator::heap())
};
}
fn Object*! temp_parse(InStream s)
fn Object*! tparse(InStream s)
{
return parse(s, allocator::temp());
return parse(tmem(), s);
}
// -- Implementation follows --
@@ -106,7 +106,7 @@ fn JsonTokenType! lex_number(JsonContext *context, char c) @local
{
@stack_mem(256; Allocator mem)
{
DString t = dstring::new_with_capacity(32, allocator: mem);
DString t = dstring::new_with_capacity(mem, 32);
bool negate = c == '-';
if (negate)
{
@@ -159,7 +159,7 @@ fn Object*! parse_map(JsonContext* context) @local
@stack_mem(256; Allocator mem)
{
DString temp_key = dstring::new_with_capacity(32, mem);
DString temp_key = dstring::new_with_capacity(mem, 32);
while (token != JsonTokenType.RBRACE)
{
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,7 +21,7 @@ fn File! open_path(Path path, String mode)
fn bool exists(String file) => @pool()
{
return path::exists(path::temp_new(file)) ?? false;
return os::native_file_or_dir_exists(file);
}
fn File from_handle(CFile file)
@@ -34,12 +34,20 @@ fn bool is_file(String path)
return os::native_is_file(path);
}
fn bool is_dir(String path)
{
return os::native_is_dir(path);
}
fn usz! get_size(String path)
{
return os::native_file_size(path);
}
fn void! delete(String filename) => os::native_remove(filename) @inline;
fn void! delete(String filename)
{
return os::native_remove(filename) @inline;
}
<*

View File

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

View File

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

View File

@@ -21,7 +21,7 @@ macro void! native_chdir(Path path)
@pool()
{
// TODO improve with better error handling.
if (win32::setCurrentDirectoryW(path.str_view().to_temp_utf16()!!)) return;
if (win32::setCurrentDirectoryW(path.str_view().to_utf16_tcopy()!!)) return;
};
return IoError.GENERAL_ERROR?;
$default:

View File

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

View File

@@ -40,7 +40,7 @@ fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || e
fn usz! native_file_size(String path) @if(env::WIN32) => @pool()
{
Win32_FILE_ATTRIBUTE_DATA data;
win32::getFileAttributesExW(path.to_temp_wstring()!, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
win32::getFileAttributesExW(path.to_wstring_tcopy()!, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
Win32_LARGE_INTEGER size;
size.lowPart = data.nFileSizeLow;
size.highPart = data.nFileSizeHigh;
@@ -74,7 +74,7 @@ fn bool native_file_or_dir_exists(String path)
$case env::WIN32:
@pool()
{
return (bool)win32::pathFileExistsW(path.to_temp_utf16()) ?? false;
return (bool)win32::pathFileExistsW(path.to_utf16_tcopy()) ?? false;
};
$case env::POSIX:
@pool()

View File

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

View File

@@ -15,7 +15,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
if (!name || name == "." || name == "..") continue;
if (entry.d_type == posix::DT_LNK && no_symlinks) continue;
if (entry.d_type == posix::DT_DIR && no_dirs) continue;
Path path = path::new(name, allocator)!!;
Path path = path::new(allocator, name)!!;
list.push(path);
}
return list;
@@ -31,7 +31,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
@pool(allocator)
{
WString result = dir.str_view().tconcat(`\*`).to_temp_wstring()!!;
WString result = dir.str_view().tconcat(`\*`).to_wstring_tcopy()!!;
Win32_WIN32_FIND_DATAW find_data;
Win32_HANDLE find = win32::findFirstFileW(result, &find_data);
if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?;
@@ -43,7 +43,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
{
String filename = string::temp_from_wstring((WString)&find_data.cFileName)!;
if (filename == ".." || filename == ".") continue;
list.push(path::new(filename, allocator)!);
list.push(path::new(allocator, filename)!);
};
} while (win32::findNextFileW(find, &find_data));
return list;

View File

@@ -29,7 +29,7 @@ macro bool! native_mkdir(Path path, MkdirPermissions permissions)
@pool()
{
// TODO security attributes
if (win32::createDirectoryW(path.str_view().to_temp_utf16()!!, null)) return true;
if (win32::createDirectoryW(path.str_view().to_utf16_tcopy()!!, null)) return true;
switch (win32::getLastError())
{
case win32::ERROR_ACCESS_DENIED:

View File

@@ -26,7 +26,7 @@ macro bool! native_rmdir(Path path)
$case env::WIN32:
@pool()
{
if (win32::removeDirectoryW(path.str_view().to_temp_utf16()!!)) return true;
if (win32::removeDirectoryW(path.str_view().to_utf16_tcopy()!!)) return true;
switch (win32::getLastError())
{
case win32::ERROR_ACCESS_DENIED:

View File

@@ -16,7 +16,7 @@ fn void! native_rmtree(Path dir)
{
String name = ((ZString)&entry.name).str_view();
if (!name || name == "." || name == "..") continue;
Path new_path = dir.temp_append(name)!;
Path new_path = dir.tappend(name)!;
if (entry.d_type == posix::DT_DIR)
{
native_rmtree(new_path)!;
@@ -39,7 +39,7 @@ fn void! native_rmtree(Path path)
{
Win32_WIN32_FIND_DATAW find_data;
String s = path.str_view().tconcat("\\*");
Win32_HANDLE find = win32::findFirstFileW(s.to_temp_utf16(), &find_data)!;
Win32_HANDLE find = win32::findFirstFileW(s.to_utf16_tcopy(), &find_data)!;
if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?;
defer win32::findClose(find);
@@ -47,16 +47,16 @@ fn void! native_rmtree(Path path)
{
@pool()
{
String filename = string::new_from_wstring((WString)&find_data.cFileName, allocator::temp())!;
String filename = string::new_from_wstring(tmem(), (WString)&find_data.cFileName)!;
if (filename == "." || filename == "..") continue;
Path file_path = path.temp_append(filename)!;
Path file_path = path.tappend(filename)!;
if (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY)
{
native_rmtree(file_path)!;
}
else
{
win32::deleteFileW(file_path.str_view().to_temp_wstring()!!);
win32::deleteFileW(file_path.str_view().to_wstring_tcopy()!!);
}
};
} while (win32::findNextFileW(find, &find_data) != 0);

View File

@@ -1,23 +1,23 @@
module std::io::os @if(env::LIBC);
import std::io::path, std::os;
fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(!env::WIN32)
fn Path! native_temp_directory(Allocator allocator) @if(!env::WIN32)
{
foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
{
String tmpdir = env::get_var(env) ?? "";
if (tmpdir) return path::new(tmpdir, allocator);
String tmpdir = env::tget_var(env) ?? "";
if (tmpdir) return path::new(allocator, tmpdir);
}
return path::new("/tmp", allocator);
return path::new(allocator, "/tmp");
}
fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(env::WIN32) => @pool(allocator)
fn Path! native_temp_directory(Allocator allocator) @if(env::WIN32) => @pool(allocator)
{
Win32_DWORD len = win32::getTempPathW(0, null);
if (!len) return IoError.GENERAL_ERROR?;
Char16[] buff = mem::temp_alloc_array(Char16, len + (usz)1);
if (!win32::getTempPathW(len, buff)) return IoError.GENERAL_ERROR?;
return path::new(string::temp_from_utf16(buff[:len]), allocator);
return path::new(allocator, string::temp_from_utf16(buff[:len]));
}
module std::io::os @if(env::NO_LIBC);

View File

@@ -2,12 +2,12 @@ module std::io::path;
import std::collections::list, std::io::os;
import std::os::win32;
const PathEnv DEFAULT_PATH_ENV = env::WIN32 ? PathEnv.WIN32 : PathEnv.POSIX;
const PathEnv DEFAULT_ENV = env::WIN32 ? PathEnv.WIN32 : PathEnv.POSIX;
const char PREFERRED_SEPARATOR_WIN32 = '\\';
const char PREFERRED_SEPARATOR_POSIX = '/';
const char PREFERRED_SEPARATOR = env::WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
def PathList = List(<Path>);
def PathList = List { Path };
fault PathResult
{
@@ -21,6 +21,7 @@ struct PathImp (Printable)
{
String path_string;
PathEnv env;
Allocator allocator;
}
enum PathEnv
@@ -29,16 +30,11 @@ enum PathEnv
POSIX
}
fn Path! new_cwd(Allocator allocator = allocator::heap()) => @pool(allocator)
{
return new(os::getcwd(allocator::temp()), allocator);
}
fn Path! getcwd(Allocator allocator = allocator::heap()) @deprecated("Use new_cwd()")
fn Path! cwd(Allocator allocator)
{
@pool(allocator)
{
return new(os::getcwd(allocator::temp()), allocator);
return new(allocator, os::getcwd(tmem()));
};
}
@@ -46,38 +42,38 @@ fn bool is_dir(Path path) => os::native_is_dir(path.str_view());
fn bool is_file(Path path) => os::native_is_file(path.str_view());
fn usz! file_size(Path path) => os::native_file_size(path.str_view());
fn bool exists(Path path) => os::native_file_or_dir_exists(path.str_view());
fn Path! temp_cwd() => new_cwd(allocator::temp()) @inline;
fn Path! tgetcwd() @deprecated("Use temp_cwd()") => new_cwd(allocator::temp()) @inline;
fn void! chdir(Path path) => os::native_chdir(path) @inline;
fn Path! temp_directory(Allocator allocator = allocator::heap()) => os::native_temp_directory(allocator);
fn Path! tcwd() => cwd(tmem()) @inline;
<*
@require @is_pathlike(path) : "Expected a Path or String to chdir"
*>
macro void! chdir(path)
{
$if @typeis(path, String):
@pool()
{
return os::native_chdir(temp(path));
};
$else
return os::native_chdir(path) @inline;
$endif
}
fn Path! temp_directory(Allocator allocator) => os::native_temp_directory(allocator);
fn void! delete(Path path) => os::native_remove(path.str_view()) @inline;
macro bool is_separator(char c, PathEnv path_env = DEFAULT_PATH_ENV)
macro bool @is_pathlike(#path) => @typeis(#path, String) || @typeis(#path, Path);
macro bool is_separator(char c, PathEnv path_env = DEFAULT_ENV)
{
return c == '/' || (c == '\\' && path_env == PathEnv.WIN32);
}
macro bool is_posix_separator(char c)
{
return c == '/' || c == '\\';
}
macro bool is_posix_separator(char c) => c == '/';
macro bool is_win32_separator(char c) => c == '/' || c == '\\';
macro bool is_win32_separator(char c)
{
return c == '/' || c == '\\';
}
fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap()) @deprecated("use new_ls")
{
return new_ls(dir, no_dirs, no_symlinks, mask, allocator);
}
fn PathList! temp_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "")
{
return new_ls(dir, no_dirs, no_symlinks, mask, allocator::temp()) @inline;
}
fn PathList! new_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap())
fn PathList! ls(Allocator allocator, Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "")
{
$if $defined(os::native_ls):
return os::native_ls(dir, no_dirs, no_symlinks, mask, allocator);
@@ -97,35 +93,35 @@ enum MkdirPermissions
Create a directory on a given path, optionally recursive.
@param path `The path to create`
@require @is_pathlike(path) : "Expected a Path or String to chdir"
@param recursive `If directories in between should be created if they're missing, defaults to false`
@param permissions `The permissions to set on the directory`
*>
fn bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
macro bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
if (is_dir(path)) return false;
if (exists(path)) return IoError.FILE_NOT_DIR?;
if (recursive)
{
if (try parent = path.parent()) mkdir(parent, true, permissions)!;
}
if (!is_dir(path.parent()) ?? false) return IoError.CANNOT_READ_DIR?;
return os::native_mkdir(path, permissions);
$if @typeis(path, String):
@pool() { return _mkdir(temp(path), recursive, permissions); };
$else
return _mkdir(path, recursive, permissions);
$endif
}
<*
Tries to delete directory, which must be empty.
@param path `The path to delete`
@require @is_pathlike(path) : "Expected a Path or String to chdir"
@return `true if there was a directory to delete, false otherwise`
@return! PathResult.INVALID_PATH `if the path was invalid`
*>
fn bool! rmdir(Path path)
macro bool! rmdir(path)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
return os::native_rmdir(path);
$if @typeis(path, String):
@pool() { return _rmdir(temp(path)); };
$else
return _mkdir(path);
$endif
}
<*
@@ -146,9 +142,9 @@ fn void! rmtree(Path path)
@return! PathResult.INVALID_PATH `if the path was invalid`
*>
fn Path! new(String path, Allocator allocator = allocator::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
fn Path! new(Allocator allocator, String path, PathEnv path_env = DEFAULT_ENV)
{
return { normalize(path.copy(allocator), path_env), path_env };
return { normalize(path.copy(allocator), path_env), path_env, allocator };
}
<*
@@ -156,24 +152,24 @@ fn Path! new(String path, Allocator allocator = allocator::heap(), PathEnv path_
@return! PathResult.INVALID_PATH `if the path was invalid`
*>
fn Path! temp_new(String path, PathEnv path_env = DEFAULT_PATH_ENV)
fn Path! temp(String path, PathEnv path_env = DEFAULT_ENV)
{
return new(path, allocator::temp(), path_env);
return new(tmem(), path, path_env);
}
fn Path! new_win32_wstring(WString path, Allocator allocator = allocator::heap()) => @pool(allocator)
fn Path! from_win32_wstring(Allocator allocator, WString path) => @pool(allocator)
{
return path::new(string::temp_from_wstring(path)!, allocator: allocator);
return path::new(allocator, string::temp_from_wstring(path)!);
}
fn Path! new_windows(String path, Allocator allocator = allocator::heap())
fn Path! for_windows(Allocator allocator, String path)
{
return new(path, allocator, WIN32);
return new(allocator, path, WIN32);
}
fn Path! new_posix(String path, Allocator allocator = allocator::heap())
fn Path! for_posix(Allocator allocator, String path)
{
return new(path, allocator, POSIX);
return new(allocator, path, POSIX);
}
fn bool Path.equals(self, Path p2)
@@ -181,19 +177,14 @@ fn bool Path.equals(self, Path p2)
return self.env == p2.env && self.path_string == p2.path_string;
}
fn Path! Path.append(self, String filename, Allocator allocator = allocator::heap()) @deprecated("Use path.new_append(...)")
{
return self.new_append(filename, allocator) @inline;
}
<*
Append the string to the current path.
@param [in] filename
*>
fn Path! Path.new_append(self, String filename, Allocator allocator = allocator::heap())
fn Path! Path.append(self, Allocator allocator, String filename)
{
if (!self.path_string.len) return new(filename, allocator, self.env)!;
if (!self.path_string.len) return new(allocator, filename, self.env)!;
assert(!is_separator(self.path_string[^1], self.env));
@pool(allocator)
@@ -202,35 +193,37 @@ fn Path! Path.new_append(self, String filename, Allocator allocator = allocator:
dstr.append(self.path_string);
dstr.append(PREFERRED_SEPARATOR);
dstr.append(filename);
return { normalize(dstr.copy_str(allocator), self.env), self.env };
return new(allocator, dstr.str_view(), self.env);
};
}
fn Path! Path.temp_append(self, String filename) => self.new_append(filename, allocator::temp());
fn Path! Path.tappend(self, String filename) => self.append(tmem(), filename);
fn Path! Path.tappend(self, String filename) @deprecated("Use path.temp_append(...)") => self.new_append(filename, allocator::temp());
fn usz Path.start_of_base_name(self) @local
fn usz! start_of_base_name(String str, PathEnv path_env) @local
{
String path_str = self.path_string;
if (!path_str.len) return 0;
if (self.env == PathEnv.WIN32)
if (!str.len) return 0;
usz! start_slash = str.rindex_of_char('/');
if (path_env != PathEnv.WIN32) return start_slash + 1 ?? 0;
if (try index = str.rindex_of_char('\\'))
{
if (try index = path_str.rindex_of_char('\\'))
{
// c:\ style path, we're done!
if (path_str[0] != '\\') return index + 1;
// Handle \\server\foo
// Find the \ before "foo"
usz last_index = 2 + path_str[2..].index_of_char('\\')!!;
// If they don't match, we're done
assert(last_index <= index, "Invalid normalized, path %d vs %s in %s", last_index, index, path_str);
if (last_index != index) return index + 1;
// Otherwise just default to the volume length.
}
return volume_name_len(path_str, self.env)!!;
if (try start_slash && start_slash > index) return start_slash + 1;
// c:\ style path, we're done!
if (str[0] != '\\') return index + 1;
// Handle \\server\foo
// Find the \ before "foo"
usz last_index = 2 + str[2..].index_of_char('\\')!;
// If they don't match, we're done
if (last_index > index) return PathResult.INVALID_PATH?;
if (last_index != index) return index + 1;
// Otherwise just default to the volume length.
}
return path_str.rindex_of_char('/') + 1 ?? 0;
return volume_name_len(str, path_env)!!;
}
fn bool! String.is_absolute_path(self) => @pool()
{
return temp(self).is_absolute();
}
fn bool! Path.is_absolute(self)
@@ -242,55 +235,69 @@ fn bool! Path.is_absolute(self)
return path_start < path_str.len && is_separator(path_str[path_start], self.env);
}
fn Path! Path.absolute(self, Allocator allocator = allocator::heap()) @deprecated("Use path.new_absolute()")
fn Path! String.to_absolute_path(self, Allocator allocator) => @pool(allocator)
{
return self.new_absolute(allocator) @inline;
return temp(self).absolute(allocator);
}
<*
@require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths"
@require self.env == DEFAULT_ENV : "This method is only available on native paths"
*>
fn Path! Path.new_absolute(self, Allocator allocator = allocator::heap())
fn Path! Path.absolute(self, Allocator allocator)
{
String path_str = self.str_view();
if (!path_str.len) return PathResult.INVALID_PATH?;
if (self.is_absolute()!) return new(path_str, allocator, self.env);
if (self.is_absolute()!) return new(allocator, path_str, self.env);
if (path_str == ".")
{
@pool(allocator)
{
String cwd = os::getcwd(allocator::temp())!;
return new(cwd, allocator, self.env);
String cwd = os::getcwd(tmem())!;
return new(allocator, cwd, self.env);
};
}
$if DEFAULT_PATH_ENV == WIN32:
$if DEFAULT_ENV == WIN32:
@pool(allocator)
{
const usz BUFFER_LEN = 4096;
WString buffer = (WString)mem::temp_alloc_array(Char16, BUFFER_LEN);
buffer = win32::_wfullpath(buffer, path_str.to_temp_wstring()!, BUFFER_LEN);
buffer = win32::_wfullpath(buffer, path_str.to_wstring_tcopy()!, BUFFER_LEN);
if (!buffer) return PathResult.INVALID_PATH?;
return { string::new_from_wstring(buffer, allocator), WIN32 };
return { string::new_from_wstring(allocator, buffer), WIN32, allocator };
};
$else
String cwd = os::getcwd(allocator::temp())!;
return (Path){ cwd, self.env }.new_append(path_str, allocator)!;
String cwd = os::getcwd(tmem())!;
return (Path){ cwd, self.env, tmem() }.append(allocator, path_str)!;
$endif
}
fn String! String.file_basename(self, Allocator allocator) => @pool(allocator)
{
return temp(self).basename().copy(allocator);
}
fn String! String.file_tbasename(self) => self.file_basename(tmem());
fn String Path.basename(self)
{
usz basename_start = self.start_of_base_name();
usz basename_start = start_of_base_name(self.path_string, self.env)!!;
String path_str = self.path_string;
if (basename_start == path_str.len) return "";
return path_str[basename_start..];
}
fn String! String.path_tdirname(self) => self.path_dirname(tmem());
fn String! String.path_dirname(self, Allocator allocator) => @pool(allocator)
{
return temp(self).dirname().copy(allocator);
}
fn String Path.dirname(self)
{
usz basename_start = self.start_of_base_name();
String path_str = self.path_string;
usz basename_start = start_of_base_name(path_str, self.env)!!;
if (basename_start == 0) return ".";
usz start = volume_name_len(path_str, self.env)!!;
if (basename_start <= start + 1)
@@ -304,6 +311,7 @@ fn String Path.dirname(self)
return path_str[:basename_start - 1];
}
<*
Test if the path has the given extension, so given the path /foo/bar.c3
this would be true matching the extension "c3"
@@ -337,6 +345,16 @@ fn String Path.volume_name(self)
return self.path_string[:len];
}
fn Path! String.to_path(self, Allocator allocator)
{
return new(allocator, self);
}
fn Path! String.to_tpath(self)
{
return new(tmem(), self);
}
fn usz! volume_name_len(String path, PathEnv path_env) @local
{
usz len = path.len;
@@ -387,13 +405,13 @@ fn Path! Path.parent(self)
{
if (is_separator(c, self.env))
{
return { self.path_string[:i], self.env };
return { self.path_string[:i], self.env, null };
}
}
return PathResult.NO_PARENT?;
}
fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
fn String! normalize(String path_str, PathEnv path_env = DEFAULT_ENV)
{
if (!path_str.len) return path_str;
usz path_start = volume_name_len(path_str, path_env)!;
@@ -544,19 +562,19 @@ def PathWalker = fn bool! (Path, bool is_dir, void*);
<*
Walk the path recursively. PathWalker is run on every file and
directory found. Return true to abort the walk.
@require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths"
@require self.env == DEFAULT_ENV : "This method is only available on native paths"
*>
fn bool! Path.walk(self, PathWalker w, void* data)
{
const PATH_MAX = 512;
@stack_mem(PATH_MAX; Allocator allocator)
{
Path abs = self.new_absolute(allocator)!;
PathList files = new_ls(abs, allocator: allocator)!;
Path abs = self.absolute(allocator)!;
PathList files = ls(allocator, abs)!;
foreach (f : files)
{
if (f.str_view() == "." || f.str_view() == "..") continue;
f = abs.new_append(f.str_view(), allocator)!;
f = abs.append(allocator, f.str_view())!;
bool is_directory = is_dir(f);
if (w(f, is_directory, data)!) return true;
if (is_directory && f.walk(w, data)!) return true;
@@ -565,6 +583,35 @@ fn bool! Path.walk(self, PathWalker w, void* data)
return false;
}
def TraverseCallback = fn bool! (Path, bool is_dir, any data);
<*
Walk the path recursively. TraverseCallback is run for every file and
directory found. Return true to abort the walk.
@require path.env == DEFAULT_ENV : "This method is only available on native paths"
*>
fn bool! traverse(Path path, TraverseCallback callback, any data)
{
const PATH_MAX = 512;
@stack_mem(PATH_MAX; Allocator allocator)
{
Path abs = path.absolute(allocator)!;
PathList files = ls(allocator, abs)!;
foreach (f : files)
{
if (f.str_view() == "." || f.str_view() == "..") continue;
@stack_mem(128; Allocator smem)
{
f = abs.append(smem, f.str_view())!;
bool is_directory = is_dir(f);
if (callback(f, is_directory, data)!) return true;
if (is_directory && traverse(f, callback, data)!) return true;
};
}
};
return false;
}
fn String Path.str_view(self) @inline
{
return self.path_string;
@@ -576,26 +623,19 @@ fn bool Path.has_suffix(self, String str)
return self.str_view().ends_with(str);
}
fn void Path.free_with_allocator(self, Allocator allocator)
{
allocator::free(allocator, self.path_string.ptr);
}
<*
@require self.allocator != null : "This Path should never be freed"
*>
fn void Path.free(self)
{
free(self.path_string.ptr);
allocator::free(self.allocator, self.path_string.ptr);
}
fn usz! Path.to_format(&self, Formatter* formatter) @dynamic
{
return formatter.print(self.str_view());
}
fn String Path.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return self.str_view().copy(allocator);
}
const bool[256] RESERVED_PATH_CHAR_POSIX = {
[0] = true,
@@ -619,9 +659,29 @@ macro bool is_reserved_win32_path_char(char c)
return RESERVED_PATH_CHAR_WIN32[c];
}
macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_PATH_ENV)
macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_ENV)
{
return path_env == PathEnv.WIN32
? RESERVED_PATH_CHAR_WIN32[c]
: RESERVED_PATH_CHAR_POSIX[c];
}
}
fn bool! _mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL) @private
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
if (is_dir(path)) return false;
if (exists(path)) return IoError.FILE_NOT_DIR?;
if (recursive)
{
if (try parent = path.parent()) mkdir(parent, true, permissions)!;
}
if (!is_dir(path.parent()) ?? false) return IoError.CANNOT_READ_DIR?;
return os::native_mkdir(path, permissions);
}
fn bool! _rmdir(Path path) @private
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
return os::native_rmdir(path);
}

View File

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

View File

@@ -24,27 +24,9 @@ fn ByteBuffer* ByteBuffer.init(&self, Allocator allocator, usz max_read, usz ini
return self;
}
<*
ByteBuffer provides a streamable read/write buffer.
max_read defines how many bytes might be kept before its internal buffer is shrinked.
@require self.bytes.len == 0 "Buffer already initialized."
*>
fn ByteBuffer* ByteBuffer.new_init(&self, usz max_read, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
*self = { .allocator = allocator, .max_read = max_read };
initial_capacity = max(initial_capacity, 16);
self.grow(initial_capacity);
return self;
}
fn ByteBuffer* ByteBuffer.tinit(&self, usz max_read, usz initial_capacity = 16)
{
return self.init(allocator::temp(), max_read, initial_capacity);
}
fn ByteBuffer* ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = 16) @deprecated("Use tinit()")
{
return self.init(allocator::temp(), max_read, initial_capacity);
return self.init(tmem(), max_read, initial_capacity);
}
<*

View File

@@ -8,18 +8,6 @@ struct ByteWriter (OutStream)
Allocator allocator;
}
<*
@param [&inout] self
@param [&inout] allocator
@require self.bytes.len == 0 "Init may not run on already initialized data"
@ensure (bool)allocator, self.index == 0
*>
fn ByteWriter* ByteWriter.new_init(&self, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
*self = { .bytes = {}, .allocator = allocator };
return self;
}
<*
@param [&inout] self
@param [&inout] allocator
@@ -39,17 +27,7 @@ fn ByteWriter* ByteWriter.init(&self, Allocator allocator)
*>
fn ByteWriter* ByteWriter.tinit(&self)
{
return self.init(allocator::temp()) @inline;
}
<*
@param [&inout] self
@require self.bytes.len == 0 "Init may not run on already initialized data"
@ensure self.index == 0
*>
fn ByteWriter* ByteWriter.temp_init(&self) @deprecated("Use tinit")
{
return self.init(allocator::temp()) @inline;
return self.init(tmem()) @inline;
}
fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data)

View File

@@ -12,20 +12,6 @@ struct MultiReader (InStream)
}
<*
@param [&inout] self
@param [&inout] allocator
@require self.readers.len == 0 "Init may not run on already initialized data"
@ensure self.index == 0
*>
fn MultiReader* MultiReader.new_init(&self, InStream... readers, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
InStream []copy = allocator::new_array(allocator, InStream, readers.len);
copy[..] = readers[..];
*self = { .readers = copy, .allocator = allocator };
return self;
}
<*
@param [&inout] self
@param [&inout] allocator
@@ -40,16 +26,6 @@ fn MultiReader* MultiReader.init(&self, Allocator allocator, InStream... readers
return self;
}
<*
@param [&inout] self
@require self.readers.len == 0 "Init may not run on already initialized data"
@ensure self.index == 0
*>
fn MultiReader* MultiReader.temp_init(&self, InStream... readers) @deprecated("Use tinit()")
{
return self.init(allocator::temp(), ...readers);
}
<*
@param [&inout] self
@require self.readers.len == 0 "Init may not run on already initialized data"
@@ -57,7 +33,7 @@ fn MultiReader* MultiReader.temp_init(&self, InStream... readers) @deprecated("U
*>
fn MultiReader* MultiReader.tinit(&self, InStream... readers)
{
return self.init(allocator::temp(), ...readers);
return self.init(tmem(), ...readers);
}
fn void MultiReader.free(&self)

View File

@@ -23,30 +23,6 @@ fn MultiWriter* MultiWriter.init(&self, Allocator allocator, OutStream... writer
return self;
}
<*
@param [&inout] self
@param [&inout] allocator
@require writers.len > 0
@require self.writers.len == 0 "Init may not run on already initialized data"
*>
fn MultiWriter* MultiWriter.new_init(&self, OutStream... writers, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
OutStream[] copy = allocator::new_array(allocator, OutStream, writers.len);
copy[..] = writers[..];
*self = { .writers = copy, .allocator = allocator };
return self;
}
<*
@param [&inout] self
@require writers.len > 0
@require self.writers.len == 0 "Init may not run on already initialized data"
*>
fn MultiWriter* MultiWriter.temp_init(&self, OutStream... writers) @deprecated("Use tinit")
{
return self.init(allocator::temp(), ...writers);
}
<*
@param [&inout] self
@require writers.len > 0
@@ -54,7 +30,7 @@ fn MultiWriter* MultiWriter.temp_init(&self, OutStream... writers) @deprecated("
*>
fn MultiWriter* MultiWriter.tinit(&self, OutStream... writers)
{
return self.init(allocator::temp(), ...writers);
return self.init(tmem(), ...writers);
}
fn void MultiWriter.free(&self)

View File

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

View File

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

View File

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

View File

@@ -1,15 +1,15 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double _fabs(double x) @weak @extern("fabs") @nostrip
{
ulong ix = bitcast(x, ulong);
ix &= ~(1ul << 63);
return bitcast(ix, double);
}
fn float _fabsf(float x) @weak @extern("fabsf") @nostrip
{
uint ix = bitcast(x, uint);
ix &= 0x7fffffff;
return bitcast(ix, float);
}
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double _fabs(double x) @weak @extern("fabs") @nostrip
{
ulong ix = bitcast(x, ulong);
ix &= ~(1ul << 63);
return bitcast(ix, double);
}
fn float _fabsf(float x) @weak @extern("fabsf") @nostrip
{
uint ix = bitcast(x, uint);
ix &= 0x7fffffff;
return bitcast(ix, float);
}

View File

@@ -1,59 +1,59 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double frexp(double x, int* exp) @extern("frexp")
{
uint hx = x.high_word();
uint ix = hx & 0x7fffffff;
uint lx = x.low_word();
if (ix >= 0x7ff00000 || (ix | lx) == 0)
{
*exp = 0;
return x;
}
// exponent extraction and normalization
int e = (int)((ix >> 20) & 0x7ff);
if (e == 0)
{
// subnormal number
x *= 0x1p64;
hx = x.high_word();
e = (int)((hx >> 20) & 0x7ff) - 64;
}
*exp = e - 1022;
// set exponent to -1 (fraction in [0.5, 1))
hx = (hx & 0x800fffff) | 0x3fe00000;
{
ulong rep = ((ulong)hx << 32) | lx;
return bitcast(rep, double);
}
}
fn float frexpf(float x, int* exp) @extern("frexpf")
{
uint ix = x.word();
uint hx = ix & 0x7fffffff;
if (hx >= 0x7f800000 || hx == 0)
{
*exp = 0;
return x;
}
// exponent extraction and normalization
int e = (int)((hx >> 23) & 0xff);
if (e == 0)
{
// subnormal number
x *= 0x1p64f;
ix = x.word();
e = (int)((ix >> 23) & 0xff) - 64;
}
*exp = e - 126;
// set exponent to -1 (fraction in [0.5, 1))
ix = (ix & 0x807fffff) | 0x3f000000;
return bitcast(ix, float);
}
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double frexp(double x, int* exp) @extern("frexp")
{
uint hx = x.high_word();
uint ix = hx & 0x7fffffff;
uint lx = x.low_word();
if (ix >= 0x7ff00000 || (ix | lx) == 0)
{
*exp = 0;
return x;
}
// exponent extraction and normalization
int e = (int)((ix >> 20) & 0x7ff);
if (e == 0)
{
// subnormal number
x *= 0x1p64;
hx = x.high_word();
e = (int)((hx >> 20) & 0x7ff) - 64;
}
*exp = e - 1022;
// set exponent to -1 (fraction in [0.5, 1))
hx = (hx & 0x800fffff) | 0x3fe00000;
{
ulong rep = ((ulong)hx << 32) | lx;
return bitcast(rep, double);
}
}
fn float frexpf(float x, int* exp) @extern("frexpf")
{
uint ix = x.word();
uint hx = ix & 0x7fffffff;
if (hx >= 0x7f800000 || hx == 0)
{
*exp = 0;
return x;
}
// exponent extraction and normalization
int e = (int)((hx >> 23) & 0xff);
if (e == 0)
{
// subnormal number
x *= 0x1p64f;
ix = x.word();
e = (int)((ix >> 23) & 0xff) - 64;
}
*exp = e - 126;
// set exponent to -1 (fraction in [0.5, 1))
ix = (ix & 0x807fffff) | 0x3f000000;
return bitcast(ix, float);
}

View File

@@ -1,67 +1,67 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double ldexp(double x, int exp) @extern("ldexp")
{
uint hx = x.high_word();
int hexp = (int)((hx & 0x7ff00000) >> 20);
if (hexp == 0x7ff) return x;
if (hexp == 0)
{
// subnormal number handling
x *= 0x1p64;
hx = x.high_word();
hexp = (int)((hx & 0x7ff00000) >> 20) - 64;
}
// new exponent calculation
hexp += exp;
if (hexp > 0x7fe) return x * double.inf;
if (hexp < 1)
{
x *= 0x1p-1022;
hexp += 1022;
if (hexp < 1) x *= 0x1p-1022;
return x;
}
// set new exponent
hx = ((ulong)hx & 0x800fffff) | ((ulong)hexp << 20);
{
ulong rep = ((ulong)hx << 32) | x.low_word();
return bitcast(rep, double);
}
}
fn float ldexpf(float x, int exp) @extern("ldexpf")
{
uint ix = x.word();
int hexp = (int)((ix & 0x7f800000) >> 23);
if (hexp == 0xff) return x;
if (hexp == 0)
{
// subnormal number handling
x *= 0x1p64f;
ix = x.word();
hexp = (int)((ix & 0x7f800000) >> 23) - 64;
}
// new exponent calculation
hexp += exp;
if (hexp > 0xfe) return x * float.inf;
if (hexp < 1)
{
x *= 0x1p-126f;
hexp += 126;
if (hexp < 1) x *= 0x1p-126f;
return x;
}
// set new exponent
ix = (ix & 0x807fffff) | (hexp << 23);
return bitcast(ix, float);
}
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double ldexp(double x, int exp) @extern("ldexp")
{
uint hx = x.high_word();
int hexp = (int)((hx & 0x7ff00000) >> 20);
if (hexp == 0x7ff) return x;
if (hexp == 0)
{
// subnormal number handling
x *= 0x1p64;
hx = x.high_word();
hexp = (int)((hx & 0x7ff00000) >> 20) - 64;
}
// new exponent calculation
hexp += exp;
if (hexp > 0x7fe) return x * double.inf;
if (hexp < 1)
{
x *= 0x1p-1022;
hexp += 1022;
if (hexp < 1) x *= 0x1p-1022;
return x;
}
// set new exponent
hx = ((ulong)hx & 0x800fffff) | ((ulong)hexp << 20);
{
ulong rep = ((ulong)hx << 32) | x.low_word();
return bitcast(rep, double);
}
}
fn float ldexpf(float x, int exp) @extern("ldexpf")
{
uint ix = x.word();
int hexp = (int)((ix & 0x7f800000) >> 23);
if (hexp == 0xff) return x;
if (hexp == 0)
{
// subnormal number handling
x *= 0x1p64f;
ix = x.word();
hexp = (int)((ix & 0x7f800000) >> 23) - 64;
}
// new exponent calculation
hexp += exp;
if (hexp > 0xfe) return x * float.inf;
if (hexp < 1)
{
x *= 0x1p-126f;
hexp += 126;
if (hexp < 1) x *= 0x1p-126f;
return x;
}
// set new exponent
ix = (ix & 0x807fffff) | (hexp << 23);
return bitcast(ix, float);
}

View File

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

View File

@@ -66,8 +66,8 @@ fn Vec3 Vec3.refract(self, Vec3 n, double r) => refract3(self, n, r);
fn void ortho_normalize(Vec3f* v1, Vec3f* v2) => ortho_normalize3(v1, v2);
fn void ortho_normalized(Vec3* v1, Vec3* v2) => ortho_normalize3(v1, v2);
fn Matrix4f matrix4f_look_at(Vec3f eye, Vec3f target, Vec3f up) @deprecated => matrix::look_at(<float>)(eye, target, up);
fn Matrix4 matrix4_look_at(Vec3 eye, Vec3 target, Vec3 up) @deprecated => matrix::look_at(<double>)(eye, target, up);
fn Matrix4f matrix4f_look_at(Vec3f eye, Vec3f target, Vec3f up) @deprecated => matrix::look_at{float}(eye, target, up);
fn Matrix4 matrix4_look_at(Vec3 eye, Vec3 target, Vec3 up) @deprecated => matrix::look_at{double}(eye, target, up);
fn Vec3f Vec3f.rotate_quat(self, Quaternionf q) => rotate_by_quat3(self, q);
fn Vec3 Vec3.rotate_quat(self, Quaternion q) => rotate_by_quat3(self, q);

View File

@@ -35,7 +35,7 @@ fn usz! Uuid.to_format(&self, Formatter* formatter) @dynamic
(*self)[10], (*self)[11], (*self)[12], (*self)[13], (*self)[14], (*self)[15]);
}
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);
}

406
lib/std/net/http.c3 Normal file
View File

@@ -0,0 +1,406 @@
module std::net::http;
/*
enum HttpStatus
{
PENDING,
COMPLETED,
FAILED
}
struct Http
{
HttpStatus status;
int status_code;
String reason;
String content_type;
String response_data;
}
fn Http* http_get(String url, Allocator using = allocator::temp())
{
return null;
}
fn Http* http_post(String url, Allocator using = allocator::temp())
{
return null;
}
fn void Http.destroy(Http* this)
{
}
fn HttpStatus Http.process(Http* this)
{
unreachable();
}
// Common across implementations
struct HttpInternal @private
{
inline Http http;
Allocator allocator;
int connect_pending;
int request_sent;
char[256] address;
char[256] request_header;
char* request_header_large;
String request_data;
char[1024] reason_phrase;
char[256] content_type;
usz data_size;
usz data_capacity;
void* data;
}
fn String! parse_url(String url, String* port, String* resource) @private
{
if (url[:7] != "http://") return NetError.INVALID_URL?;
url = url[7..];
usz end_index = url.index_of(":") ?? url.index_of("/") ?? url.len;
String address = url[:end_index];
String end_part = url[end_index..];
if (!end_part.len)
{
*port = "80";
*resource = {};
return address;
}
switch (end_part[0])
{
case ':':
end_index = end_part.index_of("/") ?? end_part.len;
end_part[:end_index].to_uint() ?? NetError.INVALID_URL?!;
*port = end_part[:end_index];
*resource = url[end_index..];
case '/':
*port = "80";
*resource = end_part;
default:
unreachable();
}
return address;
}
fn Socket! http_internal_connect(String address, uint port) @private
{
return tcp::connect_async(address, port)!;
}
fn HttpInternal* http_internal_create(usz request_data_size, Allocator allocator) @private
{
HttpInternal* internal = allocator.alloc(HttpInternal.sizeof + request_data_size)!!;
internal.status = PENDING;
internal.status_code = 0;
internal.response_data = {};
internal.allocator = allocator;
internal.connect_pending = 1;
internal.request_sent = 0;
// internal.reason = "";
// internal.content_type = "";
internal.data_size = 0;
internal.data_capacity = 64 * 1024;
internal.data = allocator.alloc(internal.data_capacity)!!;
internal.request_data = {};
return internal;
}
fn Http*! http_get(String url, Allocator allocator = allocator::temp())
{
$if env::WIN32:
int[1024] wsa_data;
if (_wsa_startup(1, &wsa_data) != 0) return NetError.GENERAL_ERROR?;
$endif
uint port;
String resource;
String address = parse_url(url, &port, &resource)?;
Socket socket = tcp::connect(address, port)?;
HttpInternal* internal = http_internal_create(0, allocator);
internal.socket = socket;
char* request_header;
usz request_header_len = 64 + resource.len + address.len + port.len;
if (request_header_len < sizeof(internal.request_header))
{
internal.request_header_large = null;
request_header = internal.request_header;
}
else
{
internal.request_header_large = (char*)allocator.malloc(request_header_len + 1);
request_header = internal.request_header_large;
}
int default_http_port = port == "80";
sprintf( request_header, "GET %s HTTP/1.0\r\nHost: %s%s%s\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port );
return internal;
}
fn Http*! http_post(String url, Allocator allocator = allocator::temp())
{
$if env::OS_TYPE == OsType::WIN32:
int[1024] wsa_data;
if (_wsa_startup(1, &wsa_data) != 0) return NetError.GENERAL_ERROR?;
$endif
String port;
String resource;
String address = parse_url(url, &port, &resource)?;
Socket socket = http_internal_connect(address, port)?;
HttpInternal* internal = http_internal_create(0, allocator);
internal.socket = socket;
char* request_header;
uz request_header_len = 64 + resource.len + address.len + port.len;
if (request_header_len < sizeof(internal.request_header))
{
internal.request_header_large = null;
request_header = internal.request_header;
}
else
{
internal.request_header_large = (char*)allocator.malloc(request_header_len + 1);
request_header = internal.request_header_large;
}
int default_http_port = port == "80";
sprintf( request_header, "POST %s HTTP/1.0\r\nHost: %s%s%s\r\nContent-Length: %d\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port,
(int) size );
internal->request_data_size = size;
internal->request_data = ( internal + 1 );
memcpy( internal->request_data, data, size );
return internal;
}
fn HttpStatus Http.process(Http* http)
{
HttpInternal* internal = (HttpInternal*)http;
if (http.status == HttpStatus.FAILED) return http.status;
if (internal.connect_pending)
{
fd_set sockets_to_check;
FD_ZERO(&sockets_to_check);
FD_SET( internal->socket, &sockets_to_check );
struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
// check if socket is ready for send
if( select( (int)( internal->socket + 1 ), NULL, &sockets_to_check, NULL, &timeout ) == 1 )
{
int opt = -1;
socklen_t len = sizeof( opt );
if( getsockopt( internal->socket, SOL_SOCKET, SO_ERROR, (char*)( &opt ), &len) >= 0 && opt == 0 )
{
internal->connect_pending = 0; // if it is, we're connected
}
}
}
if (internal.connect_pending) retur http.status;
if (!internal.request_sent)
{
char* request_header = internal->request_header_large ?
internal.request_header_large : internal.request_header;
if (send(internal.socket, request_header, (int) strlen( request_header ), 0) == -1)
{
return http.status = FAILED;
}
if (internal.request_data_size)
{
int res = send(internal.socket, (char const*)internal->request_data, (int) internal->request_data_size, 0 );
if (res == -1)
{
http.status = HTTP_STATUS_FAILED;
return http.status;
}
}
internal.request_sent = 1;
return http.status;
}
// check if socket is ready for recv
fd_set sockets_to_check;
FD_ZERO( &sockets_to_check );
#pragma warning( push )
#pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect
FD_SET( internal->socket, &sockets_to_check );
#pragma warning( pop )
struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
}
http_status_t http_process( http_t* http )
{
while( select( (int)( internal->socket + 1 ), &sockets_to_check, NULL, NULL, &timeout ) == 1 )
{
char buffer[ 4096 ];
int size = recv( internal->socket, buffer, sizeof( buffer ), 0 );
if( size == -1 )
{
http->status = HTTP_STATUS_FAILED;
return http->status;
}
else if( size > 0 )
{
size_t min_size = internal->data_size + size + 1;
if( internal->data_capacity < min_size )
{
internal->data_capacity *= 2;
if( internal->data_capacity < min_size ) internal->data_capacity = min_size;
void* new_data = HTTP_MALLOC( memctx, internal->data_capacity );
memcpy( new_data, internal->data, internal->data_size );
HTTP_FREE( memctx, internal->data );
internal->data = new_data;
}
memcpy( (void*)( ( (uintptr_t) internal->data ) + internal->data_size ), buffer, (size_t) size );
internal->data_size += size;
}
else if( size == 0 )
{
char const* status_line = (char const*) internal->data;
int header_size = 0;
char const* header_end = strstr( status_line, "\r\n\r\n" );
if( header_end )
{
header_end += 4;
header_size = (int)( header_end - status_line );
}
else
{
http->status = HTTP_STATUS_FAILED;
return http->status;
}
// skip http version
status_line = strchr( status_line, ' ' );
if( !status_line )
{
http->status = HTTP_STATUS_FAILED;
return http->status;
}
++status_line;
// extract status code
char status_code[ 16 ];
char const* status_code_end = strchr( status_line, ' ' );
if( !status_code_end )
{
http->status = HTTP_STATUS_FAILED;
return http->status;
}
memcpy( status_code, status_line, (size_t)( status_code_end - status_line ) );
status_code[ status_code_end - status_line ] = 0;
status_line = status_code_end + 1;
http->status_code = atoi( status_code );
// extract reason phrase
char const* reason_phrase_end = strstr( status_line, "\r\n" );
if( !reason_phrase_end )
{
http->status = HTTP_STATUS_FAILED;
return http->status;
}
size_t reason_phrase_len = (size_t)( reason_phrase_end - status_line );
if( reason_phrase_len >= sizeof( internal->reason_phrase ) )
reason_phrase_len = sizeof( internal->reason_phrase ) - 1;
memcpy( internal->reason_phrase, status_line, reason_phrase_len );
internal->reason_phrase[ reason_phrase_len ] = 0;
status_line = reason_phrase_end + 1;
// extract content type
char const* content_type_start = strstr( status_line, "Content-Type: " );
if( content_type_start )
{
content_type_start += strlen( "Content-Type: " );
char const* content_type_end = strstr( content_type_start, "\r\n" );
if( content_type_end )
{
size_t content_type_len = (size_t)( content_type_end - content_type_start );
if( content_type_len >= sizeof( internal->content_type ) )
content_type_len = sizeof( internal->content_type ) - 1;
memcpy( internal->content_type, content_type_start, content_type_len );
internal->content_type[ content_type_len ] = 0;
}
}
http->status = http->status_code < 300 ? HTTP_STATUS_COMPLETED : HTTP_STATUS_FAILED;
http->response_data = (void*)( ( (uintptr_t) internal->data ) + header_size );
http->response_size = internal->data_size - header_size;
// add an extra zero after the received data, but don't modify the size, so ascii results can be used as
// a zero terminated string. the size returned will be the string without this extra zero terminator.
( (char*)http->response_data )[ http->response_size ] = 0;
return http->status;
}
}
return http->status;
}
void http_release( http_t* http )
{
http_internal_t* internal = (http_internal_t*) http;
#ifdef _WIN32
closesocket( internal->socket );
#else
close( internal->socket );
#endif
if( internal->request_header_large) HTTP_FREE( memctx, internal->request_header_large );
HTTP_FREE( memctx, internal->data );
HTTP_FREE( memctx, internal );
#ifdef _WIN32
WSACleanup();
#endif
}
#endif /* HTTP_IMPLEMENTATION */
/*
revision history:
1.0 first released version
*/
/*
------------------------------------------------------------------------------
This software is available under 2 licenses - you may choose the one you like.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2016 Mattias Gustavsson
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.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
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 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.
------------------------------------------------------------------------------
*/*/

View File

@@ -56,19 +56,14 @@ fn usz! InetAddress.to_format(InetAddress* addr, Formatter* formatter) @dynamic
return formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!;
}
fn String InetAddress.to_new_string(InetAddress* addr, Allocator allocator = allocator::heap()) @dynamic
fn String InetAddress.to_string(&self, Allocator allocator)
{
if (addr.is_ipv6)
{
char[8 * 5 + 1] buffer;
String res = (String)io::bprintf(&buffer, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
addr.ipv6.a, addr.ipv6.b, addr.ipv6.c, addr.ipv6.d,
addr.ipv6.e, addr.ipv6.f, addr.ipv6.g, addr.ipv6.h)!!;
return res.copy(allocator);
}
char[3 * 4 + 3 + 1] buffer;
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!!;
return res.copy(allocator);
return string::format(allocator, "%s", *self);
}
fn String InetAddress.to_tstring(&self)
{
return string::format(tmem(), "%s", *self);
}
fn InetAddress! ipv6_from_str(String s)

View File

@@ -58,14 +58,9 @@ fn uint! ipv4toint(String s)
return out;
}
fn String! int_to_new_ipv4(uint val, Allocator allocator = allocator::heap())
fn String! int_to_ipv4(uint val, Allocator allocator)
{
char[3 * 4 + 3 + 1] buffer;
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)!;
return res.copy(allocator);
}
fn String! int_to_temp_ipv4(uint val)
{
return int_to_new_ipv4(val, allocator::temp());
}

View File

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

View File

@@ -67,7 +67,7 @@ fn usz encode_len(String s, UrlEncodingMode mode) @inline
@param [inout] allocator
@return "Percent-encoded String"
*>
fn String encode(String s, UrlEncodingMode mode, Allocator allocator) => @pool(allocator)
fn String encode(Allocator allocator, String s, UrlEncodingMode mode) => @pool(allocator)
{
usz n = encode_len(s, mode);
DString builder = dstring::temp_with_capacity(n);
@@ -83,8 +83,8 @@ fn String encode(String s, UrlEncodingMode mode, Allocator allocator) => @pool(a
// add encoded char
case should_encode(c, mode):
builder.append_char('%');
String hex = hex::encode_temp(s[i:1]);
builder.append(hex.temp_ascii_to_upper());
String hex = hex::tencode(s[i:1]);
builder.append(hex.to_upper_tcopy());
// use char, no encoding needed
default:
@@ -95,15 +95,6 @@ fn String encode(String s, UrlEncodingMode mode, Allocator allocator) => @pool(a
return builder.copy_str(allocator);
}
<*
Encode the string s for a given encoding mode.
Returned string must be freed.
@param s "String to encode"
@param mode "Url encoding mode"
@return "Percent-encoded String"
*>
fn String new_encode(String s, UrlEncodingMode mode) => encode(s, mode, allocator::heap());
<*
Encode string s for a given encoding mode, stored on the temp allocator.
@@ -112,7 +103,7 @@ fn String new_encode(String s, UrlEncodingMode mode) => encode(s, mode, allocato
@param mode "Url encoding mode"
@return "Percent-encoded String"
*>
fn String temp_encode(String s, UrlEncodingMode mode) => encode(s, mode, allocator::temp());
fn String tencode(String s, UrlEncodingMode mode) => encode(tmem(), s, mode);
<*
Calculate the length of the percent-decoded string.
@@ -143,7 +134,7 @@ fn usz! decode_len(String s, UrlEncodingMode mode) @inline
@param [inout] allocator
@return "Percent-decoded String"
*>
fn String! decode(String s, UrlEncodingMode mode, Allocator allocator) => @pool(allocator)
fn String! decode(Allocator allocator, String s, UrlEncodingMode mode) => @pool(allocator)
{
usz n = decode_len(s, mode)!;
DString builder = dstring::temp_with_capacity(n);
@@ -154,7 +145,7 @@ fn String! decode(String s, UrlEncodingMode mode, Allocator allocator) => @pool
{
// decode encoded char
case '%':
char[] hex = hex::decode_temp(s[i+1:2])!;
char[] hex = hex::tdecode(s[i+1:2])!;
builder.append(hex);
i += 2;
@@ -171,15 +162,6 @@ fn String! decode(String s, UrlEncodingMode mode, Allocator allocator) => @pool
return builder.copy_str(allocator);
}
<*
Decode string s for a given encoding mode.
Returned string must be freed.
@param s "String to decode"
@param mode "Url encoding mode"
@return "Percent-decoded String"
*>
fn String! new_decode(String s, UrlEncodingMode mode) => decode(s, mode, allocator::heap());
<*
Decode string s for a given encoding mode, stored on the temp allocator.
@@ -188,4 +170,4 @@ fn String! new_decode(String s, UrlEncodingMode mode) => decode(s, mode, alloca
@param mode "Url encoding mode"
@return "Percent-decoded String"
*>
fn String! temp_decode(String s, UrlEncodingMode mode) => decode(s, mode, allocator::temp());
fn String! tdecode(String s, UrlEncodingMode mode) => decode(tmem(), s, mode);

View File

@@ -55,7 +55,7 @@ fn void Backtrace.free(&self)
allocator::free(self.allocator, self.file);
}
fn Backtrace* Backtrace.init(&self, uptr offset, String function, String object_file, String file = "", uint line = 0, Allocator allocator)
fn Backtrace* Backtrace.init(&self, Allocator allocator, uptr offset, String function, String object_file, String file = "", uint line = 0)
{
if (!allocator)
{
@@ -91,13 +91,13 @@ fn void*[] capture_current(void*[] buffer)
$endswitch
}
def BacktraceList = List(<Backtrace>);
def BacktraceList = List{Backtrace};
def symbolize_backtrace = linux::symbolize_backtrace @if(env::LINUX);
def symbolize_backtrace = win32::symbolize_backtrace @if(env::WIN32);
def symbolize_backtrace = darwin::symbolize_backtrace @if(env::DARWIN);
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator) @if(!env::NATIVE_STACKTRACE)
fn BacktraceList! symbolize_backtrace(Allocator allocator, void*[] backtrace) @if(!env::NATIVE_STACKTRACE)
{
return {};
}

View File

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

View File

@@ -128,17 +128,17 @@ fn ulong! elf_module_image_base(String path) @local
return 0;
}
fn void! backtrace_add_from_exec(BacktraceList* list, void* addr, Allocator allocator) @local
fn void! backtrace_add_from_exec(Allocator allocator, BacktraceList* list, void* addr) @local
{
char[] buf = mem::temp_alloc_array(char, 1024);
String exec_path = process::execute_stdout_to_buffer(buf, {"realpath", "-e", string::tformat("/proc/%d/exe", posix::getpid())})!;
String obj_name = exec_path.copy(allocator);
String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", exec_path, string::tformat("0x%x", addr)})!;
return backtrace_add_addr2line(list, addr, addr2line, obj_name, "???", allocator);
return backtrace_add_addr2line(allocator, list, addr, addr2line, obj_name, "???");
}
fn void! backtrace_add_from_dlinfo(BacktraceList* list, void* addr, Linux_Dl_info* info, Allocator allocator) @local
fn void! backtrace_add_from_dlinfo(Allocator allocator, BacktraceList* list, void* addr, Linux_Dl_info* info) @local
{
char[] buf = mem::temp_alloc_array(char, 1024);
@@ -146,10 +146,10 @@ fn void! backtrace_add_from_dlinfo(BacktraceList* list, void* addr, Linux_Dl_inf
ZString obj_path = info.dli_fname;
String sname = info.dli_sname ? info.dli_sname.str_view() : "???";
String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::tformat("0x%x", obj_addr - 1)})!;
return backtrace_add_addr2line(list, addr, addr2line, info.dli_fname.str_view(), sname, allocator);
return backtrace_add_addr2line(allocator, list, addr, addr2line, info.dli_fname.str_view(), sname);
}
fn Backtrace! backtrace_line_parse(String string, String obj_name, String func_name, bool is_inlined, Allocator allocator)
fn Backtrace! backtrace_line_parse(Allocator allocator, String string, String obj_name, String func_name, bool is_inlined)
{
String[] parts = string.trim().tsplit(" at ");
if (parts.len != 2) return SearchResult.MISSING?;
@@ -171,14 +171,14 @@ fn Backtrace! backtrace_line_parse(String string, String obj_name, String func_n
.is_inline = is_inlined
};
}
fn void! backtrace_add_addr2line(BacktraceList* list, void* addr, String addr2line, String obj_name, String func_name, Allocator allocator) @local
fn void! backtrace_add_addr2line(Allocator allocator, BacktraceList* list, void* addr, String addr2line, String obj_name, String func_name) @local
{
String[] inline_parts = addr2line.tsplit("(inlined by)");
usz last = inline_parts.len - 1;
foreach (i, part : inline_parts)
{
bool is_inline = i != last;
Backtrace! trace = backtrace_line_parse(part, obj_name, func_name, is_inline, allocator);
Backtrace! trace = backtrace_line_parse(allocator, part, obj_name, func_name, is_inline);
if (catch trace)
{
list.push({
@@ -196,7 +196,7 @@ fn void! backtrace_add_addr2line(BacktraceList* list, void* addr, String addr2li
}
}
fn void! backtrace_add_element(BacktraceList *list, void* addr, Allocator allocator = allocator::heap()) @local
fn void! backtrace_add_element(Allocator allocator, BacktraceList *list, void* addr) @local
{
if (!addr)
{
@@ -209,13 +209,13 @@ fn void! backtrace_add_element(BacktraceList *list, void* addr, Allocator alloca
Linux_Dl_info info;
if (dladdr(addr, &info) == 0)
{
return backtrace_add_from_exec(list, addr, allocator);
return backtrace_add_from_exec(allocator, list, addr);
}
return backtrace_add_from_dlinfo(list, addr, &info, allocator);
return backtrace_add_from_dlinfo(allocator, list, addr, &info);
};
}
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
fn BacktraceList! symbolize_backtrace(Allocator allocator, void*[] backtrace)
{
BacktraceList list;
list.init(allocator, backtrace.len);
@@ -231,7 +231,7 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
{
foreach (addr : backtrace)
{
backtrace_add_element(&list, addr, allocator)!;
backtrace_add_element(allocator, &list, addr)!;
}
};
return list;

View File

@@ -80,7 +80,7 @@ fn uptr! load_address() @local
{
Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT");
if (!cmd) return BacktraceFault.SEGMENT_NOT_FOUND?;
String path = env::new_executable_path(allocator::temp()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!;
String path = env::executable_path(tmem()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!;
uint dyld_count = darwin::_dyld_image_count();
for (uint i = 0; i < dyld_count; i++)
{
@@ -132,7 +132,7 @@ fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_a
};
}
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
fn BacktraceList! symbolize_backtrace(Allocator allocator, void*[] backtrace)
{
void *load_addr = (void *)load_address()!;
BacktraceList list;

View File

@@ -109,7 +109,7 @@ fn WString convert_command_line_win32(String[] command_line) @inline @if(env::WI
str.append('"');
}
str.append('\0');
return str.str_view().to_temp_wstring()!!;
return str.str_view().to_wstring_tcopy()!!;
}
<*
@@ -149,7 +149,7 @@ fn SubProcess! create(String[] command_line, SubProcessOptions options = {}, Str
env.append("\0");
}
env.append("\0");
used_environment = env.str_view().to_temp_wstring()!;
used_environment = env.str_view().to_wstring_tcopy()!;
}
int fd = win32::_open_osfhandle((iptr)wr, 0);
if (fd != -1)

View File

@@ -154,7 +154,7 @@ struct Symbol
Win32_DWORD64 displacement;
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
fn BacktraceList! symbolize_backtrace(Allocator allocator, void*[] backtrace)
{
BacktraceList list;
list.init(allocator, backtrace.len);
@@ -163,12 +163,12 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
defer symCleanup(process);
foreach (addr : backtrace)
{
list.push(resolve_backtrace(addr, process, allocator) ?? backtrace::BACKTRACE_UNKNOWN);
list.push(resolve_backtrace(allocator, addr, process) ?? backtrace::BACKTRACE_UNKNOWN);
}
return list;
}
fn Backtrace! resolve_backtrace(void* addr, Win32_HANDLE process, Allocator allocator)
fn Backtrace! resolve_backtrace(Allocator allocator, void* addr, Win32_HANDLE process)
{
Symbol symbol;
//Win32_DWORD image_type = load_modules()!;
@@ -193,10 +193,10 @@ fn Backtrace! resolve_backtrace(void* addr, Win32_HANDLE process, Allocator allo
ZString zname = (ZString)&name;
if (!symGetLineFromAddr64(process, (Win32_ULONG64)addr - 1, &offset, &line))
{
backtrace.init((uptr)addr, zname.str_view(), module_name.str_view(), allocator: allocator);
backtrace.init(allocator, (uptr)addr, zname.str_view(), module_name.str_view());
return backtrace;
}
String filename = ((ZString)line.fileName).str_view();
backtrace.init((uptr)addr, zname.str_view(), module_name.str_view(), file: filename, line: line.lineNumber, allocator: allocator);
backtrace.init(allocator, (uptr)addr, zname.str_view(), module_name.str_view(), file: filename, line: line.lineNumber);
return backtrace;
}

View File

@@ -11,20 +11,20 @@ Sort list using the counting sort algorithm.
macro countingsort(list, key_fn = EMPTY_MACRO_SLOT) @builtin
{
usz len = sort::len_from_list(list);
cs::csort(<$typeof(list), $typeof(key_fn)>)(list, 0, len, key_fn, ~((uint)0));
cs::csort{$typeof(list), $typeof(key_fn)}(list, 0, len, key_fn, ~((uint)0));
}
macro insertionsort_indexed(list, start, end, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
{
is::isort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, (usz)start, (usz)end, cmp, context);
is::isort{$typeof(list), $typeof(cmp), $typeof(context)}(list, (usz)start, (usz)end, cmp, context);
}
macro quicksort_indexed(list, start, end, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
{
qs::qsort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, (isz)start, (isz)(end-1), cmp, context);
qs::qsort{$typeof(list), $typeof(cmp), $typeof(context)}(list, (isz)start, (isz)(end-1), cmp, context);
}
module std::sort::cs(<Type, KeyFn>);
module std::sort::cs{Type, KeyFn};
def Counts = usz[256] @private;
def Ranges = usz[257] @private;

View File

@@ -10,14 +10,14 @@ macro insertionsort(list, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @b
{
$if @typekind(list) == POINTER &&& (@typekind(*list) == ARRAY || @typekind(*list) == VECTOR):
$typeof((*list)[0])[] list2 = list;
is::isort(<$typeof(list2), $typeof(cmp), $typeof(context)>)(list2, 0, list.len, cmp, context);
is::isort{$typeof(list2), $typeof(cmp), $typeof(context)}(list2, 0, list.len, cmp, context);
$else
usz len = sort::len_from_list(list);
is::isort(<$typeof(list), $typeof(cmp), $typeof(context)>)(list, 0, (isz)len, cmp, context);
is::isort{$typeof(list), $typeof(cmp), $typeof(context)}(list, 0, (isz)len, cmp, context);
$endif
}
module std::sort::is(<Type, CmpFn, Context>);
module std::sort::is{Type, CmpFn, Context};
def ElementType = $typeof(((Type){})[0]);

View File

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

17
lib/std/text/i18n.c3 Normal file
View File

@@ -0,0 +1,17 @@
/*module std::text::i18n;
import std::collections::map;
import std::hash::fnv32a;
distinct Language = char[];
const Language EN = "en";
def TranslationMap = HashMap{String, String};
fn uint Language.hash(self) => fnv32a::encode((char[])self);
HashMap{Language, TranslationMap*} language_map @private;
TranslationMap! current_map;
macro String @localized(String string) @builtin
{
return current_map[string] ?? string;
}

View File

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

View File

@@ -0,0 +1,208 @@
module std::thread::event;
/*
import std::collections;
enum EventThreadState
{
LEADER,
WORKER,
FOLLOWER,
KILLED
}
struct Event
{
int x;
bool is_cancelled;
}
struct EventThreadTask
{
EventHandler* target;
Event*[] events;
}
distinct EventQueue = void;
distinct EventHandler = void;
distinct EventMultiQueue = void;
fn EventThreadTask EventQueue.wait_for_task(&self) => EventThreadTask {};
struct EventThread
{
inline Thread thread;
EventThreadState state;
EventQueue* queue;
EventThreadPool* pool;
bool is_alive;
String name;
}
// synchronized
fn void EventThread.assign_to_leader(&self)
{
self.state = LEADER;
//self.notify_all();
}
fn void EventThread.kill(&self)
{
self.is_alive = false;
/* interrupt */
}
fn void wait_for_leadership(EventThread* thread) @local
{
// SYNCHRONICED
while (thread.is_alive && thread.state != LEADER)
{
//thread.wait();
}
}
// synchronized
fn int run_thread(void* arg) @local
{
EventThread* thread = arg;
EventThreadPool* pool = thread.pool;
while (thread.state != KILLED)
{
wait_for_leadership(thread);
if (!@volatile_load(thread.is_alive))
{
thread.state = KILLED;
continue;
}
EventThreadTask! task = thread.queue.wait_for_task();
if (catch task)
{
pool.notify_user_shutdown(thread);
continue;
}
thread.state = WORKER;
pool.notify_thread_start(thread);
foreach (e : task.events)
{
run_event(task.target, e);
}
//task.target.finished_processing_event();
thread.state = FOLLOWER;
pool.notify_thread_end(thread);
}
return 0;
}
fn void run_event(EventHandler* handler, Event* event)
{
//defer event.free();
if (event.is_cancelled) return;
// handler.process_event(event);
}
struct EventThreadPool
{
Allocator allocator;
String name;
LinkedList{EventThread*} pool;
EventThread* leader;
bool is_alive;
int busy_threads;
void* monitor_context;
List{EventThread*} threads;
EventMultiQueue* queue;
}
// synchronized
fn void EventThreadPool.notify_thread_start(&self, EventThread* thread)
{
self.busy_threads++;
if (!self.is_alive) return;
if (try follower = self.pool.pop())
{
follower.assign_to_leader();
self.leader = follower;
return;
}
self.leader = null;
}
// sync
fn void EventThreadPool.notify_thread_end(&self, EventThread* thread)
{
self.busy_threads--;
if (!self.is_alive)
{
thread.kill();
self.leader = null;
return;
}
if (self.leader)
{
self.pool.push(thread);
return;
}
thread.assign_to_leader();
self.leader = thread;
return;
}
// sync
fn void EventThreadPool.notify_user_shutdown(&self, EventThread* thread)
{
// monitor.notifyThreadPoolShutdown(this);
remove_all_pool_threads(self);
thread.kill();
self.leader = null;
self.is_alive = false;
}
fn void remove_all_pool_threads(EventThreadPool* pool) @local
{
while (try thread = pool.pool.pop())
{
thread.kill();
}
if (pool.leader)
{
pool.leader.kill();
pool.leader = null;
}
}
<*
@require size > 0 "Must have at least one thread"
*>
fn EventThreadPool* EventThreadPool.init(&self, int size, String name, Allocator allocator, void* monitor_context)
{
*self = { .is_alive = true, .name = name.copy(allocator), .monitor_context = monitor_context, .allocator = allocator };
self.pool.init(allocator);
self.threads.init(allocator: allocator);
for (int i = 0; i < size; i++)
{
EventThread* thread = allocator::new(allocator, EventThread, { .is_alive = true, .state = FOLLOWER, .pool = null, .queue = null /* pool.getQueue() */, });
thread.name = string::format("%s:%d", name, i, allocator: allocator);
self.threads.push(thread);
if (self.leader)
{
self.pool.push(thread);
}
else
{
thread.assign_to_leader();
self.leader = thread;
}
}
foreach (t : self.threads)
{
// t.start()
}
return self;
}
// sync
fn void EventThreadPool.destroy(&self)
{
remove_all_pool_threads(self);
self.is_alive = false;
}

View File

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

View File

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

View File

@@ -20,59 +20,54 @@ enum DateTimeFormat
TIMEONLY, // "15:04:05"
}
fn String format(DateTimeFormat type, TzDateTime dt, Allocator allocator)
fn String format(Allocator allocator, DateTimeFormat type, TzDateTime dt)
{
switch (type)
{
case ANSIC:
return string::format("%s %s %2d %02d:%02d:%02d %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, dt.year, allocator: allocator);
return string::format(allocator, "%s %s %2d %02d:%02d:%02d %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, dt.year);
case UNIXDATE:
return string::format("%s %s %2d %02d:%02d:%02d GMT %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, dt.year, allocator: allocator);
return string::format(allocator, "%s %s %2d %02d:%02d:%02d GMT %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, dt.year);
case RUBYDATE:
return string::format("%s %s %2d %02d:%02d:%02d %s %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix(dt.gmt_offset), dt.year, allocator: allocator);
return string::format(allocator, "%s %s %2d %02d:%02d:%02d %s %04d", dt.weekday.abbrev, dt.month.abbrev, dt.day, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix(dt.gmt_offset), dt.year);
case RFC822:
dt = dt.to_gmt_offset(0); // For named representations of the timezone we always go for GMT, which is required by some RFCs
return string::format("%02d %s %02d %02d:%02d GMT", dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min, allocator: allocator);
return string::format(allocator, "%02d %s %02d %02d:%02d GMT", dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min);
case RFC822Z:
return string::format("%02d %s %02d %02d:%02d %s", dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min, temp_numeric_tzsuffix(dt.gmt_offset), allocator: allocator);
return string::format(allocator, "%02d %s %02d %02d:%02d %s", dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min, temp_numeric_tzsuffix(dt.gmt_offset));
case RFC850:
dt = dt.to_gmt_offset(0); // For named representations of the timezone we always go for GMT, which is required by some RFCs
return string::format("%s, %02d-%s-%02d %02d:%02d:%02d GMT", dt.weekday.name, dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min, dt.sec, allocator: allocator);
return string::format(allocator, "%s, %02d-%s-%02d %02d:%02d:%02d GMT", dt.weekday.name, dt.day, dt.month.abbrev, dt.year % 100, dt.hour, dt.min, dt.sec);
case RFC1123:
dt = dt.to_gmt_offset(0); // For named representations of the timezone we always go for GMT, which is required by some RFCs
return string::format("%s, %02d %s %d %02d:%02d:%02d GMT", dt.weekday.abbrev, dt.day, dt.month.abbrev, dt.year, dt.hour, dt.min, dt.sec, allocator: allocator);
return string::format(allocator, "%s, %02d %s %d %02d:%02d:%02d GMT", dt.weekday.abbrev, dt.day, dt.month.abbrev, dt.year, dt.hour, dt.min, dt.sec);
case RFC1123Z:
return string::format("%s, %02d %s %d %02d:%02d:%02d %s", dt.weekday.abbrev, dt.day, dt.month.abbrev, dt.year, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix(dt.gmt_offset), allocator: allocator);
return string::format(allocator, "%s, %02d %s %d %02d:%02d:%02d %s", dt.weekday.abbrev, dt.day, dt.month.abbrev, dt.year, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix(dt.gmt_offset));
case RFC3339:
dt = dt.to_gmt_offset(0);
return string::format("%04d-%02d-%02dT%02d:%02d:%02dZ", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, allocator: allocator);
return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02dZ", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec);
case RFC3339Z:
return string::format("%04d-%02d-%02dT%02d:%02d:%02d%s", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix_colon(dt.gmt_offset), allocator: allocator);
return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d%s", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix_colon(dt.gmt_offset));
case RFC3339MS:
dt = dt.to_gmt_offset(0);
return string::format("%04d-%02d-%02dT%02d:%02d:%02d.%dZ", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec, allocator: allocator);
return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d.%dZ", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec);
case RFC3339ZMS:
return string::format("%04d-%02d-%02dT%02d:%02d:%02d.%d%s", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec, temp_numeric_tzsuffix_colon(dt.gmt_offset), allocator: allocator);
return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d.%d%s", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec, temp_numeric_tzsuffix_colon(dt.gmt_offset));
case DATETIME:
return string::format("%04d-%02d-%02d %02d:%02d:%02d", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, allocator: allocator);
return string::format(allocator, "%04d-%02d-%02d %02d:%02d:%02d", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec);
case DATEONLY:
return string::format("%04d-%02d-%02d", dt.year, dt.month + 1, dt.day, allocator: allocator);
return string::format(allocator, "%04d-%02d-%02d", dt.year, dt.month + 1, dt.day);
case TIMEONLY:
return string::format("%02d:%02d:%02d", dt.hour, dt.min, dt.sec, allocator: allocator);
return string::format(allocator, "%02d:%02d:%02d", dt.hour, dt.min, dt.sec);
}
}
fn String new_format(DateTimeFormat dt_format, TzDateTime dt) => format(dt_format, dt, allocator::heap());
fn String temp_format(DateTimeFormat dt_format, TzDateTime dt) => format(dt_format, dt, allocator::temp());
fn String tformat(DateTimeFormat dt_format, TzDateTime dt) => format(tmem(), dt_format, dt);
fn String TzDateTime.format(self, DateTimeFormat dt_format, Allocator allocator) => format(dt_format, self, allocator);
fn String TzDateTime.new_format(self, DateTimeFormat dt_format) => format(dt_format, self, allocator::heap());
fn String TzDateTime.temp_format(self, DateTimeFormat dt_format) => format(dt_format, self, allocator::temp());
fn String TzDateTime.format(self, Allocator allocator, DateTimeFormat dt_format) => format(allocator, dt_format, self);
// .with_gmt_offset(0) instead of .to_local() is used to avoid surprises when user is formatting to a representation without a timezone
fn String DateTime.format(self, DateTimeFormat dt_format, Allocator allocator) => format(dt_format, self.with_gmt_offset(0), allocator);
fn String DateTime.new_format(self, DateTimeFormat dt_format) => format(dt_format, self.with_gmt_offset(0), allocator::heap());
fn String DateTime.temp_format(self, DateTimeFormat dt_format) => format(dt_format, self.with_gmt_offset(0), allocator::temp());
fn String DateTime.format(self, Allocator allocator, DateTimeFormat dt_format) => format(allocator, dt_format, self.with_gmt_offset(0));
<*
Returns the timezone offset in the format of "+HHMM" or "-HHMM"