Skip to content

Commit 13076d5

Browse files
committed
std.mem: add more slice manipulation functions
* add std.mem.trimLeft * add std.mem.trimRight * add std.mem.trimRight * add std.mem.lastIndexOfScalar * add std.mem.lastIndexOfAny * add std.mem.lastIndexOf * add std.mem.endsWith closes #944 Thanks Braedon Wooding for the original PR
1 parent 0501e06 commit 13076d5

File tree

1 file changed

+82
-2
lines changed

1 file changed

+82
-2
lines changed

std/mem.zig

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub const Allocator = struct {
2020
/// * alignment >= alignment of old_mem.ptr
2121
///
2222
/// If `new_byte_count <= old_mem.len`:
23-
/// * this function must return successfully.
23+
/// * this function must return successfully.
2424
/// * alignment <= alignment of old_mem.ptr
2525
///
2626
/// The returned newly allocated memory is undefined.
@@ -174,6 +174,20 @@ pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T {
174174
return new_buf;
175175
}
176176

177+
/// Remove values from the beginning of a slice.
178+
pub fn trimLeft(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
179+
var begin: usize = 0;
180+
while (begin < slice.len and indexOfScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {}
181+
return slice[begin..];
182+
}
183+
184+
/// Remove values from the end of a slice.
185+
pub fn trimRight(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
186+
var end: usize = slice.len;
187+
while (end > 0 and indexOfScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {}
188+
return slice[0..end];
189+
}
190+
177191
/// Remove values from the beginning and end of a slice.
178192
pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
179193
var begin: usize = 0;
@@ -184,6 +198,8 @@ pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []co
184198
}
185199

186200
test "mem.trim" {
201+
assert(eql(u8, trimLeft(u8, " foo\n ", " \n"), "foo\n "));
202+
assert(eql(u8, trimRight(u8, " foo\n ", " \n"), " foo"));
187203
assert(eql(u8, trim(u8, " foo\n ", " \n"), "foo"));
188204
assert(eql(u8, trim(u8, "foo", " \n"), "foo"));
189205
}
@@ -193,6 +209,17 @@ pub fn indexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {
193209
return indexOfScalarPos(T, slice, 0, value);
194210
}
195211

212+
/// Linear search for the last index of a scalar value inside a slice.
213+
pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {
214+
var i: usize = slice.len;
215+
while (i != 0) {
216+
i -= 1;
217+
if (slice[i] == value)
218+
return i;
219+
}
220+
return null;
221+
}
222+
196223
pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize {
197224
var i: usize = start_index;
198225
while (i < slice.len) : (i += 1) {
@@ -206,6 +233,18 @@ pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize
206233
return indexOfAnyPos(T, slice, 0, values);
207234
}
208235

236+
pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize {
237+
var i: usize = slice.len;
238+
while (i != 0) {
239+
i -= 1;
240+
for (values) |value| {
241+
if (slice[i] == value)
242+
return i;
243+
}
244+
}
245+
return null;
246+
}
247+
209248
pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize {
210249
var i: usize = start_index;
211250
while (i < slice.len) : (i += 1) {
@@ -221,6 +260,22 @@ pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize
221260
return indexOfPos(T, haystack, 0, needle);
222261
}
223262

263+
/// Find the index in a slice of a sub-slice, searching from the end backwards.
264+
/// To start looking at a different index, slice the haystack first.
265+
/// TODO is there even a better algorithm for this?
266+
pub fn lastIndexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize {
267+
if (needle.len > haystack.len)
268+
return null;
269+
270+
var i: usize = haystack.len - needle.len;
271+
while (true) : (i -= 1) {
272+
if (mem.eql(T, haystack[i..i+needle.len], needle))
273+
return i;
274+
if (i == 0)
275+
return null;
276+
}
277+
}
278+
224279
// TODO boyer-moore algorithm
225280
pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize {
226281
if (needle.len > haystack.len)
@@ -237,9 +292,19 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee
237292

238293
test "mem.indexOf" {
239294
assert(??indexOf(u8, "one two three four", "four") == 14);
295+
assert(??lastIndexOf(u8, "one two three two four", "two") == 14);
240296
assert(indexOf(u8, "one two three four", "gour") == null);
297+
assert(lastIndexOf(u8, "one two three four", "gour") == null);
241298
assert(??indexOf(u8, "foo", "foo") == 0);
299+
assert(??lastIndexOf(u8, "foo", "foo") == 0);
242300
assert(indexOf(u8, "foo", "fool") == null);
301+
assert(lastIndexOf(u8, "foo", "lfoo") == null);
302+
assert(lastIndexOf(u8, "foo", "fool") == null);
303+
304+
assert(??indexOf(u8, "foo foo", "foo") == 0);
305+
assert(??lastIndexOf(u8, "foo foo", "foo") == 4);
306+
assert(??lastIndexOfAny(u8, "boo, cat", "abo") == 6);
307+
assert(??lastIndexOfScalar(u8, "boo", 'o') == 2);
243308
}
244309

245310
/// Reads an integer from memory with size equal to bytes.len.
@@ -359,9 +424,24 @@ pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool
359424
return if (needle.len > haystack.len) false else eql(T, haystack[0 .. needle.len], needle);
360425
}
361426

427+
test "mem.startsWith" {
428+
assert(startsWith(u8, "Bob", "Bo"));
429+
assert(!startsWith(u8, "Needle in haystack", "haystack"));
430+
}
431+
432+
pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool {
433+
return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle);
434+
}
435+
436+
437+
test "mem.endsWith" {
438+
assert(endsWith(u8, "Needle in haystack", "haystack"));
439+
assert(!endsWith(u8, "Bob", "Bo"));
440+
}
441+
362442
pub const SplitIterator = struct {
363443
buffer: []const u8,
364-
split_bytes: []const u8,
444+
split_bytes: []const u8,
365445
index: usize,
366446

367447
pub fn next(self: &SplitIterator) ?[]const u8 {

0 commit comments

Comments
 (0)