jc/internal/array.jai
2025-09-03 20:27:41 -06:00

205 lines
5.3 KiB
Text

/// 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; // The last matching element should be returned.
FromEnd; // The search be done 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; // The start of the array should be trimmed.
FromEnd; // The end of the array should be trimmed.
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 ]));
});
}