From ebc3773542ef2bccedd268cfa42ee2cdd529210a Mon Sep 17 00:00:00 2001 From: Kyle Coffey Date: Wed, 10 May 2023 09:47:58 -0500 Subject: [PATCH] Add std.mem.indexOfNone This introduces a parallel set of functions to the std.mem.indexOfAny functions. They simply invert the logic, yielding the first/last index which is *not* contained in the "values" slice. Inverting this logic is useful when you are attempting to determine the initial span which contains only characters in a particular set. * Document the `indexOfNone` family. These descriptions are somewhat brief, but the functions themselves are also simple enough to describe in such a way. --- lib/std/mem.zig | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 6cc60ddad2..aae5c1b617 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1013,6 +1013,54 @@ pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, val return null; } +/// Find the first item in `slice` which is not contained in `values`. +/// +/// Comparable to `strspn` in the C standard library. +pub fn indexOfNone(comptime T: type, slice: []const T, values: []const T) ?usize { + return indexOfNonePos(T, slice, 0, values); +} + +/// Find the last item in `slice` which is not contained in `values`. +/// +/// Like `strspn` in the C standard library, but searches from the end. +pub fn lastIndexOfNone(comptime T: type, slice: []const T, values: []const T) ?usize { + var i: usize = slice.len; + outer: while (i != 0) { + i -= 1; + for (values) |value| { + if (slice[i] == value) continue :outer; + } + return i; + } + return null; +} + +/// Find the first item in `slice[start_index..]` which is not contained in `values`. +/// The returned index will be relative to the start of `slice`, and never less than `start_index`. +/// +/// Comparable to `strspn` in the C standard library. +pub fn indexOfNonePos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize { + var i: usize = start_index; + outer: while (i < slice.len) : (i += 1) { + for (values) |value| { + if (slice[i] == value) continue :outer; + } + return i; + } + return null; +} + +test "indexOfNone" { + try testing.expect(indexOfNone(u8, "abc123", "123").? == 0); + try testing.expect(lastIndexOfNone(u8, "abc123", "123").? == 2); + try testing.expect(indexOfNone(u8, "123abc", "123").? == 3); + try testing.expect(lastIndexOfNone(u8, "123abc", "123").? == 5); + try testing.expect(indexOfNone(u8, "123123", "123") == null); + try testing.expect(indexOfNone(u8, "333333", "123") == null); + + try testing.expect(indexOfNonePos(u8, "abc123", 3, "321") == null); +} + pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize { return indexOfPos(T, haystack, 0, needle); }