/// Slice returns a subsection of an array. Slice :: (view: []$T, start_idx: int, count := -1, loc := #caller_location) -> []T { AssertCallsite(start_idx >= +0 && start_idx < view.count, "incorrect slice bounds"); AssertCallsite(count >= -1 && count < view.count, "incorrect slice length"); if count == -1 { count = view.count - start_idx; } return .{ data = view.data + start_idx, count = count }; } /// Reset sets an array's length to 0, allowing it to be reused /// without allocating new memory. Reset :: (view: *[]$T) { view.count = 0; } /// Clear zeroes the memory of an array and sets its length to 0. /// /// Note: Clear does not free the array's memory. Clear :: (view: *[]$T) { MemZero(view.data, view.count * size_of(T)); view.count = 0; } /// Equal checks the equality of two arrays. Equal :: (lhs: []$T, rhs: []T) -> bool { if lhs.count != rhs.count { return false; } return MemEqual(lhs.data, rhs.data, lhs.count * size_of(T)); } FindFlags :: enum_flags { Last; // Return the last matching element. FromEnd; // Search in reverse. } FindResult :: struct(T: Type) { value: T = ---; index: int; } /// Find searches through an array, returning the first element /// that matches the given value. Find :: (view: []$T, value: T, $flags: FindFlags = 0) -> (bool, FindResult(T)) { found: bool; result: FindResult(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; } /// Contains checks if the given value exists in an array. Contains :: (view: []$T, value: T) -> bool { return Find(view, value); } TrimFlags :: enum_flags { FromStart; // Trim the start of the array. FromEnd; // Trim the end of the array. MatchInFull; // Only trim when the cutset matches exactly. } /// Trim returns a subsection of an array with all leading/trailing values /// from cutset removed. Trim :: (view: []$T, cutset: []T, $flags: TrimFlags = .FromStart) -> []T { result := view; if cutset.count == 0 || cutset.count > view.count { return result; } #if flags & .FromStart { #if flags & .MatchInFull { if Equal(Slice(view, 0, cutset.count), cutset) { result = Slice(view, cutset.count, -1); } } else { while result.count > 0 { if !Contains(cutset, result[0]) { break; } result.data += 1; result.count -= 1; } } } #if flags & .FromEnd { #if flags & .MatchInFull { if Equal(Slice(view, view.count - cutset.count), cutset) { result.count -= cutset.count; } } else { while result.count > 0 { if !Contains(cutset, result[result.count - 1]) { break; } result.count -= 1; } } } return result; } #scope_file #if #exists(RunTests) #run,stallable { Test("slice", t => { a1 := int.[ 1, 2, 3, 4, 5 ]; a2 := Slice(a1, 2); Expect(a2.count == 3); Expect(Equal(a2, int.[ 3, 4, 5 ])); b1 := int.[ 1, 2, 3, 4, 5 ]; b2 := Slice(b1, 2, 0); Expect(b2.count == 0); Expect(b2.data == b1.data + 2); c1 := int.[ 1, 2, 3, 4, 5 ]; c2 := Slice(c1, 3, 1); Expect(c2.count == 1); Expect(Equal(c2, int.[ 4 ])); d1 := int.[ 1, 2, 3 ]; d2 := Slice(d1, 2); Expect(d2.count == 1); Expect(Equal(d2, int.[ 3 ])); }); Test("find", t => { a := int.[ 1, 2, 3, 4, 5 ]; ok, res := Find(a, 3); Expect(ok && res.index == 2); Expect(res.value == 3); ok, res = Find(a, -1); Expect(!ok && res.index == -1); b := int.[ 1, 2, 2, 3, 4, 5, 2 ]; ok, res = Find(b, 2); Expect(ok && res.index == 1); ok, res = Find(b, 2, .FromEnd); Expect(ok && res.index == 6); c := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]; ok, res = Find(c, 0, .Last); Expect(ok && res.index == 8); ok, res = Find(c, 0, .FromEnd | .Last); Expect(ok && res.index == 0); }); Test("contains", t => { a := int.[ 1, 2, 3, 4, 5 ]; Expect(Contains(a, 3)); Expect(!Contains(a, -1)); }); Test("trim", t => { a1 := int.[ 0, 0, 0, 1, 2, 3 ]; a2 := Trim(a1, .[ 0 ]); Expect(Equal(a1, .[ 0, 0, 0, 1, 2, 3 ])); Expect(Equal(a2, .[ 1, 2, 3 ])); b1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]; b2 := Trim(b1, .[ 0 ], .FromEnd); Expect(Equal(b1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ])); Expect(Equal(b2, .[ 0, 0, 0, 1, 2, 3 ])); c1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]; c2 := Trim(c1, .[ 0 ], .FromStart | .FromEnd); Expect(Equal(c1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ])); Expect(Equal(c2, .[ 1, 2, 3 ])); d1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]; d2 := Trim(d1, .[ 0, 0, 0 ], .FromStart | .MatchInFull); d3 := Trim(d1, .[ 0, 0, 0 ], .FromEnd | .MatchInFull); d4 := Trim(d1, .[ 0, 0, 0 ], .FromStart | .FromEnd | .MatchInFull); Expect(Equal(d1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ])); Expect(Equal(d2, .[ 1, 2, 3, 0, 0, 0 ])); Expect(Equal(d3, .[ 0, 0, 0, 1, 2, 3 ])); Expect(Equal(d4, .[ 1, 2, 3 ])); }); }