From 688cb55c2bafd107b22b7eb2b4bed26dc40a198b Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Sat, 8 Feb 2025 15:54:40 +0800 Subject: [PATCH] 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) --- src/browser/mime.zig | 12 +++--- src/str/parser.zig | 91 ++++++++++++++++++-------------------------- src/url/query.zig | 4 +- 3 files changed, 45 insertions(+), 62 deletions(-) diff --git a/src/browser/mime.zig b/src/browser/mime.zig index 509040e7..da8ac710 100644 --- a/src/browser/mime.zig +++ b/src/browser/mime.zig @@ -19,9 +19,7 @@ const std = @import("std"); const testing = std.testing; -const strparser = @import("../str/parser.zig"); -const Reader = strparser.Reader; -const trim = strparser.trim; +const Reader = @import("../str/parser.zig").Reader; const Self = @This(); @@ -70,7 +68,7 @@ pub fn parse(s: []const u8) Self.MimeError!Self { if (ln > 255) return MimeError.TooBig; var res = Self{ .mtype = "", .msubtype = "" }; - var r = Reader{ .s = s }; + var r = Reader{ .data = s }; res.mtype = trim(r.until('/')); if (res.mtype.len == 0) return MimeError.Invalid; @@ -87,7 +85,7 @@ pub fn parse(s: []const u8) Self.MimeError!Self { // parse well known parameters. // don't check invalid parameter format. - var rp = Reader{ .s = res.params }; + var rp = Reader{ .data = res.params }; while (true) { const name = trim(rp.until('=')); if (!rp.skip()) return res; @@ -106,6 +104,10 @@ pub fn parse(s: []const u8) Self.MimeError!Self { return res; } +fn trim(s: []const u8) []const u8 { + return std.mem.trim(u8, s, &std.ascii.whitespace); +} + test "parse valid" { for ([_][]const u8{ "text/html", diff --git a/src/str/parser.zig b/src/str/parser.zig index 903ea0d9..f663c4d5 100644 --- a/src/str/parser.zig +++ b/src/str/parser.zig @@ -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()); } diff --git a/src/url/query.zig b/src/url/query.zig index 674e1777..3b1f877f 100644 --- a/src/url/query.zig +++ b/src/url/query.zig @@ -199,12 +199,12 @@ pub fn parseQuery(alloc: std.mem.Allocator, s: []const u8) !Values { const ln = s.len; if (ln == 0) return values; - var r = Reader{ .s = s }; + var r = Reader{ .data = s }; while (true) { const param = r.until('&'); if (param.len == 0) break; - var rr = Reader{ .s = param }; + var rr = Reader{ .data = param }; const k = rr.until('='); if (k.len == 0) continue;