jc/+internal/arrays.jai
2025-09-07 15:56:45 -06:00

163 lines
4.6 KiB
Text

/// ArrayAppend pushes values to the end of an array, resizing if necessary.
/// A pointer to the first value appended will be returned.
///
/// Note: If no allocator has been set, ArrayAppend will use the current context allocator.
/// Note: Calls to Append may invalidate pre-existing pointers.
ArrayAppend :: inline (arr: *[..]$T, values: ..T) -> *T {
TrySetAllocator(arr,, allocator = ArenaToAllocator(*context.arena));
if values.count == 0 {
return basic.array_add(arr);
}
count := arr.count;
basic.array_add(arr, ..values);
return *arr.data[count];
}
/// ArrayGrow resizes the memory associated with an array to hold new_count values.
///
/// Note: If the array has enough allocated memory to accomodate new_count, ArrayGrow does nothing.
/// Note: ArrayGrow does not guarantee pointer stability.
ArrayGrow :: inline (arr: *[..]$T, new_count: int) {
if new_count <= arr.allocated
{ return; }
TrySetAllocator(arr,, allocator = ArenaToAllocator(*context.arena));
basic.array_reserve(arr, new_count,, allocator = arr.allocator);
}
/// ArraySlice returns a subsection of an array.
ArraySlice :: (arr: []$T, start_idx: int, count := -1, loc := #caller_location) -> []T {
AssertCallsite(start_idx >= +0 && start_idx < arr.count, "jc: incorrect slice bounds");
AssertCallsite(count >= -1 && count < arr.count, "jc: incorrect slice length");
if count == -1
{ count = arr.count - start_idx; }
return .{ data = arr.data + start_idx, count = count };
}
/// ArrayReset sets an array's length to 0, but leaves its memory intact.
/// Note: To reset the associated memory as well, see ArrayClear.
ArrayReset :: (arr: *[]$T) {
arr.count = 0;
}
/// ArrayClear zeroes an array's memory and sets its length to 0.
/// Note: To leave the associated memory intact, see ArrayReset.
ArrayClear :: (arr: *[]$T) {
MemZero(arr.data, arr.count * size_of(T));
arr.count = 0;
}
/// ArrayEquals checks equality between two arrays.
ArrayEquals :: (lhs: []$T, rhs: []T) -> bool {
if lhs.count != rhs.count
{ return false; }
return MemEqual(lhs.data, rhs.data, lhs.count * size_of(T));
}
ArrayIndex :: struct(T: Type) {
value: T = ---;
index: int;
}
ArrayFindFlags :: enum_flags {
Last; // Return the last matching element.
FromEnd; // Search in reverse.
}
/// ArrayFind searches through an array, returning the first element that matches the given value.
ArrayFind :: (view: []$T, value: T, $flags: ArrayFindFlags = 0) -> (bool, ArrayIndex(T)) {
found: bool;
result: ArrayIndex(T);
result.index = -1;
REVERSE :: #run (flags & .FromEnd).(bool);
for #v2 <=REVERSE view if it == value {
found = true;
result.index = it_index;
result.value = it;
#if !(flags & .Last) break;
}
return found, result;
}
/// ArrayContains checks if the given value exists in an array.
ArrayContains :: (view: []$T, value: T) -> bool {
return ArrayFind(view, value);
}
ArrayTrimFlags :: enum_flags {
FromStart; // ArrayTrim the start of the array.
FromEnd; // ArrayTrim the end of the array.
MatchInFull; // Only trim when the cutset matches exactly.
}
/// ArrayTrim returns a subsection of an array with all leading/trailing values
/// from cutset removed.
ArrayTrim :: (view: []$T, cutset: []T, $flags: ArrayTrimFlags = .FromStart) -> []T {
result := view;
if cutset.count == 0 || cutset.count > view.count {
return result;
}
#if flags & .FromStart {
#if flags & .MatchInFull {
if ArrayEquals(ArraySlice(view, 0, cutset.count), cutset) {
result = ArraySlice(view, cutset.count, -1);
}
}
else {
while result.count > 0 {
if !ArrayContains(cutset, result[0]) {
break;
}
result.data += 1;
result.count -= 1;
}
}
}
#if flags & .FromEnd {
#if flags & .MatchInFull {
if ArrayEquals(ArraySlice(view, view.count - cutset.count), cutset) {
result.count -= cutset.count;
}
}
else {
while result.count > 0 {
if !ArrayContains(cutset, result[result.count - 1]) {
break;
}
result.count -= 1;
}
}
}
return result;
}
CheckBounds :: ($$index: $T, $$count: T, loc := #caller_location) #expand {
Message :: "bounds check failed!";
#if is_constant(index) && is_constant(count) {
if index < 0 || index >= count {
CompileError(Message, loc = loc);
}
}
else if index < 0 || index > count {
Panic(Message, loc = loc);
}
}
#scope_file
basic :: #import "Basic"; // @future