mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Added += and related as overloads. Updated tests and docs. Slice2 extracted to its own file.
This commit is contained in:
@@ -1,18 +1,22 @@
|
||||
// Copyright (c) 2023-2025 C3 team. All rights reserved.
|
||||
// Use of self source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
<*
|
||||
@require SIZE > 0
|
||||
@require SIZE > 0 : "The size of the bitset in bits must be at least 1"
|
||||
*>
|
||||
module std::collections::bitset {SIZE};
|
||||
|
||||
alias Type = uint;
|
||||
|
||||
const BITS = Type.sizeof * 8;
|
||||
const BITS = uint.sizeof * 8;
|
||||
const SZ = (SIZE + BITS - 1) / BITS;
|
||||
|
||||
struct BitSet
|
||||
{
|
||||
Type[SZ] data;
|
||||
uint[SZ] data;
|
||||
}
|
||||
|
||||
<*
|
||||
@return "The number of bits set"
|
||||
*>
|
||||
fn usz BitSet.cardinality(&self)
|
||||
{
|
||||
usz n;
|
||||
@@ -24,7 +28,11 @@ fn usz BitSet.cardinality(&self)
|
||||
}
|
||||
|
||||
<*
|
||||
@require i < SIZE
|
||||
Set a bit in the bitset.
|
||||
|
||||
@param i : "The index to set"
|
||||
|
||||
@require i < SIZE : "Index was out of range"
|
||||
*>
|
||||
fn void BitSet.set(&self, usz i)
|
||||
{
|
||||
@@ -34,7 +42,86 @@ fn void BitSet.set(&self, usz i)
|
||||
}
|
||||
|
||||
<*
|
||||
@require i < SIZE
|
||||
Perform xor over all bits, mutating itself
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
macro BitSet BitSet.xor_self(&self, BitSet set) @operator(^=)
|
||||
{
|
||||
foreach (i, &x : self.data) *x ^= set.data[i];
|
||||
return *self;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform xor over all bits, returning a new bit set.
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
fn BitSet BitSet.xor(&self, BitSet set) @operator(^)
|
||||
{
|
||||
BitSet new_set @noinit;
|
||||
foreach (i, x : self.data) new_set.data[i] = x ^ set.data[i];
|
||||
return new_set;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform or over all bits, returning a new bit set.
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
fn BitSet BitSet.or(&self, BitSet set) @operator(|)
|
||||
{
|
||||
BitSet new_set @noinit;
|
||||
foreach (i, x : self.data) new_set.data[i] = x | set.data[i];
|
||||
return new_set;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform or over all bits, mutating itself
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
macro BitSet BitSet.or_self(&self, BitSet set) @operator(|=)
|
||||
{
|
||||
foreach (i, &x : self.data) *x |= set.data[i];
|
||||
return *self;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform & over all bits, returning a new bit set.
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
fn BitSet BitSet.and(&self, BitSet set) @operator(&)
|
||||
{
|
||||
BitSet new_set @noinit;
|
||||
foreach (i, x : self.data) new_set.data[i] = x & set.data[i];
|
||||
return new_set;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform & over all bits, mutating itself.
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
macro BitSet BitSet.and_self(&self, BitSet set) @operator(&=)
|
||||
{
|
||||
foreach (i, &x : self.data) *x &= set.data[i];
|
||||
return *self;
|
||||
}
|
||||
|
||||
<*
|
||||
Unset (clear) a bit in the bitset.
|
||||
|
||||
@param i : "The index to set"
|
||||
|
||||
@require i < SIZE : "Index was out of range"
|
||||
*>
|
||||
fn void BitSet.unset(&self, usz i)
|
||||
{
|
||||
@@ -44,7 +131,11 @@ fn void BitSet.unset(&self, usz i)
|
||||
}
|
||||
|
||||
<*
|
||||
@require i < SIZE
|
||||
Get a particular bit in the bitset
|
||||
|
||||
@param i : "The index of the bit"
|
||||
|
||||
@require i < SIZE : "Index was out of range"
|
||||
*>
|
||||
fn bool BitSet.get(&self, usz i) @operator([]) @inline
|
||||
{
|
||||
@@ -59,7 +150,12 @@ fn usz BitSet.len(&self) @operator(len) @inline
|
||||
}
|
||||
|
||||
<*
|
||||
@require i < SIZE
|
||||
Change a particular bit in the bitset
|
||||
|
||||
@param i : "The index of the bit"
|
||||
@param value : "The value to set the bit to"
|
||||
|
||||
@require i < SIZE : "Index was out of range"
|
||||
*>
|
||||
fn void BitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline
|
||||
{
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
|
||||
// Copyright (c) 2023-2025 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::mem::allocator;
|
||||
import std::math;
|
||||
|
||||
// The arena allocator allocates up to its maximum data
|
||||
// and then fails to allocate more, returning out of memory.
|
||||
// It supports mark and reset to mark.
|
||||
|
||||
struct ArenaAllocator (Allocator)
|
||||
{
|
||||
char[] data;
|
||||
@@ -12,6 +16,8 @@ struct ArenaAllocator (Allocator)
|
||||
|
||||
<*
|
||||
Initialize a memory arena for use using the provided bytes.
|
||||
|
||||
@param [inout] data : "The memory to use for the arena."
|
||||
*>
|
||||
fn ArenaAllocator* ArenaAllocator.init(&self, char[] data)
|
||||
{
|
||||
@@ -20,23 +26,44 @@ fn ArenaAllocator* ArenaAllocator.init(&self, char[] data)
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
Reset the usage completely.
|
||||
*>
|
||||
fn void ArenaAllocator.clear(&self)
|
||||
{
|
||||
self.used = 0;
|
||||
}
|
||||
|
||||
struct ArenaAllocatorHeader @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
<*
|
||||
Given some memory, create an arena allocator on the stack for it.
|
||||
|
||||
@param [inout] bytes : `The bytes to use`
|
||||
|
||||
@return `An arena allocator using the bytes`
|
||||
*>
|
||||
macro ArenaAllocator* wrap(char[] bytes)
|
||||
{
|
||||
return (ArenaAllocator){}.init(bytes);
|
||||
}
|
||||
|
||||
<*
|
||||
"Mark" the current state of the arena allocator by returning the use count.
|
||||
|
||||
@return `The value to pass to 'reset' in order to reset to the current use.`
|
||||
*>
|
||||
fn usz ArenaAllocator.mark(&self) => self.used;
|
||||
|
||||
<*
|
||||
Reset to a previous mark.
|
||||
|
||||
@param mark : `The previous mark.`
|
||||
@require mark <= self.used : "Invalid mark - out of range"
|
||||
*>
|
||||
fn void ArenaAllocator.reset(&self, usz mark) => self.used = mark;
|
||||
|
||||
<*
|
||||
Implements the Allocator interface method.
|
||||
|
||||
@require ptr != null
|
||||
*>
|
||||
fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||
@@ -50,10 +77,10 @@ fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||
}
|
||||
}
|
||||
|
||||
fn usz ArenaAllocator.mark(&self) => self.used;
|
||||
fn void ArenaAllocator.reset(&self, usz mark) => self.used = mark;
|
||||
|
||||
<*
|
||||
Implements the Allocator interface method.
|
||||
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
|
||||
@require size > 0
|
||||
@@ -77,6 +104,8 @@ fn void*? ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz a
|
||||
}
|
||||
|
||||
<*
|
||||
Implements the Allocator interface method.
|
||||
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
|
||||
@require old_pointer != null
|
||||
@@ -112,3 +141,11 @@ fn void*? ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
|
||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
|
||||
// Internal data
|
||||
|
||||
struct ArenaAllocatorHeader @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,14 @@ macro index_of(array, element)
|
||||
}
|
||||
|
||||
<*
|
||||
Slice a 2d array and create a Slice2d from it.
|
||||
|
||||
@param array_ptr : "the pointer to create a slice from"
|
||||
@param x : "The starting position of the slice x, optional"
|
||||
@param y : "The starting position of the slice y, optional"
|
||||
@param xlen : "The length of the slice in x, defaults to the length of the array"
|
||||
@param ylen : "The length of the slice in y, defaults to the length of the array"
|
||||
@return "A Slice2d from the array"
|
||||
@require @typekind(array_ptr) == POINTER
|
||||
@require @typekind(*array_ptr) == VECTOR || @typekind(*array_ptr) == ARRAY
|
||||
@require @typekind((*array_ptr)[0]) == VECTOR || @typekind((*array_ptr)[0]) == ARRAY
|
||||
@@ -82,96 +90,3 @@ macro concat(Allocator allocator, arr1, arr2) @nodiscard
|
||||
@ensure return.len == arr1.len + arr2.len
|
||||
*>
|
||||
macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);
|
||||
|
||||
module std::core::array::slice{Type};
|
||||
|
||||
struct Slice2d
|
||||
{
|
||||
Type* ptr;
|
||||
usz inner_len;
|
||||
usz ystart;
|
||||
usz ylen;
|
||||
usz xstart;
|
||||
usz xlen;
|
||||
}
|
||||
|
||||
fn usz Slice2d.len(&self) @operator(len)
|
||||
{
|
||||
return self.ylen;
|
||||
}
|
||||
|
||||
fn usz Slice2d.count(&self)
|
||||
{
|
||||
return self.ylen * self.xlen;
|
||||
}
|
||||
|
||||
macro void Slice2d.@each(&self; @body(usz[<2>], Type))
|
||||
{
|
||||
foreach (y, line : *self)
|
||||
{
|
||||
foreach (x, val : line)
|
||||
{
|
||||
@body({ x, y }, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
|
||||
{
|
||||
foreach (y, line : *self)
|
||||
{
|
||||
foreach (x, &val : line)
|
||||
{
|
||||
@body({ x, y }, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@require idy >= 0 && idy < self.ylen
|
||||
*>
|
||||
macro Type[] Slice2d.get_row(self, usz idy) @operator([])
|
||||
{
|
||||
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
|
||||
}
|
||||
|
||||
macro Type Slice2d.get_coord(self, usz[<2>] coord)
|
||||
{
|
||||
return *self.get_coord_ref(coord);
|
||||
}
|
||||
|
||||
macro Type Slice2d.get_xy(self, x, y)
|
||||
{
|
||||
return *self.get_xy_ref(x, y);
|
||||
}
|
||||
|
||||
macro Type* Slice2d.get_xy_ref(self, x, y)
|
||||
{
|
||||
return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
|
||||
}
|
||||
|
||||
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
|
||||
{
|
||||
return self.get_xy_ref(coord.x, coord.y);
|
||||
}
|
||||
|
||||
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
|
||||
{
|
||||
*self.get_coord_ref(coord) = value;
|
||||
}
|
||||
|
||||
macro void Slice2d.set_xy(self, x, y, Type value)
|
||||
{
|
||||
*self.get_xy_ref(x, y) = value;
|
||||
}
|
||||
|
||||
<*
|
||||
@require y >= 0 && y < self.ylen
|
||||
@require x >= 0 && x < self.xlen
|
||||
*>
|
||||
fn Slice2d Slice2d.slice(&self, isz x = 0, isz xlen = 0, isz y = 0, isz ylen = 0)
|
||||
{
|
||||
if (xlen < 1) xlen = self.xlen + xlen;
|
||||
if (ylen < 1) ylen = self.ylen + ylen;
|
||||
return { self.ptr, self.inner_len, y + self.ystart, ylen, x + self.xstart, xlen };
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno and contributors. All rights reserved.
|
||||
// Copyright (c) 2023-2025 Christoffer Lerno and contributors. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::bitorder;
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
module std::core::dstring;
|
||||
import std::io;
|
||||
|
||||
<*
|
||||
The DString offers a dynamic string builder.
|
||||
*>
|
||||
typedef DString (OutStream) = DStringOpaque*;
|
||||
typedef DStringOpaque = void;
|
||||
|
||||
const usz MIN_CAPACITY @private = 16;
|
||||
|
||||
<*
|
||||
Initialize the DString with a particular allocator.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@param capacity : "Starting capacity, defaults to MIN_CAPACITY and cannot be smaller"
|
||||
@return "Return the DString itself"
|
||||
@require !self.data() : "String already initialized"
|
||||
*>
|
||||
fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY)
|
||||
@@ -20,6 +28,11 @@ fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY)
|
||||
}
|
||||
|
||||
<*
|
||||
Initialize the DString with the temp allocator. Note that if the dstring is never
|
||||
initialized, this is the allocator it will default to.
|
||||
|
||||
@param capacity : "Starting capacity, defaults to MIN_CAPACITY and cannot be smaller"
|
||||
@return "Return the DString itself"
|
||||
@require !self.data() : "String already initialized"
|
||||
*>
|
||||
fn DString DString.tinit(&self, usz capacity = MIN_CAPACITY)
|
||||
|
||||
169
lib/std/core/slice2d.c3
Normal file
169
lib/std/core/slice2d.c3
Normal file
@@ -0,0 +1,169 @@
|
||||
module std::core::array::slice {Type};
|
||||
|
||||
<*
|
||||
A slice2d allows slicing an array like int[10][10] into an arbitrary "int[][]"-like counterpart
|
||||
Typically you'd use array::slice2d(...) to create one.
|
||||
*>
|
||||
struct Slice2d
|
||||
{
|
||||
Type* ptr;
|
||||
usz inner_len;
|
||||
usz ystart;
|
||||
usz ylen;
|
||||
usz xstart;
|
||||
usz xlen;
|
||||
}
|
||||
|
||||
<*
|
||||
@return `The length of the "outer" slice`
|
||||
*>
|
||||
fn usz Slice2d.len(&self) @operator(len)
|
||||
{
|
||||
return self.ylen;
|
||||
}
|
||||
|
||||
<*
|
||||
@return `The total number of elements.`
|
||||
*>
|
||||
fn usz Slice2d.count(&self)
|
||||
{
|
||||
return self.ylen * self.xlen;
|
||||
}
|
||||
|
||||
<*
|
||||
Step through each element of the slice.
|
||||
*>
|
||||
macro void Slice2d.@each(&self; @body(usz[<2>], Type))
|
||||
{
|
||||
foreach (y, line : *self)
|
||||
{
|
||||
foreach (x, val : line)
|
||||
{
|
||||
@body({ x, y }, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Step through each element of the slice *by reference*
|
||||
*>
|
||||
macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
|
||||
{
|
||||
foreach (y, line : *self)
|
||||
{
|
||||
foreach (x, &val : line)
|
||||
{
|
||||
@body({ x, y }, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Return a row as a slice.
|
||||
|
||||
@param idy : "The row to return"
|
||||
@return "The slice for the particular row"
|
||||
@require idy >= 0 && idy < self.ylen
|
||||
*>
|
||||
macro Type[] Slice2d.get_row(self, usz idy) @operator([])
|
||||
{
|
||||
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
|
||||
}
|
||||
|
||||
<*
|
||||
Get the value at a particular x/y position in the slice.
|
||||
|
||||
@param coord : "The xy coordinate"
|
||||
@return "The value at that coordinate"
|
||||
@require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
|
||||
@require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro Type Slice2d.get_coord(self, usz[<2>] coord)
|
||||
{
|
||||
return *self.get_coord_ref(coord);
|
||||
}
|
||||
|
||||
<*
|
||||
Get a pointer to the value at a particular x/y position in the slice.
|
||||
|
||||
@param coord : "The xy coordinate"
|
||||
@return "A pointer to the value at that coordinate"
|
||||
@require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
|
||||
@require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
|
||||
{
|
||||
return self.get_xy_ref(coord.x, coord.y);
|
||||
}
|
||||
|
||||
<*
|
||||
Get the value at a particular x/y position in the slice.
|
||||
|
||||
@param x : "The x coordinate"
|
||||
@param y : "The x coordinate"
|
||||
@return "The value at that coordinate"
|
||||
@require y >= 0 && y < self.ylen : "y value out of range"
|
||||
@require x >= 0 && x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro Type Slice2d.get_xy(self, x, y)
|
||||
{
|
||||
return *self.get_xy_ref(x, y);
|
||||
}
|
||||
|
||||
<*
|
||||
Get the value at a particular x/y position in the slice by reference.
|
||||
|
||||
@param x : "The x coordinate"
|
||||
@param y : "The y coordinate"
|
||||
@return "A pointer to the value at that coordinate"
|
||||
@require y >= 0 && y < self.ylen : "y value out of range"
|
||||
@require x >= 0 && x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro Type* Slice2d.get_xy_ref(self, x, y)
|
||||
{
|
||||
return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
|
||||
}
|
||||
|
||||
<*
|
||||
Set the ´value at a particular x/y position in the slice.
|
||||
|
||||
@param coord : "The xy coordinate"
|
||||
@param value : "The new value"
|
||||
@require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
|
||||
@require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
|
||||
{
|
||||
*self.get_coord_ref(coord) = value;
|
||||
}
|
||||
|
||||
<*
|
||||
Set the value at a particular x/y position in the slice.
|
||||
|
||||
@param x : "The x coordinate"
|
||||
@param y : "The y coordinate"
|
||||
@param value : "The new value"
|
||||
@require y >= 0 && y < self.ylen : "y value out of range"
|
||||
@require x >= 0 && x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro void Slice2d.set_xy(self, x, y, Type value)
|
||||
{
|
||||
*self.get_xy_ref(x, y) = value;
|
||||
}
|
||||
|
||||
<*
|
||||
Reslice a slice2d returning a new slice.
|
||||
|
||||
@param x : "The starting x"
|
||||
@param xlen : "The length along x"
|
||||
@param y : "The starting y"
|
||||
@param ylen : "The length along y"
|
||||
@require y >= 0 && y < self.ylen
|
||||
@require x >= 0 && x < self.xlen
|
||||
*>
|
||||
fn Slice2d Slice2d.slice(&self, isz x = 0, isz xlen = 0, isz y = 0, isz ylen = 0)
|
||||
{
|
||||
if (xlen < 1) xlen = self.xlen + xlen;
|
||||
if (ylen < 1) ylen = self.ylen + ylen;
|
||||
return { self.ptr, self.inner_len, y + self.ystart, ylen, x + self.xstart, xlen };
|
||||
}
|
||||
@@ -127,13 +127,13 @@ fn bool BigInt.is_negative(&self)
|
||||
return self.data[MAX_LEN - 1] & 0x80000000 != 0;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.add(self, BigInt other)
|
||||
fn BigInt BigInt.add(self, BigInt other) @operator(+)
|
||||
{
|
||||
self.add_this(other);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.add_this(&self, BigInt other)
|
||||
fn void BigInt.add_this(&self, BigInt other) @operator(+=)
|
||||
{
|
||||
bool sign = self.is_negative();
|
||||
bool sign_arg = other.is_negative();
|
||||
@@ -178,7 +178,7 @@ fn BigInt BigInt.mult(self, BigInt bi2) @operator(*)
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.mult_this(&self, BigInt bi2)
|
||||
fn void BigInt.mult_this(&self, BigInt bi2) @operator(*=)
|
||||
{
|
||||
if (bi2.is_zero())
|
||||
{
|
||||
@@ -276,7 +276,7 @@ fn BigInt BigInt.sub(self, BigInt other) @operator(-)
|
||||
return self;
|
||||
}
|
||||
|
||||
fn BigInt* BigInt.sub_this(&self, BigInt other)
|
||||
fn BigInt* BigInt.sub_this(&self, BigInt other) @operator(-=)
|
||||
{
|
||||
self.len = max(self.len, other.len);
|
||||
|
||||
@@ -340,7 +340,7 @@ macro BigInt BigInt.div(self, BigInt other) @operator(/)
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.div_this(&self, BigInt other)
|
||||
fn void BigInt.div_this(&self, BigInt other) @operator(/=)
|
||||
{
|
||||
bool negate_answer = self.is_negative();
|
||||
|
||||
@@ -383,7 +383,7 @@ fn BigInt BigInt.mod(self, BigInt bi2) @operator(%)
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.mod_this(&self, BigInt bi2)
|
||||
fn void BigInt.mod_this(&self, BigInt bi2) @operator(%=)
|
||||
{
|
||||
if (bi2.is_negative())
|
||||
{
|
||||
@@ -440,7 +440,7 @@ fn BigInt BigInt.shr(self, int shift) @operator(>>)
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.shr_this(self, int shift)
|
||||
fn void BigInt.shr_this(self, int shift) @operator(>>=)
|
||||
{
|
||||
self.len = shift_right(&self.data, self.len, shift);
|
||||
}
|
||||
@@ -818,7 +818,7 @@ fn void BigInt.bit_xor_this(&self, BigInt bi2)
|
||||
self.reduce_len();
|
||||
}
|
||||
|
||||
fn void BigInt.shl_this(&self, int shift)
|
||||
fn void BigInt.shl_this(&self, int shift) @operator(<<=)
|
||||
{
|
||||
self.len = shift_left(&self.data, self.len, shift);
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
- Better errors trying to convert an enum to an int and vice versa.
|
||||
- Function `@require` checks are added to the caller in safe mode. #186
|
||||
- Improved error message when narrowing isn't allowed.
|
||||
- Operator overloading for `+ - * / % & | ^ << >> ~ == !=`
|
||||
- Operator overloading for `+ - * / % & | ^ << >> ~ == != += -= *= /= %= &= |= ^= <<= >>=`
|
||||
- Add `@operator_r` and `@operator_s` attributes.
|
||||
- More stdlib tests: `sincos`.
|
||||
- More stdlib tests: `sincos`, `ArenaAllocator`, `Slice2d`.
|
||||
|
||||
### Fixes
|
||||
- Trying to cast an enum to int and back caused the compiler to crash.
|
||||
|
||||
@@ -926,7 +926,17 @@ typedef enum
|
||||
OVERLOAD_SHR,
|
||||
OVERLOAD_EQUAL,
|
||||
OVERLOAD_NOT_EQUAL,
|
||||
OVERLOADS_COUNT = OVERLOAD_NOT_EQUAL
|
||||
OVERLOAD_PLUS_ASSIGN,
|
||||
OVERLOAD_MINUS_ASSIGN,
|
||||
OVERLOAD_MULTIPLY_ASSIGN,
|
||||
OVERLOAD_DIVIDE_ASSIGN,
|
||||
OVERLOAD_REMINDER_ASSIGN,
|
||||
OVERLOAD_AND_ASSIGN,
|
||||
OVERLOAD_OR_ASSIGN,
|
||||
OVERLOAD_XOR_ASSIGN,
|
||||
OVERLOAD_SHL_ASSIGN,
|
||||
OVERLOAD_SHR_ASSIGN,
|
||||
OVERLOADS_COUNT = OVERLOAD_SHR_ASSIGN
|
||||
} OperatorOverload;
|
||||
|
||||
typedef enum
|
||||
|
||||
@@ -960,33 +960,63 @@ static Expr *parse_overload_from_token(ParseContext *c, TokenType token)
|
||||
case TOKEN_PLUS:
|
||||
overload = OVERLOAD_PLUS;
|
||||
break;
|
||||
case TOKEN_PLUS_ASSIGN:
|
||||
overload = OVERLOAD_PLUS_ASSIGN;
|
||||
break;
|
||||
case TOKEN_MINUS:
|
||||
overload = OVERLOAD_MINUS;
|
||||
break;
|
||||
case TOKEN_MINUS_ASSIGN:
|
||||
overload = OVERLOAD_MINUS_ASSIGN;
|
||||
break;
|
||||
case TOKEN_STAR:
|
||||
overload = OVERLOAD_MULTIPLY;
|
||||
break;
|
||||
case TOKEN_MULT_ASSIGN:
|
||||
overload = OVERLOAD_MULTIPLY_ASSIGN;
|
||||
break;
|
||||
case TOKEN_DIV:
|
||||
overload = OVERLOAD_DIVIDE;
|
||||
break;
|
||||
case TOKEN_DIV_ASSIGN:
|
||||
overload = OVERLOAD_DIVIDE_ASSIGN;
|
||||
break;
|
||||
case TOKEN_MOD:
|
||||
overload = OVERLOAD_REMINDER;
|
||||
break;
|
||||
case TOKEN_MOD_ASSIGN:
|
||||
overload = OVERLOAD_REMINDER_ASSIGN;
|
||||
break;
|
||||
case TOKEN_AMP:
|
||||
overload = OVERLOAD_AND;
|
||||
break;
|
||||
case TOKEN_BIT_AND_ASSIGN:
|
||||
overload = OVERLOAD_AND_ASSIGN;
|
||||
break;
|
||||
case TOKEN_BIT_OR:
|
||||
overload = OVERLOAD_OR;
|
||||
break;
|
||||
case TOKEN_BIT_OR_ASSIGN:
|
||||
overload = OVERLOAD_OR_ASSIGN;
|
||||
break;
|
||||
case TOKEN_BIT_XOR:
|
||||
overload = OVERLOAD_XOR;
|
||||
break;
|
||||
case TOKEN_BIT_XOR_ASSIGN:
|
||||
overload = OVERLOAD_XOR_ASSIGN;
|
||||
break;
|
||||
case TOKEN_SHL:
|
||||
overload = OVERLOAD_SHL;
|
||||
break;
|
||||
case TOKEN_SHL_ASSIGN:
|
||||
overload = OVERLOAD_SHL_ASSIGN;
|
||||
break;
|
||||
case TOKEN_SHR:
|
||||
overload = OVERLOAD_SHR;
|
||||
break;
|
||||
case TOKEN_SHR_ASSIGN:
|
||||
overload = OVERLOAD_SHR_ASSIGN;
|
||||
break;
|
||||
case TOKEN_BIT_NOT:
|
||||
overload = OVERLOAD_NEGATE;
|
||||
break;
|
||||
@@ -1081,6 +1111,16 @@ bool parse_attribute(ParseContext *c, Attr **attribute_ref, bool expect_eos)
|
||||
case TOKEN_SHR:
|
||||
case TOKEN_EQEQ:
|
||||
case TOKEN_NOT_EQUAL:
|
||||
case TOKEN_BIT_AND_ASSIGN:
|
||||
case TOKEN_BIT_OR_ASSIGN:
|
||||
case TOKEN_BIT_XOR_ASSIGN:
|
||||
case TOKEN_PLUS_ASSIGN:
|
||||
case TOKEN_MINUS_ASSIGN:
|
||||
case TOKEN_MULT_ASSIGN:
|
||||
case TOKEN_DIV_ASSIGN:
|
||||
case TOKEN_MOD_ASSIGN:
|
||||
case TOKEN_SHL_ASSIGN:
|
||||
case TOKEN_SHR_ASSIGN:
|
||||
if (!next_is_rparen) goto PARSE_EXPR;
|
||||
expr = parse_overload_from_token(c, c->tok);
|
||||
break;
|
||||
|
||||
@@ -1953,7 +1953,7 @@ static inline bool sema_analyse_operator_arithmetics(SemaContext *context, Decl
|
||||
RETURN_SEMA_ERROR(rtype, "The return type was %s, but it must be bool for comparisons.", type_quoted_error_string(rtype->type));
|
||||
}
|
||||
}
|
||||
if (type_is_void(rtype->type))
|
||||
if (operator_overload < OVERLOAD_PLUS_ASSIGN && type_is_void(rtype->type))
|
||||
{
|
||||
RETURN_SEMA_ERROR(rtype, "The return type may not be %s.", type_quoted_error_string(rtype->type));
|
||||
}
|
||||
@@ -2034,6 +2034,16 @@ static bool sema_check_operator_method_validity(SemaContext *context, Decl *meth
|
||||
case OVERLOAD_XOR:
|
||||
case OVERLOAD_SHL:
|
||||
case OVERLOAD_SHR:
|
||||
case OVERLOAD_PLUS_ASSIGN:
|
||||
case OVERLOAD_MINUS_ASSIGN:
|
||||
case OVERLOAD_MULTIPLY_ASSIGN:
|
||||
case OVERLOAD_DIVIDE_ASSIGN:
|
||||
case OVERLOAD_REMINDER_ASSIGN:
|
||||
case OVERLOAD_AND_ASSIGN:
|
||||
case OVERLOAD_OR_ASSIGN:
|
||||
case OVERLOAD_XOR_ASSIGN:
|
||||
case OVERLOAD_SHR_ASSIGN:
|
||||
case OVERLOAD_SHL_ASSIGN:
|
||||
return sema_analyse_operator_arithmetics(context, method, operator);
|
||||
case OVERLOAD_NEGATE:
|
||||
return sema_analyse_operator_unary(context, method, operator);
|
||||
@@ -2117,6 +2127,53 @@ static inline void sema_get_overload_arguments(Decl *method, Type **value_ref, T
|
||||
}
|
||||
}
|
||||
|
||||
static const char *OVERLOAD_NAME[OVERLOADS_COUNT + 1] =
|
||||
{
|
||||
[OVERLOAD_ELEMENT_AT] = "[]",
|
||||
[OVERLOAD_ELEMENT_REF] = "&[]",
|
||||
[OVERLOAD_ELEMENT_SET] = "[]=",
|
||||
[OVERLOAD_LEN] = ".len",
|
||||
[OVERLOAD_NEGATE] = "~",
|
||||
[OVERLOAD_UNARY_MINUS] = "-",
|
||||
[OVERLOAD_PLUS] = "+",
|
||||
[OVERLOAD_MINUS] = "-",
|
||||
[OVERLOAD_MULTIPLY] = "*",
|
||||
[OVERLOAD_DIVIDE] = "/",
|
||||
[OVERLOAD_REMINDER] = "%",
|
||||
[OVERLOAD_AND] = "&",
|
||||
[OVERLOAD_OR] = "|",
|
||||
[OVERLOAD_XOR] = "^",
|
||||
[OVERLOAD_SHL] = "<<",
|
||||
[OVERLOAD_SHR] = ">>",
|
||||
[OVERLOAD_EQUAL] = "==",
|
||||
[OVERLOAD_NOT_EQUAL] = "!=",
|
||||
[OVERLOAD_PLUS_ASSIGN] = "+=",
|
||||
[OVERLOAD_MINUS_ASSIGN] = "-=",
|
||||
[OVERLOAD_MULTIPLY_ASSIGN] = "*=",
|
||||
[OVERLOAD_DIVIDE_ASSIGN] = "/=",
|
||||
[OVERLOAD_REMINDER_ASSIGN] = "%=",
|
||||
[OVERLOAD_AND_ASSIGN] = "&=",
|
||||
[OVERLOAD_OR_ASSIGN] = "|=",
|
||||
[OVERLOAD_XOR_ASSIGN] = "^=",
|
||||
[OVERLOAD_SHL_ASSIGN] = "<<=",
|
||||
[OVERLOAD_SHR_ASSIGN] = ">>=",
|
||||
};
|
||||
|
||||
static bool OVERLOAD_MAY_BE_REVERSE[OVERLOADS_COUNT + 1] =
|
||||
{
|
||||
[OVERLOAD_PLUS] = true,
|
||||
[OVERLOAD_MINUS] = true,
|
||||
[OVERLOAD_MULTIPLY] = true,
|
||||
[OVERLOAD_DIVIDE] = true,
|
||||
[OVERLOAD_REMINDER] = true,
|
||||
[OVERLOAD_AND] = true,
|
||||
[OVERLOAD_OR] = true,
|
||||
[OVERLOAD_XOR] = true,
|
||||
[OVERLOAD_SHL] = true,
|
||||
[OVERLOAD_SHR] = true,
|
||||
[OVERLOAD_EQUAL] = true,
|
||||
[OVERLOAD_NOT_EQUAL] = true,
|
||||
};
|
||||
/**
|
||||
* Do checks on an operator method:
|
||||
*
|
||||
@@ -2138,19 +2195,6 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type
|
||||
{
|
||||
RETURN_SEMA_ERROR(method, "Only user-defined types may have overloads.");
|
||||
}
|
||||
bool is_symmetric = method->func_decl.overload_type == OVERLOAD_TYPE_SYMMETRIC;
|
||||
bool is_reverse = method->func_decl.overload_type == OVERLOAD_TYPE_RIGHT;
|
||||
|
||||
if (!second_param)
|
||||
{
|
||||
if (is_symmetric) RETURN_SEMA_ERROR(method, "Methods with single arguments cannot be have symmetric operators.");
|
||||
if (is_reverse) RETURN_SEMA_ERROR(method, "Methods with single arguments cannot have reverse operators.");
|
||||
}
|
||||
else if (second_param->type_kind == parent_type->type_kind)
|
||||
{
|
||||
if (is_symmetric) RETURN_SEMA_ERROR(method, "Methods with same argument types cannot be have symmetric operators.");
|
||||
if (is_reverse) RETURN_SEMA_ERROR(method, "Methods with the same argument types cannot have reverse operators.");
|
||||
}
|
||||
|
||||
Decl *other = NULL;
|
||||
if (operator >= OVERLOAD_TYPED_START)
|
||||
@@ -2239,6 +2283,16 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type
|
||||
case OVERLOAD_MINUS:
|
||||
case OVERLOAD_EQUAL:
|
||||
case OVERLOAD_NOT_EQUAL:
|
||||
case OVERLOAD_PLUS_ASSIGN:
|
||||
case OVERLOAD_MINUS_ASSIGN:
|
||||
case OVERLOAD_MULTIPLY_ASSIGN:
|
||||
case OVERLOAD_DIVIDE_ASSIGN:
|
||||
case OVERLOAD_REMINDER_ASSIGN:
|
||||
case OVERLOAD_AND_ASSIGN:
|
||||
case OVERLOAD_OR_ASSIGN:
|
||||
case OVERLOAD_XOR_ASSIGN:
|
||||
case OVERLOAD_SHL_ASSIGN:
|
||||
case OVERLOAD_SHR_ASSIGN:
|
||||
return true;
|
||||
default:
|
||||
UNREACHABLE
|
||||
@@ -2951,9 +3005,17 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_
|
||||
decl->func_decl.overload_type = OVERLOAD_TYPE_LEFT;
|
||||
break;
|
||||
case ATTRIBUTE_OPERATOR_R:
|
||||
if (!OVERLOAD_MAY_BE_REVERSE[expr->overload_expr])
|
||||
{
|
||||
RETURN_SEMA_ERROR(attr, "'%s' may not be used with @operator_r(), only @operator().", OVERLOAD_NAME[expr->overload_expr]);
|
||||
}
|
||||
decl->func_decl.overload_type = OVERLOAD_TYPE_RIGHT;
|
||||
break;
|
||||
case ATTRIBUTE_OPERATOR_S:
|
||||
if (!OVERLOAD_MAY_BE_REVERSE[expr->overload_expr])
|
||||
{
|
||||
RETURN_SEMA_ERROR(attr, "'%s' may not be used with @operator_s(), only @operator().", OVERLOAD_NAME[expr->overload_expr]);
|
||||
}
|
||||
decl->func_decl.overload_type = OVERLOAD_TYPE_SYMMETRIC;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -208,6 +208,7 @@ static inline bool sema_analyse_expr_check(SemaContext *context, Expr *expr, Che
|
||||
static inline Expr **sema_prepare_splat_insert(Expr **exprs, unsigned added, unsigned insert_point);
|
||||
static inline bool sema_analyse_maybe_dead_expr(SemaContext *, Expr *expr, bool is_dead, Type *infer_type);
|
||||
static inline bool sema_insert_binary_overload(SemaContext *context, Expr *expr, Decl *overload, Expr *lhs, Expr *rhs, bool reverse);
|
||||
static bool sema_replace_with_overload(SemaContext *context, Expr *expr, Expr *left, Expr *right, Type *left_type, OperatorOverload* operator_overload_ref);
|
||||
|
||||
// -- implementations
|
||||
|
||||
@@ -6212,6 +6213,34 @@ static bool sema_binary_analyse_ct_subscript_op_assign(SemaContext *context, Exp
|
||||
|
||||
}
|
||||
|
||||
static BoolErr sema_insert_overload_in_op_assign_or_error(SemaContext *context, Expr *expr, Expr *left, Expr *right, BinaryOp operator, Type *lhs_type)
|
||||
{
|
||||
if (type_is_user_defined(lhs_type))
|
||||
{
|
||||
if (lhs_type->type_kind == TYPE_BITSTRUCT)
|
||||
{
|
||||
if (operator == BINARYOP_BIT_OR_ASSIGN || operator == BINARYOP_BIT_AND_ASSIGN || operator == BINARYOP_BIT_XOR_ASSIGN) return BOOL_FALSE;
|
||||
}
|
||||
if (!sema_analyse_inferred_expr(context, lhs_type, right)) return BOOL_ERR;
|
||||
static OperatorOverload MAP[BINARYOP_LAST + 1] = {
|
||||
[BINARYOP_ADD_ASSIGN] = OVERLOAD_PLUS_ASSIGN,
|
||||
[BINARYOP_SUB_ASSIGN] = OVERLOAD_MINUS_ASSIGN,
|
||||
[BINARYOP_MULT_ASSIGN] = OVERLOAD_MULTIPLY_ASSIGN,
|
||||
[BINARYOP_DIV_ASSIGN] = OVERLOAD_DIVIDE_ASSIGN,
|
||||
[BINARYOP_MOD_ASSIGN] = OVERLOAD_REMINDER_ASSIGN,
|
||||
[BINARYOP_BIT_XOR_ASSIGN] = OVERLOAD_XOR_ASSIGN,
|
||||
[BINARYOP_BIT_OR_ASSIGN] = OVERLOAD_OR_ASSIGN,
|
||||
[BINARYOP_BIT_AND_ASSIGN] = OVERLOAD_AND_ASSIGN,
|
||||
[BINARYOP_SHL_ASSIGN] = OVERLOAD_SHL_ASSIGN,
|
||||
[BINARYOP_SHR_ASSIGN] = OVERLOAD_SHR_ASSIGN,
|
||||
};
|
||||
OperatorOverload overload = MAP[operator];
|
||||
assert(overload && "Overload not mapped");
|
||||
if (!sema_replace_with_overload(context, expr, left, right, lhs_type, &overload)) return BOOL_ERR;
|
||||
if (!overload) return BOOL_TRUE;
|
||||
}
|
||||
return BOOL_FALSE;
|
||||
}
|
||||
/**
|
||||
* Analyse *= /= %= ^= |= &= += -= <<= >>=
|
||||
*
|
||||
@@ -6274,11 +6303,14 @@ static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr *
|
||||
Type *no_fail = type_no_optional(left->type);
|
||||
Type *flat = type_flatten(no_fail);
|
||||
|
||||
BoolErr b = sema_insert_overload_in_op_assign_or_error(context, expr, left, right, operator, no_fail->canonical);
|
||||
if (b == BOOL_ERR) return false;
|
||||
if (b == BOOL_TRUE) return true;
|
||||
|
||||
// 3. If this is only defined for ints (^= |= &= %=) verify that this is an int.
|
||||
if (int_only && !type_flat_is_intlike(flat))
|
||||
{
|
||||
if (is_bit_op && (flat->type_kind == TYPE_BITSTRUCT || flat == type_bool || type_flat_is_bool_vector(flat))) goto BITSTRUCT_OK;
|
||||
|
||||
RETURN_SEMA_ERROR(left, "Expected an integer here, not a value of type %s.", type_quoted_error_string(left->type));
|
||||
}
|
||||
|
||||
@@ -6421,6 +6453,7 @@ END:
|
||||
// 7. Assign type
|
||||
expr->type = type_add_optional(left->type, optional);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
static bool sema_replace_with_overload(SemaContext *context, Expr *expr, Expr *left, Expr *right, Type *left_type, OperatorOverload* operator_overload_ref)
|
||||
|
||||
@@ -8,6 +8,58 @@ alias List = List{usz};
|
||||
|
||||
alias BitSet = BitSet {2048};
|
||||
|
||||
fn void bit_ops_assign()
|
||||
{
|
||||
BitSet bs;
|
||||
BitSet bs2;
|
||||
bs.set(4);
|
||||
bs.set(6);
|
||||
bs2.set(4);
|
||||
bs2.set(8);
|
||||
BitSet bs3 = bs;
|
||||
bs3 ^= bs2;
|
||||
assert(!bs3.get(4));
|
||||
assert(!bs3.get(5));
|
||||
assert(bs3.get(6));
|
||||
assert(bs3.get(8));
|
||||
BitSet bs4 = bs;
|
||||
bs4 |= bs2;
|
||||
assert(bs4.get(4));
|
||||
assert(!bs4.get(5));
|
||||
assert(bs4.get(6));
|
||||
assert(bs4.get(8));
|
||||
BitSet bs5 = bs;
|
||||
bs5 &= bs2;
|
||||
assert(bs5.get(4));
|
||||
assert(!bs5.get(5));
|
||||
assert(!bs5.get(6));
|
||||
assert(!bs5.get(8));
|
||||
}
|
||||
|
||||
fn void bit_ops()
|
||||
{
|
||||
BitSet bs;
|
||||
BitSet bs2;
|
||||
bs.set(4);
|
||||
bs.set(6);
|
||||
bs2.set(4);
|
||||
bs2.set(8);
|
||||
BitSet bs3 = bs ^ bs2;
|
||||
assert(!bs3.get(4));
|
||||
assert(!bs3.get(5));
|
||||
assert(bs3.get(6));
|
||||
assert(bs3.get(8));
|
||||
BitSet bs4 = bs | bs2;
|
||||
assert(bs4.get(4));
|
||||
assert(!bs4.get(5));
|
||||
assert(bs4.get(6));
|
||||
assert(bs4.get(8));
|
||||
BitSet bs5 = bs & bs2;
|
||||
assert(bs5.get(4));
|
||||
assert(!bs5.get(5));
|
||||
assert(!bs5.get(6));
|
||||
assert(!bs5.get(8));
|
||||
}
|
||||
fn void set_get()
|
||||
{
|
||||
BitSet bs;
|
||||
|
||||
19
test/unit/stdlib/core/slice2d.c3
Normal file
19
test/unit/stdlib/core/slice2d.c3
Normal file
@@ -0,0 +1,19 @@
|
||||
module test_slice @test;
|
||||
|
||||
fn void slice2d()
|
||||
{
|
||||
int[3][2] x = { { 1, 2, 3 }, { 4, 5, 6 }};
|
||||
Slice2d {int} s = array::slice2d(&x);
|
||||
assert(s.len() == 2);
|
||||
assert(s.count() == 6);
|
||||
assert(s.get_xy(1, 1) == 5);
|
||||
assert(s.get_coord({1, 1}) == 5);
|
||||
s.set_coord({ 0, 1 }, 100);
|
||||
assert(x[1][0] == 100);
|
||||
s.set_xy(0, 1, 101);
|
||||
assert(x[1][0] == 101);
|
||||
Slice2d {int} s2 = s.slice(1, 2, 0, 2);
|
||||
assert(s2[0] == { 2, 3 });
|
||||
assert(s2.count() == 4);
|
||||
|
||||
}
|
||||
19
test/unit/stdlib/mem/arena_allocator.c3
Normal file
19
test/unit/stdlib/mem/arena_allocator.c3
Normal file
@@ -0,0 +1,19 @@
|
||||
module allocator_test @test;
|
||||
import std::core::mem;
|
||||
|
||||
|
||||
fn void test_arena_allocator_err()
|
||||
{
|
||||
char[40] data;
|
||||
char[40] empty;
|
||||
ArenaAllocator* foo = allocator::wrap(&data);
|
||||
char* alloc = allocator::malloc(foo, 5);
|
||||
alloc[0] = 3;
|
||||
assert(alloc >= &data[0] && alloc <= &data[^1]);
|
||||
assert(foo.used >= 5);
|
||||
assert(data != empty);
|
||||
test::@error(allocator::malloc_try(foo, 50), mem::INVALID_ALLOC_SIZE);
|
||||
test::@error(allocator::malloc_try(foo, 30), mem::OUT_OF_MEMORY);
|
||||
foo.clear();
|
||||
(void)allocator::malloc(foo, 20);
|
||||
}
|
||||
Reference in New Issue
Block a user