Minor Reader tweaks

1- Remove `parser.trim`, it was only being used in 1 place. All other places
   are using `std.mem.trim(u8, X, &std.ascii.whitespace)`, so i updated MIME to
   use this as well

2- Use slightly more meaningful field name, i => pos, s = data

3- Leverage std.mem.indexOfScalarPos which can be more efficient for longer
   inputs (since it leverages SIMD)
This commit is contained in:
Karl Seguin
2025-02-08 15:54:40 +08:00
parent 1594f148f8
commit 688cb55c2b
3 changed files with 45 additions and 62 deletions

View File

@@ -18,89 +18,70 @@
// some utils to parser strings.
const std = @import("std");
const testing = std.testing;
pub const Reader = struct {
s: []const u8,
i: usize = 0,
pos: usize = 0,
data: []const u8,
pub fn until(self: *Reader, c: u8) []const u8 {
const ln = self.s.len;
const start = self.i;
while (self.i < ln) {
if (c == self.s[self.i]) return self.s[start..self.i];
self.i += 1;
}
const pos = self.pos;
const data = self.data;
return self.s[start..self.i];
const index = std.mem.indexOfScalarPos(u8, data, pos, c) orelse data.len;
self.pos = index;
return data[pos..index];
}
pub fn tail(self: *Reader) []const u8 {
if (self.i > self.s.len) return "";
defer self.i = self.s.len;
return self.s[self.i..];
const pos = self.pos;
const data = self.data;
if (pos > data.len) {
return "";
}
self.pos = data.len;
return data[pos..];
}
pub fn skip(self: *Reader) bool {
if (self.i >= self.s.len) return false;
self.i += 1;
const pos = self.pos;
if (pos >= self.data.len) {
return false;
}
self.pos = pos + 1;
return true;
}
};
test "Reader.skip" {
var r = Reader{ .s = "foo" };
try testing.expect(r.skip());
try testing.expect(r.skip());
try testing.expect(r.skip());
try testing.expect(!r.skip());
try testing.expect(!r.skip());
const testing = std.testing;
test "parser.Reader: skip" {
var r = Reader{ .data = "foo" };
try testing.expectEqual(true, r.skip());
try testing.expectEqual(true, r.skip());
try testing.expectEqual(true, r.skip());
try testing.expectEqual(false, r.skip());
try testing.expectEqual(false, r.skip());
}
test "Reader.tail" {
var r = Reader{ .s = "foo" };
test "parser.Reader: tail" {
var r = Reader{ .data = "foo" };
try testing.expectEqualStrings("foo", r.tail());
try testing.expectEqualStrings("", r.tail());
try testing.expectEqualStrings("", r.tail());
}
test "Reader.until" {
var r = Reader{ .s = "foo.bar.baz" };
test "parser.Reader: until" {
var r = Reader{ .data = "foo.bar.baz" };
try testing.expectEqualStrings("foo", r.until('.'));
_ = r.skip();
try testing.expectEqualStrings("bar", r.until('.'));
_ = r.skip();
try testing.expectEqualStrings("baz", r.until('.'));
r = Reader{ .s = "foo" };
r = Reader{ .data = "foo" };
try testing.expectEqualStrings("foo", r.until('.'));
try testing.expectEqualStrings("", r.tail());
r = Reader{ .s = "" };
r = Reader{ .data = "" };
try testing.expectEqualStrings("", r.until('.'));
}
pub fn trim(s: []const u8) []const u8 {
const ln = s.len;
if (ln == 0) {
return "";
}
var start: usize = 0;
while (start < ln) {
if (!std.ascii.isWhitespace(s[start])) break;
start += 1;
}
var end: usize = ln;
while (end > 0) {
if (!std.ascii.isWhitespace(s[end - 1])) break;
end -= 1;
}
return s[start..end];
}
test "trim" {
try testing.expectEqualStrings("", trim(""));
try testing.expectEqualStrings("foo", trim("foo"));
try testing.expectEqualStrings("foo", trim(" \n\tfoo"));
try testing.expectEqualStrings("foo", trim("foo \n\t"));
try testing.expectEqualStrings("", r.tail());
}