mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Add array::zip and Related Macros (#2370)
* zip / zip_into * Deprecate `add_array` in favour of `push_all` on lists. * Add support for generic lists for zip. --------- Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
This commit is contained in:
@@ -121,7 +121,24 @@ fn usz ElasticArray.add_all_to_limit(&self, ElasticArray* other_list)
|
|||||||
|
|
||||||
@param [in] array
|
@param [in] array
|
||||||
*>
|
*>
|
||||||
fn usz ElasticArray.add_array_to_limit(&self, Type[] array)
|
fn usz ElasticArray.add_array_to_limit(&self, Type[] array) @deprecated("Use push_all_to_limit")
|
||||||
|
{
|
||||||
|
if (!array.len) return 0;
|
||||||
|
foreach (i, &value : array)
|
||||||
|
{
|
||||||
|
if (self.size == MAX_SIZE) return array.len - i;
|
||||||
|
self.entries[self.size++] = *value;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
<*
|
||||||
|
Add as many values from this array as possible, returning the
|
||||||
|
number of elements that didn't fit.
|
||||||
|
|
||||||
|
@param [in] array
|
||||||
|
*>
|
||||||
|
fn usz ElasticArray.push_all_to_limit(&self, Type[] array)
|
||||||
{
|
{
|
||||||
if (!array.len) return 0;
|
if (!array.len) return 0;
|
||||||
foreach (i, &value : array)
|
foreach (i, &value : array)
|
||||||
@@ -139,7 +156,7 @@ fn usz ElasticArray.add_array_to_limit(&self, Type[] array)
|
|||||||
@require array.len + self.size <= MAX_SIZE : `Size would exceed max.`
|
@require array.len + self.size <= MAX_SIZE : `Size would exceed max.`
|
||||||
@ensure self.size >= array.len
|
@ensure self.size >= array.len
|
||||||
*>
|
*>
|
||||||
fn void ElasticArray.add_array(&self, Type[] array)
|
fn void ElasticArray.add_array(&self, Type[] array) @deprecated("Use push_all")
|
||||||
{
|
{
|
||||||
if (!array.len) return;
|
if (!array.len) return;
|
||||||
foreach (&value : array)
|
foreach (&value : array)
|
||||||
@@ -148,6 +165,21 @@ fn void ElasticArray.add_array(&self, Type[] array)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<*
|
||||||
|
Add the values of an array to this list.
|
||||||
|
|
||||||
|
@param [in] array
|
||||||
|
@require array.len + self.size <= MAX_SIZE : `Size would exceed max.`
|
||||||
|
@ensure self.size >= array.len
|
||||||
|
*>
|
||||||
|
fn void ElasticArray.push_all(&self, Type[] array)
|
||||||
|
{
|
||||||
|
if (!array.len) return;
|
||||||
|
foreach (&value : array)
|
||||||
|
{
|
||||||
|
self.entries[self.size++] = *value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
<*
|
<*
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ fn List* List.tinit(&self, usz initial_capacity = 16)
|
|||||||
fn List* List.init_with_array(&self, Allocator allocator, Type[] values)
|
fn List* List.init_with_array(&self, Allocator allocator, Type[] values)
|
||||||
{
|
{
|
||||||
self.init(allocator, values.len) @inline;
|
self.init(allocator, values.len) @inline;
|
||||||
self.add_array(values) @inline;
|
self.push_all(values) @inline;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ fn List* List.init_with_array(&self, Allocator allocator, Type[] values)
|
|||||||
fn List* List.tinit_with_array(&self, Type[] values)
|
fn List* List.tinit_with_array(&self, Type[] values)
|
||||||
{
|
{
|
||||||
self.tinit(values.len) @inline;
|
self.tinit(values.len) @inline;
|
||||||
self.add_array(values) @inline;
|
self.push_all(values) @inline;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +199,21 @@ fn Type[] List.array_view(&self)
|
|||||||
@param [in] array
|
@param [in] array
|
||||||
@ensure self.size >= array.len
|
@ensure self.size >= array.len
|
||||||
*>
|
*>
|
||||||
fn void List.add_array(&self, Type[] array)
|
fn void List.add_array(&self, Type[] array) @deprecated("Use push_all")
|
||||||
|
{
|
||||||
|
if (!array.len) return;
|
||||||
|
self.reserve(array.len);
|
||||||
|
usz index = self.set_size(self.size + array.len);
|
||||||
|
self.entries[index : array.len] = array[..];
|
||||||
|
}
|
||||||
|
|
||||||
|
<*
|
||||||
|
Add the values of an array to this list.
|
||||||
|
|
||||||
|
@param [in] array
|
||||||
|
@ensure self.size >= array.len
|
||||||
|
*>
|
||||||
|
fn void List.push_all(&self, Type[] array)
|
||||||
{
|
{
|
||||||
if (!array.len) return;
|
if (!array.len) return;
|
||||||
self.reserve(array.len);
|
self.reserve(array.len);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
module std::core::array;
|
module std::core::array;
|
||||||
|
|
||||||
import std::core::array::slice;
|
import std::core::array::slice;
|
||||||
|
import std::collections::pair;
|
||||||
|
|
||||||
<*
|
<*
|
||||||
Returns true if the array contains at least one element, else false
|
Returns true if the array contains at least one element, else false
|
||||||
@@ -18,6 +20,7 @@ macro bool contains(array, element)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
<*
|
<*
|
||||||
Return the first index of element found in the array, searching from the start.
|
Return the first index of element found in the array, searching from the start.
|
||||||
|
|
||||||
@@ -35,6 +38,7 @@ macro index_of(array, element)
|
|||||||
return NOT_FOUND?;
|
return NOT_FOUND?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
<*
|
<*
|
||||||
Slice a 2d array and create a Slice2d from it.
|
Slice a 2d array and create a Slice2d from it.
|
||||||
|
|
||||||
@@ -74,6 +78,7 @@ macro rindex_of(array, element)
|
|||||||
return NOT_FOUND?;
|
return NOT_FOUND?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
<*
|
<*
|
||||||
Concatenate two arrays or slices, returning a slice containing the concatenation of them.
|
Concatenate two arrays or slices, returning a slice containing the concatenation of them.
|
||||||
|
|
||||||
@@ -99,6 +104,7 @@ macro concat(Allocator allocator, arr1, arr2) @nodiscard
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
<*
|
<*
|
||||||
Concatenate two arrays or slices, returning a slice containing the concatenation of them,
|
Concatenate two arrays or slices, returning a slice containing the concatenation of them,
|
||||||
allocated using the temp allocator.
|
allocated using the temp allocator.
|
||||||
@@ -110,4 +116,225 @@ macro concat(Allocator allocator, arr1, arr2) @nodiscard
|
|||||||
@require @typeis(arr1[0], $typeof(arr2[0])) : "Arrays must have the same type"
|
@require @typeis(arr1[0], $typeof(arr2[0])) : "Arrays must have the same type"
|
||||||
@ensure return.len == arr1.len + arr2.len
|
@ensure return.len == arr1.len + arr2.len
|
||||||
*>
|
*>
|
||||||
macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);
|
macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);
|
||||||
|
|
||||||
|
|
||||||
|
<*
|
||||||
|
Zip together two separate arrays/slices into a single array of Pairs or return values. Values will
|
||||||
|
be collected up to the length of the shorter array if `fill_with` is left undefined; otherwise, they
|
||||||
|
will be collected up to the length of the LONGER array, with missing values in the shorter array being
|
||||||
|
assigned to the value of `fill_with`. Return array elements do not have to be of the same type.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
```c3
|
||||||
|
uint[] chosen_session_ids = server::get_random_sessions(instance)[:128];
|
||||||
|
String[200] refreshed_session_keys = prng::new_keys_batch();
|
||||||
|
|
||||||
|
Pair { uint, String }[] sessions_meta = array::zip(mem, chosen_session_ids, refreshed_session_keys);
|
||||||
|
// The resulting Pair{}[] slice is then length of the shortest of the two arrays, so 128.
|
||||||
|
|
||||||
|
foreach (i, &sess : sessions:meta) {
|
||||||
|
// distribute new session keys to associated instance IDs
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or:
|
||||||
|
```c3
|
||||||
|
String[] client_names = server::online_usernames(instance);
|
||||||
|
uint128[] session_ids = server::user_keys();
|
||||||
|
|
||||||
|
// in this example, we 'know' ahead of time that 'session_ids' can only ever be SHORTER
|
||||||
|
// than 'client_names', but never longer, because it's possible new users have logged
|
||||||
|
// in without getting whatever this 'session ID' is delegated to them.
|
||||||
|
Pair { String, uint128 }[] zipped = array::tzip(client_names, session_ids, fill_with: uint128.max);
|
||||||
|
|
||||||
|
server::refresh_session_keys_by_pair(zipped)!;
|
||||||
|
```
|
||||||
|
|
||||||
|
### When an `operation` is supplied...
|
||||||
|
Apply an operation to each element of two slices or arrays and return the results of
|
||||||
|
each operation into a newly allocated array.
|
||||||
|
|
||||||
|
This essentially combines Iterable1 with Iterable2 using the `operation` functor.
|
||||||
|
|
||||||
|
See the functional `zipWith` construct, which has a more appropriate name than, e.g., `map`;
|
||||||
|
a la: https://hackage.haskell.org/package/base-4.21.0.0/docs/Prelude.html#v:zipWith
|
||||||
|
|
||||||
|
Similar to "normal" `zip`, this macro pads the shorter input array with a given `fill_with`, or
|
||||||
|
an empty value if one isn't supplied. This `fill_with` is supplied to the `operation` functor
|
||||||
|
_BEFORE_ calculating its result while zipping.
|
||||||
|
|
||||||
|
For example: a functor of `fn char (char a, char b) => a + b` with a `fill_with` of 7,
|
||||||
|
where the `left` array is the shorter iterable, will put 7 into that lambda in each place
|
||||||
|
where `left` is being filled in during the zip operation.
|
||||||
|
|
||||||
|
@param [&inout] allocator : "The allocator to use; default is the heap allocator."
|
||||||
|
@param [in] left : "The left-side array. These items will be placed as the First in each Pair"
|
||||||
|
@param [in] right : "The right-side array. These items will be placed as the Second in each Pair"
|
||||||
|
@param #operation : "The function to apply. Must have a signature of `$typeof(a) (a, b)`, where the type of 'a' and 'b' is the element type of left/right respectively."
|
||||||
|
@param fill_with : "The value used to fill or pad the shorter iterable to the length of the longer one while zipping."
|
||||||
|
|
||||||
|
@require @is_valid_list(left) &&& @is_valid_list(right) : "Left and right sides must be integer indexable"
|
||||||
|
@require @is_valid_operation(#operation, left, right) : "The operator must take two parameters matching the elements of the left and right side"
|
||||||
|
@require @is_valid_fill(left, right, fill_with) : "The specified fill value does not match either the left or the right array's underlying type."
|
||||||
|
|
||||||
|
*>
|
||||||
|
macro @zip(Allocator allocator, left, right, #operation = EMPTY_MACRO_SLOT, fill_with = EMPTY_MACRO_SLOT) @nodiscard
|
||||||
|
{
|
||||||
|
var $LeftType = $typeof(left[0]);
|
||||||
|
var $RightType = $typeof(right[0]);
|
||||||
|
|
||||||
|
var $Type = Pair { $LeftType, $RightType };
|
||||||
|
bool $is_op = @is_valid_macro_slot(#operation);
|
||||||
|
$if $is_op:
|
||||||
|
$Type = $typeof(#operation).returns;
|
||||||
|
$endif
|
||||||
|
|
||||||
|
|
||||||
|
usz left_len = find_len(left);
|
||||||
|
usz right_len = find_len(right);
|
||||||
|
|
||||||
|
$LeftType left_fill;
|
||||||
|
$RightType right_fill;
|
||||||
|
usz result_len = min(left_len, right_len);
|
||||||
|
bool $has_fill = @is_valid_macro_slot(fill_with);
|
||||||
|
$if $has_fill:
|
||||||
|
switch
|
||||||
|
{
|
||||||
|
case left_len > right_len:
|
||||||
|
$if !$defined(($RightType)fill_with):
|
||||||
|
unreachable();
|
||||||
|
$else
|
||||||
|
right_fill = ($RightType)fill_with;
|
||||||
|
result_len = left_len;
|
||||||
|
$endif
|
||||||
|
case left_len < right_len:
|
||||||
|
$if !$defined(($LeftType)fill_with):
|
||||||
|
unreachable();
|
||||||
|
$else
|
||||||
|
left_fill = ($LeftType)fill_with;
|
||||||
|
result_len = right_len;
|
||||||
|
$endif
|
||||||
|
}
|
||||||
|
$endif
|
||||||
|
|
||||||
|
if (result_len == 0) return ($Type[]){};
|
||||||
|
|
||||||
|
$Type[] result = allocator::alloc_array(allocator, $Type, result_len);
|
||||||
|
|
||||||
|
foreach (idx, &item : result)
|
||||||
|
{
|
||||||
|
$if $is_op:
|
||||||
|
var $LambdaType = $typeof(fn $Type ($LeftType a, $RightType b) => ($Type){});
|
||||||
|
$LambdaType $operation = ($LambdaType)#operation;
|
||||||
|
$LeftType lval = idx >= left_len ? left_fill : left[idx];
|
||||||
|
$RightType rval = idx >= right_len ? right_fill : right[idx];
|
||||||
|
*item = $operation(lval, rval);
|
||||||
|
$else
|
||||||
|
*item = {
|
||||||
|
idx >= left_len ? left_fill : left[idx],
|
||||||
|
idx >= right_len ? right_fill : right[idx]
|
||||||
|
};
|
||||||
|
$endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
<*
|
||||||
|
Array 'zip' using the temp allocator.
|
||||||
|
|
||||||
|
@param [in] left : "The left-side array. These items will be placed as the First in each Pair"
|
||||||
|
@param [in] right : "The right-side array. These items will be placed as the Second in each Pair"
|
||||||
|
@param #operation : "The function to apply. Must have a signature of `$typeof(a) (a, b)`, where the type of 'a' and 'b' is the element type of left/right respectively."
|
||||||
|
@param fill_with : "The value used to fill or pad the shorter iterable to the length of the longer one while zipping."
|
||||||
|
|
||||||
|
@require @is_valid_list(left) &&& @is_valid_list(right) : "Left and right sides must be integer indexable"
|
||||||
|
@require @is_valid_operation(#operation, left, right) : "The operator must take two parameters matching the elements of the left and right side"
|
||||||
|
@require @is_valid_fill(left, right, fill_with) : "The specified fill value does not match either the left or the right array's underlying type."
|
||||||
|
|
||||||
|
*>
|
||||||
|
macro @tzip(left, right, #operation = EMPTY_MACRO_SLOT, fill_with = EMPTY_MACRO_SLOT) @nodiscard
|
||||||
|
{
|
||||||
|
return @zip(tmem, left, right, #operation, fill_with);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
<*
|
||||||
|
Apply an operation to each element of two slices or arrays and store the results of
|
||||||
|
each operation into the 'left' value.
|
||||||
|
|
||||||
|
This is useful because no memory allocations are required in order to perform the operation.
|
||||||
|
|
||||||
|
A good example of using this might be using algorithmic transformations on data in-place:
|
||||||
|
```
|
||||||
|
char[] partial_cipher = get_next_plaintext_block();
|
||||||
|
|
||||||
|
array::@zip_into(
|
||||||
|
partial_cipher[ENCRYPT_OFFSET:BASE_KEY.len],
|
||||||
|
BASE_KEY,
|
||||||
|
fn char (char a, char b) => a ^ (b * 5) % 37
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
This parameterizes the lambda function with left (`partial_cipher`) and right (`BASE_KEY`) slice
|
||||||
|
elements and stores the end result in-place within the left slice. This is in contrast to a
|
||||||
|
regular `zip_with` which will create a cloned final result and return it.
|
||||||
|
|
||||||
|
@param [inout] left : `Slice to store results of applied functor/operation.`
|
||||||
|
@param [in] right : `Slice to apply in the functor/operation.`
|
||||||
|
@param #operation : "The function to apply. Must have a signature of `$typeof(a) (a, b)`, where the type of 'a' and 'b' is the element type of left/right respectively."
|
||||||
|
|
||||||
|
@require @is_valid_list(left) : "Expected a valid list"
|
||||||
|
@require @is_valid_list(right) : "Expected a valid list"
|
||||||
|
@require find_len(right) >= find_len(left) : `Right side length must be >= the destination (left) side length; consider using a sub-array of data for the assignment.`
|
||||||
|
@require @assignable_to(#operation, $typefrom(@zip_into_fn(left, right))) : "The functor must use the same types as the `left` and `right` inputs, and return a value of the `left` type."
|
||||||
|
*>
|
||||||
|
macro @zip_into(left, right, #operation)
|
||||||
|
{
|
||||||
|
$typefrom(@zip_into_fn(left, right)) $operation = #operation;
|
||||||
|
|
||||||
|
foreach (i, &v : left) *v = $operation(left[i], right[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- helper functions
|
||||||
|
|
||||||
|
macro typeid @zip_into_fn(#left, #right) @private @const
|
||||||
|
{
|
||||||
|
return @typeid(fn $typeof(#left[0]) ($typeof(#left[0]) l, $typeof(#right[0]) r) => l);
|
||||||
|
}
|
||||||
|
macro bool @is_valid_operation(#operation, #left, #right) @const @private
|
||||||
|
{
|
||||||
|
$switch:
|
||||||
|
$case @is_empty_macro_slot(#operation):
|
||||||
|
return true;
|
||||||
|
$case @typekind(#operation) != FUNC:
|
||||||
|
return false;
|
||||||
|
$default:
|
||||||
|
return $defined(#operation(#left[0], #right[0]));
|
||||||
|
$endswitch
|
||||||
|
}
|
||||||
|
|
||||||
|
macro bool @is_valid_list(#expr) @const @private
|
||||||
|
{
|
||||||
|
return $defined(#expr[0]) &&& ($defined(#expr.len) ||| $defined(#expr.len()));
|
||||||
|
}
|
||||||
|
|
||||||
|
macro bool @is_valid_fill(left, right, fill_with) @private
|
||||||
|
{
|
||||||
|
if (@is_empty_macro_slot(fill_with)) return true;
|
||||||
|
usz left_len = @select($defined(left.len()), left.len(), left.len);
|
||||||
|
usz right_len = @select($defined(right.len()), right.len(), right.len);
|
||||||
|
if (left_len == right_len) return true;
|
||||||
|
return left_len > right_len ? $defined(($typeof(right[0]))fill_with) : $defined(($typeof(left[0]))fill_with);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro usz find_len(list)
|
||||||
|
{
|
||||||
|
$if $defined(list.len()):
|
||||||
|
return list.len();
|
||||||
|
$else
|
||||||
|
return list.len;
|
||||||
|
$endif
|
||||||
|
}
|
||||||
@@ -684,6 +684,12 @@ macro @clone(value) @builtin @nodiscard
|
|||||||
return allocator::clone(mem, value);
|
return allocator::clone(mem, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<*
|
||||||
|
@param value : "The value to clone"
|
||||||
|
@return "A pointer to the cloned value"
|
||||||
|
*>
|
||||||
|
macro @clone_slice(value) @builtin @nodiscard => allocator::clone_slice(mem, value);
|
||||||
|
|
||||||
<*
|
<*
|
||||||
@param value : "The value to clone"
|
@param value : "The value to clone"
|
||||||
@return "A pointer to the cloned value, which must be released using free_aligned"
|
@return "A pointer to the cloned value, which must be released using free_aligned"
|
||||||
@@ -706,6 +712,12 @@ macro @tclone(value) @builtin @nodiscard
|
|||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<*
|
||||||
|
@param value : "The value to clone"
|
||||||
|
@return "A pointer to the cloned value"
|
||||||
|
*>
|
||||||
|
macro @tclone_slice(value) @builtin @nodiscard => allocator::clone_slice(tmem, value);
|
||||||
|
|
||||||
fn void* malloc(usz size) @builtin @inline @nodiscard
|
fn void* malloc(usz size) @builtin @inline @nodiscard
|
||||||
{
|
{
|
||||||
return allocator::malloc(mem, size);
|
return allocator::malloc(mem, size);
|
||||||
|
|||||||
@@ -317,6 +317,23 @@ macro clone(Allocator allocator, value) @nodiscard
|
|||||||
return new(allocator, $typeof(value), value);
|
return new(allocator, $typeof(value), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<*
|
||||||
|
@param [&inout] allocator : "The allocator used to clone"
|
||||||
|
@param slice : "The slice to clone"
|
||||||
|
@return "A pointer to the cloned slice"
|
||||||
|
|
||||||
|
@require @typekind(slice) == SLICE || @typekind(slice) == ARRAY
|
||||||
|
*>
|
||||||
|
macro clone_slice(Allocator allocator, slice) @nodiscard
|
||||||
|
{
|
||||||
|
var $Type = $typeof(slice[0]);
|
||||||
|
|
||||||
|
$Type[] new_arr = new_array(allocator, $Type, slice.len);
|
||||||
|
mem::copy(new_arr.ptr, &slice[0], slice.len * $Type.sizeof);
|
||||||
|
|
||||||
|
return new_arr;
|
||||||
|
}
|
||||||
|
|
||||||
<*
|
<*
|
||||||
Clone overaligned values. Must be released using free_aligned.
|
Clone overaligned values. Must be released using free_aligned.
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ macro @select(bool $bool, #value_1, #value_2) @builtin
|
|||||||
return #value_2;
|
return #value_2;
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
macro promote_int_same(x, y)
|
macro promote_int_same(x, y)
|
||||||
{
|
{
|
||||||
$if @is_int(x):
|
$if @is_int(x):
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
- Switch available for types implementing `@operator(==)`.
|
- Switch available for types implementing `@operator(==)`.
|
||||||
- `Type.is_eq` is now true for types with `==` overload.
|
- `Type.is_eq` is now true for types with `==` overload.
|
||||||
- Methods ignore visibility settings.
|
- Methods ignore visibility settings.
|
||||||
|
- Allow inout etc on untyped macro parameters even if they are not pointers.
|
||||||
|
- Deprecate `add_array` in favour of `push_all` on lists.
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
- List.remove_at would incorrectly trigger ASAN.
|
- List.remove_at would incorrectly trigger ASAN.
|
||||||
@@ -49,6 +51,7 @@
|
|||||||
- Updated hash functions in default hash methods.
|
- Updated hash functions in default hash methods.
|
||||||
- Added `FixedBlockPool` which is a memory pool for fixed size blocks.
|
- Added `FixedBlockPool` which is a memory pool for fixed size blocks.
|
||||||
- Added the experimental `std::core::log` for logging.
|
- Added the experimental `std::core::log` for logging.
|
||||||
|
- Added array `@zip` and `@zip_into` macros. #2370
|
||||||
- Updated termios bindings to use bitstructs and fixed some constants with incorrect values #2372
|
- Updated termios bindings to use bitstructs and fixed some constants with incorrect values #2372
|
||||||
- Added libloaderapi to `std::os::win32`.
|
- Added libloaderapi to `std::os::win32`.
|
||||||
|
|
||||||
|
|||||||
@@ -2578,7 +2578,6 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
|
|||||||
goto SKIP_LINK;
|
goto SKIP_LINK;
|
||||||
}
|
}
|
||||||
Decl *func = context->call_env.current_function;
|
Decl *func = context->call_env.current_function;
|
||||||
ASSERT_SPAN(func, func);
|
|
||||||
ASSERT_SPAN(func, func->resolved_attributes);
|
ASSERT_SPAN(func, func->resolved_attributes);
|
||||||
if (!func->attrs_resolved)
|
if (!func->attrs_resolved)
|
||||||
{
|
{
|
||||||
@@ -2773,7 +2772,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
|
|||||||
if (param->var.init_expr)
|
if (param->var.init_expr)
|
||||||
{
|
{
|
||||||
Type *param_type = param->type;
|
Type *param_type = param->type;
|
||||||
if (param_type && (param->var.out_param || param->var.not_null))
|
if (param_type && param->var.type_info && (param->var.out_param || param->var.not_null))
|
||||||
{
|
{
|
||||||
param_type = type_flatten(param_type);
|
param_type = type_flatten(param_type);
|
||||||
if (param_type->type_kind != TYPE_POINTER && param_type->type_kind != TYPE_SLICE && param_type->type_kind != TYPE_INTERFACE && param_type->type_kind != TYPE_ANY)
|
if (param_type->type_kind != TYPE_POINTER && param_type->type_kind != TYPE_SLICE && param_type->type_kind != TYPE_INTERFACE && param_type->type_kind != TYPE_ANY)
|
||||||
@@ -2782,7 +2781,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
|
|||||||
goto EXIT_FAIL;
|
goto EXIT_FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (param->var.not_null)
|
if (param->var.not_null && (param_type->type_kind == TYPE_POINTER || param_type->type_kind == TYPE_SLICE || param_type->type_kind == TYPE_INTERFACE || param_type->type_kind == TYPE_ANY))
|
||||||
{
|
{
|
||||||
Expr *expr = expr_variable(param);
|
Expr *expr = expr_variable(param);
|
||||||
Expr *binary = expr_new_expr(EXPR_BINARY, expr);
|
Expr *binary = expr_new_expr(EXPR_BINARY, expr);
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ macro foo(x) => *x = 1;
|
|||||||
|
|
||||||
fn int main()
|
fn int main()
|
||||||
{
|
{
|
||||||
foo(0); // #error: Expected a pointer
|
foo(0); // #error: x must be
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ alias PtrList = ElasticArray{void*, 10};
|
|||||||
fn void delete_contains_index()
|
fn void delete_contains_index()
|
||||||
{
|
{
|
||||||
IntList test;
|
IntList test;
|
||||||
test.add_array({ 1, 2 });
|
test.push_all({ 1, 2 });
|
||||||
assert(test.contains(1));
|
assert(test.contains(1));
|
||||||
assert(test.contains(2));
|
assert(test.contains(2));
|
||||||
assert(!test.contains(0));
|
assert(!test.contains(0));
|
||||||
@@ -36,7 +36,7 @@ fn void delete_contains_index()
|
|||||||
fn void compact()
|
fn void compact()
|
||||||
{
|
{
|
||||||
PtrList test;
|
PtrList test;
|
||||||
test.add_array({ null, &test });
|
test.push_all({ null, &test });
|
||||||
assert(test.compact_count() == 1);
|
assert(test.compact_count() == 1);
|
||||||
test.push(null);
|
test.push(null);
|
||||||
assert(test.compact_count() == 1);
|
assert(test.compact_count() == 1);
|
||||||
@@ -50,7 +50,7 @@ fn void reverse()
|
|||||||
{
|
{
|
||||||
IntList test;
|
IntList test;
|
||||||
test.reverse();
|
test.reverse();
|
||||||
test.add_array({ 1, 2 });
|
test.push_all({ 1, 2 });
|
||||||
test.push(3);
|
test.push(3);
|
||||||
assert(test.array_view() == (int[]) { 1, 2, 3});
|
assert(test.array_view() == (int[]) { 1, 2, 3});
|
||||||
test.reverse();
|
test.reverse();
|
||||||
@@ -66,13 +66,13 @@ fn void remove_if()
|
|||||||
IntList test;
|
IntList test;
|
||||||
usz removed;
|
usz removed;
|
||||||
|
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.remove_if(&filter);
|
removed = test.remove_if(&filter);
|
||||||
assert(removed == 3);
|
assert(removed == 3);
|
||||||
assert(test.array_view() == (int[]){1, 2});
|
assert(test.array_view() == (int[]){1, 2});
|
||||||
|
|
||||||
test.clear();
|
test.clear();
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.remove_if(&select);
|
removed = test.remove_if(&select);
|
||||||
assert(removed == 2);
|
assert(removed == 2);
|
||||||
assert(test.array_view() == (int[]){11, 10, 20});
|
assert(test.array_view() == (int[]){11, 10, 20});
|
||||||
@@ -84,13 +84,13 @@ fn void remove_using_test()
|
|||||||
IntList test;
|
IntList test;
|
||||||
usz removed;
|
usz removed;
|
||||||
|
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.remove_using_test(fn bool(i, ctx) => *i >= *(int*)ctx, &&10);
|
removed = test.remove_using_test(fn bool(i, ctx) => *i >= *(int*)ctx, &&10);
|
||||||
assert(removed == 3);
|
assert(removed == 3);
|
||||||
assert(test.array_view() == (int[]){1, 2});
|
assert(test.array_view() == (int[]){1, 2});
|
||||||
|
|
||||||
test.clear();
|
test.clear();
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.remove_using_test(fn bool(i, ctx) => *i < *(int*)ctx, &&10);
|
removed = test.remove_using_test(fn bool(i, ctx) => *i < *(int*)ctx, &&10);
|
||||||
assert(removed == 2);
|
assert(removed == 2);
|
||||||
assert(test.array_view() == (int[]){11, 10, 20});
|
assert(test.array_view() == (int[]){11, 10, 20});
|
||||||
@@ -101,13 +101,13 @@ fn void retain_if()
|
|||||||
IntList test;
|
IntList test;
|
||||||
usz removed;
|
usz removed;
|
||||||
|
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.retain_if(&select);
|
removed = test.retain_if(&select);
|
||||||
assert(removed == 3);
|
assert(removed == 3);
|
||||||
assert(test.array_view() == (int[]){1, 2});
|
assert(test.array_view() == (int[]){1, 2});
|
||||||
|
|
||||||
test.clear();
|
test.clear();
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.retain_if(&filter);
|
removed = test.retain_if(&filter);
|
||||||
assert(removed == 2);
|
assert(removed == 2);
|
||||||
assert(test.array_view() == (int[]){11, 10, 20});
|
assert(test.array_view() == (int[]){11, 10, 20});
|
||||||
@@ -118,13 +118,13 @@ fn void retain_using_test()
|
|||||||
IntList test;
|
IntList test;
|
||||||
usz removed;
|
usz removed;
|
||||||
|
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.remove_using_test(fn bool(i, ctx) => *i >= *(int*)ctx, &&10);
|
removed = test.remove_using_test(fn bool(i, ctx) => *i >= *(int*)ctx, &&10);
|
||||||
assert(removed == 3);
|
assert(removed == 3);
|
||||||
assert(test.array_view() == (int[]){1, 2});
|
assert(test.array_view() == (int[]){1, 2});
|
||||||
|
|
||||||
test.clear();
|
test.clear();
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.remove_using_test(fn bool(i, ctx) => *i < *(int*)ctx, &&10);
|
removed = test.remove_using_test(fn bool(i, ctx) => *i < *(int*)ctx, &&10);
|
||||||
assert(removed == 2);
|
assert(removed == 2);
|
||||||
assert(test.array_view() == (int[]){11, 10, 20});
|
assert(test.array_view() == (int[]){11, 10, 20});
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ fn void remove_at()
|
|||||||
IntList test;
|
IntList test;
|
||||||
test.init(mem);
|
test.init(mem);
|
||||||
defer test.free();
|
defer test.free();
|
||||||
test.add_array({ 1, 2, 3, 4 });
|
test.push_all({ 1, 2, 3, 4 });
|
||||||
test::eq(test.array_view(), (int[]){ 1, 2, 3, 4 });
|
test::eq(test.array_view(), (int[]){ 1, 2, 3, 4 });
|
||||||
test.remove_at(0);
|
test.remove_at(0);
|
||||||
test::eq(test.array_view(), (int[]){ 2, 3, 4 });
|
test::eq(test.array_view(), (int[]){ 2, 3, 4 });
|
||||||
@@ -41,7 +41,7 @@ fn void delete_contains_index()
|
|||||||
{
|
{
|
||||||
IntList test;
|
IntList test;
|
||||||
|
|
||||||
test.add_array({ 1, 2 });
|
test.push_all({ 1, 2 });
|
||||||
assert(test.contains(1));
|
assert(test.contains(1));
|
||||||
assert(test.contains(2));
|
assert(test.contains(2));
|
||||||
assert(!test.contains(0));
|
assert(!test.contains(0));
|
||||||
@@ -70,7 +70,7 @@ fn void delete_contains_index()
|
|||||||
fn void compact()
|
fn void compact()
|
||||||
{
|
{
|
||||||
PtrList test;
|
PtrList test;
|
||||||
test.add_array({ null, &test });
|
test.push_all({ null, &test });
|
||||||
assert(test.compact_count() == 1);
|
assert(test.compact_count() == 1);
|
||||||
test.push(null);
|
test.push(null);
|
||||||
assert(test.compact_count() == 1);
|
assert(test.compact_count() == 1);
|
||||||
@@ -85,7 +85,7 @@ fn void reverse()
|
|||||||
IntList test;
|
IntList test;
|
||||||
|
|
||||||
test.reverse();
|
test.reverse();
|
||||||
test.add_array({ 1, 2 });
|
test.push_all({ 1, 2 });
|
||||||
test.push(3);
|
test.push(3);
|
||||||
assert(test.array_view() == { 1, 2, 3});
|
assert(test.array_view() == { 1, 2, 3});
|
||||||
test.reverse();
|
test.reverse();
|
||||||
@@ -101,13 +101,13 @@ fn void remove_if()
|
|||||||
IntList test;
|
IntList test;
|
||||||
usz removed;
|
usz removed;
|
||||||
|
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.remove_if(&filter);
|
removed = test.remove_if(&filter);
|
||||||
assert(removed == 3);
|
assert(removed == 3);
|
||||||
assert(test.array_view() == {1, 2});
|
assert(test.array_view() == {1, 2});
|
||||||
|
|
||||||
test.clear();
|
test.clear();
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.remove_if(&select);
|
removed = test.remove_if(&select);
|
||||||
assert(removed == 2);
|
assert(removed == 2);
|
||||||
assert(test.array_view() == {11, 10, 20});
|
assert(test.array_view() == {11, 10, 20});
|
||||||
@@ -135,13 +135,13 @@ fn void remove_using_test()
|
|||||||
IntList test;
|
IntList test;
|
||||||
usz removed;
|
usz removed;
|
||||||
|
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.remove_using_test(fn bool(i, ctx) => *i >= *(int*)ctx, &&10);
|
removed = test.remove_using_test(fn bool(i, ctx) => *i >= *(int*)ctx, &&10);
|
||||||
assert(removed == 3);
|
assert(removed == 3);
|
||||||
assert(test.array_view() == {1, 2});
|
assert(test.array_view() == {1, 2});
|
||||||
|
|
||||||
test.clear();
|
test.clear();
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.remove_using_test(fn bool(i, ctx) => *i < *(int*)ctx, &&10);
|
removed = test.remove_using_test(fn bool(i, ctx) => *i < *(int*)ctx, &&10);
|
||||||
assert(removed == 2);
|
assert(removed == 2);
|
||||||
assert(test.array_view() == {11, 10, 20});
|
assert(test.array_view() == {11, 10, 20});
|
||||||
@@ -152,13 +152,13 @@ fn void retain_if()
|
|||||||
IntList test;
|
IntList test;
|
||||||
usz removed;
|
usz removed;
|
||||||
|
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.retain_if(&select);
|
removed = test.retain_if(&select);
|
||||||
assert(removed == 3);
|
assert(removed == 3);
|
||||||
assert(test.array_view() == {1, 2});
|
assert(test.array_view() == {1, 2});
|
||||||
|
|
||||||
test.clear();
|
test.clear();
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.retain_if(&filter);
|
removed = test.retain_if(&filter);
|
||||||
assert(removed == 2);
|
assert(removed == 2);
|
||||||
assert(test.array_view() == {11, 10, 20});
|
assert(test.array_view() == {11, 10, 20});
|
||||||
@@ -169,13 +169,13 @@ fn void retain_using_test()
|
|||||||
IntList test;
|
IntList test;
|
||||||
usz removed;
|
usz removed;
|
||||||
|
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.remove_using_test(fn bool(i, ctx) => *i >= *(int*)ctx, &&10);
|
removed = test.remove_using_test(fn bool(i, ctx) => *i >= *(int*)ctx, &&10);
|
||||||
assert(removed == 3);
|
assert(removed == 3);
|
||||||
assert(test.array_view() == {1, 2});
|
assert(test.array_view() == {1, 2});
|
||||||
|
|
||||||
test.clear();
|
test.clear();
|
||||||
test.add_array({ 1, 11, 2, 10, 20 });
|
test.push_all({ 1, 11, 2, 10, 20 });
|
||||||
removed = test.remove_using_test(fn bool(i, ctx) => *i < *(int*)ctx, &&10);
|
removed = test.remove_using_test(fn bool(i, ctx) => *i < *(int*)ctx, &&10);
|
||||||
assert(removed == 2);
|
assert(removed == 2);
|
||||||
assert(test.array_view() == {11, 10, 20});
|
assert(test.array_view() == {11, 10, 20});
|
||||||
|
|||||||
@@ -1,19 +1,47 @@
|
|||||||
module arraytests @test;
|
module array_test;
|
||||||
|
import std::io;
|
||||||
|
|
||||||
|
struct TestStructZip (Printable)
|
||||||
|
{
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn TestStructZip TestStructZip.mult(self, TestStructZip other) @operator(*)
|
||||||
|
{
|
||||||
|
self.a *= other.a;
|
||||||
|
self.b *= other.b;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool TestStructZip.eq(self, TestStructZip other) @operator(==)
|
||||||
|
{
|
||||||
|
return self.a == other.a && self.b == other.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usz? TestStructZip.to_format(&self, Formatter* f) @dynamic
|
||||||
|
{
|
||||||
|
return f.printf("{ %d, %d }", self.a, self.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
module array_test @test;
|
||||||
|
import std::collections::pair, std::collections::list;
|
||||||
|
|
||||||
|
|
||||||
fn void contains()
|
fn void contains()
|
||||||
{
|
{
|
||||||
int[3] a = { 1, 2, 3 };
|
int[3] a = { 1, 2, 3 };
|
||||||
assert(array::contains(a, 2) == true);
|
assert(array::contains(a, 2));
|
||||||
assert(array::contains(a, 15) == false);
|
assert(!array::contains(a, 15));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void find()
|
fn void find()
|
||||||
{
|
{
|
||||||
int[3] a = { 1, 2, 3 };
|
int[3] a = { 1, 2, 3 };
|
||||||
assert(array::index_of(a, 2)!! == 1);
|
test::eq(array::index_of(a, 2)!!, 1);
|
||||||
assert(array::index_of(a, 1)!! == 0);
|
test::eq(array::index_of(a, 1)!!, 0);
|
||||||
assert(array::index_of(a, 3)!! == 2);
|
test::eq(array::index_of(a, 3)!!, 2);
|
||||||
assert(@catch(array::index_of(a, 4)) == NOT_FOUND);
|
test::@error(array::index_of(a, 4), NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void find_subarray()
|
fn void find_subarray()
|
||||||
@@ -35,5 +63,194 @@ fn void concat()
|
|||||||
free(array::concat(mem, a[:0], (int[2]) { 1, 2 }));
|
free(array::concat(mem, a[:0], (int[2]) { 1, 2 }));
|
||||||
int[] c = array::concat(mem, a[1..2], a);
|
int[] c = array::concat(mem, a[1..2], a);
|
||||||
defer free(c);
|
defer free(c);
|
||||||
assert (c == (int[]){ 2, 3, 1, 2, 3 });
|
assert(c == (int[]){ 2, 3, 1, 2, 3 });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn void zip() => @pool()
|
||||||
|
{
|
||||||
|
char[] left = "abcde";
|
||||||
|
long[] right = { -1, 0x8000, 0 };
|
||||||
|
|
||||||
|
Pair{char, long}[] expected = { {'a', -1}, {'b', 0x8000}, {'c', 0} };
|
||||||
|
|
||||||
|
Pair{char, long}[] zipped = array::@tzip(left, right);
|
||||||
|
|
||||||
|
test::eq(zipped.len, 3);
|
||||||
|
foreach (i, c : zipped) assert(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void zip_list() => @pool()
|
||||||
|
{
|
||||||
|
char[] left = "abcde";
|
||||||
|
List{long} l;
|
||||||
|
l.push(-1);
|
||||||
|
l.push(0x8000);
|
||||||
|
l.push(0);
|
||||||
|
|
||||||
|
Pair{char, long}[] expected = { {'a', -1}, {'b', 0x8000}, {'c', 0} };
|
||||||
|
|
||||||
|
Pair{char, long}[] zipped = array::@tzip(left, l);
|
||||||
|
|
||||||
|
test::eq(zipped.len, 3);
|
||||||
|
foreach (i, c : zipped) assert(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void zip_fill_with_default() => @pool()
|
||||||
|
{
|
||||||
|
char[] left = "abcde";
|
||||||
|
long[] right = { -1, 0x8000, 0 };
|
||||||
|
|
||||||
|
Pair{char, long}[] expected = { {'a', -1}, {'b', 0x8000}, {'c', 0}, {'d', 0}, {'e', 0} };
|
||||||
|
|
||||||
|
Pair{char, long}[] zipped = array::@tzip(left, right, fill_with: 0);
|
||||||
|
|
||||||
|
test::eq(zipped.len, 5);
|
||||||
|
foreach (i, c : zipped) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void zip_fill_with_char() => @pool()
|
||||||
|
{
|
||||||
|
char[] left = "abcde";
|
||||||
|
long[] right = { -1, 0x8000, 0 };
|
||||||
|
|
||||||
|
Pair{char, long}[] expected = { {'a', -1}, {'b', 0x8000}, {'c', 0}, {'d', 0x40}, {'e', 0x40} };
|
||||||
|
|
||||||
|
Pair{char, long}[] zipped = array::@tzip(left, right, fill_with: 0x40);
|
||||||
|
|
||||||
|
test::eq(zipped.len, 5);
|
||||||
|
foreach (i, c : zipped) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void zip_fill_with_string() => @pool()
|
||||||
|
{
|
||||||
|
String[] left = { "abcde", "123456" };
|
||||||
|
long[] right = { -1, 0x8000, 20, 30, 40 };
|
||||||
|
|
||||||
|
Pair{String, long}[] expected = { {"abcde", -1}, {"123456", 0x8000}, {"aaa", 20}, {"aaa", 30}, {"aaa", 40} };
|
||||||
|
|
||||||
|
Pair{String, long}[] zipped = array::@tzip(left, right, fill_with: "aaa");
|
||||||
|
|
||||||
|
test::eq(zipped.len, 5);
|
||||||
|
foreach (i, c : zipped) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void zip_fill_with_struct() => @pool()
|
||||||
|
{
|
||||||
|
String[] left = { "abcde", "123456", "zzz" };
|
||||||
|
TestStructZip[] right = { {1, 2} };
|
||||||
|
|
||||||
|
Pair{String, TestStructZip}[] expected = { {"abcde", {1, 2}}, {"123456", {100, 200}}, {"zzz", {100, 200}} };
|
||||||
|
|
||||||
|
Pair{String, TestStructZip}[] zipped = array::@tzip(left, right, fill_with: (TestStructZip){100, 200});
|
||||||
|
|
||||||
|
test::eq(zipped.len, 3);
|
||||||
|
foreach (i, c : zipped) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void zip_with() => @pool()
|
||||||
|
{
|
||||||
|
char[] left = "abcde";
|
||||||
|
char[4] right = { 0x05, 0x04, 0x03, 0x00 };
|
||||||
|
|
||||||
|
char[] expected = "fffd";
|
||||||
|
|
||||||
|
char[] zipped = array::@tzip(left, right, fn char (char a, char b) => a + b);
|
||||||
|
|
||||||
|
test::eq(zipped.len, 4);
|
||||||
|
foreach (i, c : zipped) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void zip_with_fill_with_default() => @pool()
|
||||||
|
{
|
||||||
|
char[] left = "abcde";
|
||||||
|
char[] right = { 0x05, 0x04 };
|
||||||
|
|
||||||
|
char[] expected = "ffcde";
|
||||||
|
|
||||||
|
char[] zipped = array::@tzip(left, right, fn char (char a, char b) => a + b, 0);
|
||||||
|
|
||||||
|
test::eq(zipped.len, 5);
|
||||||
|
foreach (i, c : zipped) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void zip_with_fill_with_char() => @pool()
|
||||||
|
{
|
||||||
|
char[] left = "abcde";
|
||||||
|
char[] right = { 0x05, 0x04 };
|
||||||
|
|
||||||
|
char[] expected = "ffghi";
|
||||||
|
|
||||||
|
char[] zipped = array::@tzip(left, right, fn char (char a, char b) => a + b, 0x04);
|
||||||
|
|
||||||
|
test::eq(zipped.len, 5);
|
||||||
|
foreach (i, c : zipped) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void zip_with_fill_with_pointers() => @pool()
|
||||||
|
{
|
||||||
|
ZString field = "0123456789abcdefghijklmnopqrstuvwxyz-_=!";
|
||||||
|
|
||||||
|
char*[] left = { &field[3], &field[1] };
|
||||||
|
char[] right = { 0x05, 0x04, 0x0A, 0x10, 0x11 };
|
||||||
|
|
||||||
|
char[] expected = "85agh";
|
||||||
|
|
||||||
|
char[] zipped = array::@tzip(left, right, fn char (char* a, char b) => a[b], &field[0]);
|
||||||
|
|
||||||
|
test::eq(zipped.len, 5);
|
||||||
|
foreach (i, c : zipped) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void zip_with_fill_with_string() => @pool()
|
||||||
|
{
|
||||||
|
String[] left = { "Hello", "World", "Foo", "Bazzy" };
|
||||||
|
String[] right = { " there", "!" };
|
||||||
|
|
||||||
|
String[] expected = { "Hello there", "World!", "FooBar", "BazzyBar" };
|
||||||
|
|
||||||
|
String[] zipped = array::@tzip(left, right, fn String (String a, String b) => a.tconcat(b), "Bar");
|
||||||
|
|
||||||
|
test::eq(zipped.len, 4);
|
||||||
|
foreach (i, c : zipped) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void zip_with_fill_with_struct() => @pool()
|
||||||
|
{
|
||||||
|
TestStructZip[] left = { {1, 2}, {300, 400} };
|
||||||
|
TestStructZip[] right = { {-1, -1} };
|
||||||
|
|
||||||
|
TestStructZip[] expected = { {-1, -2}, {600, 1200} };
|
||||||
|
|
||||||
|
TestStructZip[] zipped = array::@tzip(left, right, fn TestStructZip (TestStructZip a, TestStructZip b) => a * b, (TestStructZip){2, 3});
|
||||||
|
|
||||||
|
test::eq(zipped.len, 2);
|
||||||
|
foreach (i, c : zipped) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void zip_into()
|
||||||
|
{
|
||||||
|
char[] left = { '1', '2', '3', '4' };
|
||||||
|
String[6] right = { "one", "two", "three", "four", "five", "six" };
|
||||||
|
|
||||||
|
char[] expected = { '4', '5', '8', '8' };
|
||||||
|
|
||||||
|
array::@zip_into(left, right, fn (a, b) => a + (char)b.len);
|
||||||
|
|
||||||
|
test::eq(left.len, 4);
|
||||||
|
foreach (i, c : left) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void zip_into_list() => @pool()
|
||||||
|
{
|
||||||
|
List{char} l;
|
||||||
|
l.push_all({ '1', '2', '3', '4' });
|
||||||
|
String[6] right = { "one", "two", "three", "four", "five", "six" };
|
||||||
|
|
||||||
|
char[] expected = { '4', '5', '8', '8' };
|
||||||
|
|
||||||
|
array::@zip_into(l, right, fn (a, b) => a + (char)b.len);
|
||||||
|
|
||||||
|
test::eq(l.len(), 4);
|
||||||
|
foreach (i, c : l) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ alias CountingSortTestList = List{int};
|
|||||||
fn void countingsort_list()
|
fn void countingsort_list()
|
||||||
{
|
{
|
||||||
CountingSortTestList list;
|
CountingSortTestList list;
|
||||||
list.add_array({ 2, 1, 3});
|
list.push_all({ 2, 1, 3});
|
||||||
sort::countingsort(list, &sort::key_int_value);
|
sort::countingsort(list, &sort::key_int_value);
|
||||||
assert(check::int_ascending_sort(list.array_view()));
|
assert(check::int_ascending_sort(list.array_view()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ alias InsertionSortTestList = List{int};
|
|||||||
fn void insertionsort_list()
|
fn void insertionsort_list()
|
||||||
{
|
{
|
||||||
InsertionSortTestList list;
|
InsertionSortTestList list;
|
||||||
list.add_array({ 2, 1, 3});
|
list.push_all({ 2, 1, 3});
|
||||||
sort::insertionsort(list, &sort::cmp_int_value);
|
sort::insertionsort(list, &sort::cmp_int_value);
|
||||||
assert(check::int_ascending_sort(list.array_view()));
|
assert(check::int_ascending_sort(list.array_view()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ alias List = List{int};
|
|||||||
fn void quicksort_list()
|
fn void quicksort_list()
|
||||||
{
|
{
|
||||||
List list;
|
List list;
|
||||||
list.add_array({ 2, 1, 3});
|
list.push_all({ 2, 1, 3});
|
||||||
sort::quicksort(list, &sort::cmp_int_value);
|
sort::quicksort(list, &sort::cmp_int_value);
|
||||||
assert(check::int_sort(list.array_view()));
|
assert(check::int_sort(list.array_view()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ fn void sorted()
|
|||||||
// with list
|
// with list
|
||||||
List{int} list;
|
List{int} list;
|
||||||
list.tinit();
|
list.tinit();
|
||||||
list.add_array(tc.input);
|
list.push_all(tc.input);
|
||||||
|
|
||||||
got = is_sorted(list);
|
got = is_sorted(list);
|
||||||
assert(got == tc.want, "list: %s, got: %s, want: %s",
|
assert(got == tc.want, "list: %s, got: %s, want: %s",
|
||||||
|
|||||||
Reference in New Issue
Block a user