/// 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