diff --git a/src/core/io.zig b/src/core/io.zig index ad814dc..5102493 100644 --- a/src/core/io.zig +++ b/src/core/io.zig @@ -32,26 +32,6 @@ pub fn Spliterator(comptime Element: type) type { return (self.source.len != 0); } - test "Check has data" { - var empty_spliterator = Spliterator(u8){ - .source = "", - .delimiter = "/", - }; - - try testing.expect(!empty_spliterator.hasNext()); - - var stateful_spliterator = Spliterator(u8){ - .source = "data", - .delimiter = "/", - }; - - try testing.expect(stateful_spliterator.hasNext()); - - _ = try stateful_spliterator.next(); - - try testing.expect(!stateful_spliterator.hasNext()); - } - /// /// Iterates on `self` and returns the next view of [Spliterator.source] that matches /// [Spliterator.delimiter], or `null` if there is no more data to be processed. @@ -59,81 +39,99 @@ pub fn Spliterator(comptime Element: type) type { pub fn next(self: *Self) ?[]const Element { if (!self.hasNext()) return null; + if (self.delimiter.len == 0) { + defer self.source = self.source[self.source.len .. self.source.len]; + + return self.source[0 .. self.source.len]; + } + if (findFirstOf(Element, self.source, self.delimiter, struct { fn testEquality(this: Element, that: Element) bool { return this == that; } }.testEquality)) |head| { - const tail = (head + self.delimiter.len); + defer self.source = self.source[(head + self.delimiter.len) .. self.source.len]; - defer self.source = self.source[tail .. self.source.len]; - - return self.source[0 .. (tail - 1)]; + return self.source[0 .. head]; } defer self.source = self.source[self.source.len .. self.source.len]; return self.source; } - - test "Iterate through data" { - // Single-character delimiter. - { - var spliterator = Spliterator(u8){ - .source = "single.character.separated.hello.world", - .delimiter = ".", - }; - - const components = [_][]const u8{"single", - "character", "separated", "hello", "world"}; - - var index = @as(usize, 0); - - while (spliterator.next()) |split| : (index += 1) { - try testing.expect(equals(u8, split, components[index])); - } - } - - // Multi-character delimiter. - { - var spliterator = Spliterator(u8){ - .source = "finding a needle in a needle stack", - .delimiter = "needle", - }; - - const components = [_][]const u8{"finding a ", " in a ", " stack"}; - var index = @as(usize, 0); - - while (spliterator.next()) |split| : (index += 1) { - try testing.expect(equals(u8, split, components[index])); - } - } - } }; } +test "Spliterator of string literals" { + // Empty source. + { + var spliterator = Spliterator(u8){ + .source = "", + .delimiter = " ", + }; + + try testing.expect(!spliterator.hasNext()); + } + + // Empty delimiter. + { + var spliterator = Spliterator(u8){ + .source = "aaa", + .delimiter = "", + }; + + try testing.expect(spliterator.hasNext()); + try testing.expect(equals(u8, spliterator.next().?, "aaa")); + try testing.expect(!spliterator.hasNext()); + } + + // Single-character delimiter. + { + var spliterator = Spliterator(u8){ + .source = "single.character.separated.hello.world", + .delimiter = ".", + }; + + const components = [_][]const u8{"single", + "character", "separated", "hello", "world"}; + + var index = @as(usize, 0); + const components_tail = components.len - 1; + + while (spliterator.next()) |split| : (index += 1) { + try testing.expect(spliterator.hasNext() == (index < components_tail)); + try testing.expect(equals(u8, split, components[index])); + } + + try testing.expect(!spliterator.hasNext()); + } + + // Multi-character delimiter. + { + var spliterator = Spliterator(u8){ + .source = "finding a needle in a needle stack", + .delimiter = "needle", + }; + + const components = [_][]const u8{"finding a ", " in a ", " stack"}; + var index = @as(usize, 0); + const components_tail = components.len - 1; + + while (spliterator.next()) |split| : (index += 1) { + try testing.expect(spliterator.hasNext() == (index < components_tail)); + try testing.expect(equals(u8, split, components[index])); + } + + try testing.expect(!spliterator.hasNext()); + } +} + /// /// Closure that captures a reference to writable resources like block devices, memory buffers, /// network sockets, and more. /// pub const Writer = meta.Function(@sizeOf(usize), []const u8, usize); -/// -/// Returns `true` if `elements` starts with the characters in `with`, otherwise `false`. -/// -pub fn begins(comptime Element: type, elements: []const Element, with: []const Element) bool { - if (elements.len < with.len) return false; - - return equals(Element, elements[0 .. with.len], with); -} - -test "Check memory begins with" { - const bytes_sequence = &.{69, 42}; - - try testing.expect(begins(u8, &.{69, 42, 0, 89}, bytes_sequence)); - try testing.expect(!begins(u8, &.{69, 89, 42, 0}, bytes_sequence)); -} - /// /// Returns a sliced reference of the raw bytes in `pointer`. /// @@ -260,9 +258,8 @@ pub fn findFirstOf(comptime Element: type, haystack: []const Element, const tail = (haystack.len - needle.len); walk_haystack: while (head <= tail) : (head += 1) { - for (needle) |element, index| { + for (needle) |element, index| if (!testEquality(haystack[head + index], element)) continue: walk_haystack; - } return head; } @@ -271,7 +268,16 @@ pub fn findFirstOf(comptime Element: type, haystack: []const Element, } test "Find first of sequence" { + const haystack = &.{"foo", "bar", "baz"}; + const testEquality = struct { + fn testEquality(this: []const u8, that: []const u8) bool { + return equals(u8, this, that); + } + }.testEquality; + + try testing.expect(findFirstOf([]const u8, haystack, &.{"bar", "baz"}, testEquality).? == 1); + try testing.expect(findFirstOf([]const u8, haystack, &.{"baz", "bar"}, testEquality) == null); } ///