205 lines
5.3 KiB
Text
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; // 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 ]));
|
|
});
|
|
}
|