authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-01-22 03:25:48 -08:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-01-22 03:25:48 -08:00
loge5ac23bf48c1d0ef54b95490723a0419b68abc6c
tree440df1b6aa6016af90f34d6d03cc00afdc57c6f5
parent96facb375b873ab50b97dc82176df879bdb97cb0

populate protocol field


3 files changed, 66 insertions(+), 31 deletions(-)

generate.ts+1-1
......@@ -49,7 +49,7 @@ pub fn parsePass(input: []const u8, base: ?[]const u8, href: []const u8, origin:
4949 defer allocator.free(u.href);
5050 _ = href;
5151 _ = origin;
52 _ = protocol;
52 try expect(u.protocol).toEqualString(protocol);
5353 _ = username;
5454 _ = password;
5555 _ = host;
url.zig+64-29
......@@ -7,6 +7,7 @@ const unicode_idna = @import("unicode-idna");
77pub const URL = struct {
88 href: []const u8,
99 scheme: []const u8 = "",
10 protocol: []const u8,
1011
1112 pub const HostKind = enum {
1213 name,
......@@ -75,23 +76,27 @@ pub const URL = struct {
7576 // inputl[i], must change in tandem with i
7677 var c: u8 = if (length > 0) inputl.items[i] else 0;
7778
78 var href = std.ArrayList(u8).init(alloc);
79 var href = ManyArrayList(8 + 7, u8).init(alloc);
7980 defer href.deinit();
80
81 var scheme = std.ArrayList(u8).init(alloc);
82 defer scheme.deinit();
81 // :
82 // //
8383 var username = std.ArrayList(u8).init(alloc);
8484 defer username.deinit();
85 // :
8586 var password = std.ArrayList(u8).init(alloc);
8687 defer password.deinit();
88 // @
8789 var host: Host = .{ .name = "" };
8890 defer if (host == .name) alloc.free(host.name);
91 // :
8992 var port = std.ArrayList(u8).init(alloc);
9093 defer port.deinit();
9194 var path = std.ArrayList(u8).init(alloc);
9295 defer path.deinit();
96 // ?
9397 var query = std.ArrayList(u8).init(alloc);
9498 defer query.deinit();
99 // #
95100 var fragment = std.ArrayList(u8).init(alloc);
96101 defer fragment.deinit();
97102
......@@ -122,6 +127,7 @@ pub const URL = struct {
122127 }
123128 // 2. Otherwise, if c is U+003A (:), then:
124129 else if (c == ':') {
130 try href.set(1, ":");
125131 // 1. If state override is given, then:
126132 if (state_override != null) {
127133 @panic("TODO");
......@@ -131,8 +137,7 @@ pub const URL = struct {
131137 // 4. If url’s scheme is "file" and its host is an empty host, then return.
132138 }
133139 // 2. Set url’s scheme to buffer.
134 scheme.clearRetainingCapacity();
135 try scheme.appendSlice(buffer.items);
140 try href.set(0, buffer.items);
136141 // 3. If state override is given, then:
137142 if (state_override != null) {
138143 @panic("TODO");
......@@ -142,20 +147,20 @@ pub const URL = struct {
142147 // 4. Set buffer to the empty string.
143148 buffer.clearRetainingCapacity();
144149 // 5. If url’s scheme is "file", then:
145 if (std.mem.eql(u8, scheme.items, "file")) {
150 if (std.mem.eql(u8, href.items(0), "file")) {
146151 // 1. If remaining does not start with "//", special-scheme-missing-following-solidus validation error.
147152 {}
148153 // 2. Set state to file state.
149154 state = .file;
150155 }
151156 // 6. Otherwise, if url is special, base is non-null, and base’s scheme is url’s scheme:
152 else if (isSchemeSpecial(scheme.items) and base != null and std.mem.eql(u8, base.?.scheme, scheme.items)) {
157 else if (isSchemeSpecial(href.items(0)) and base != null and std.mem.eql(u8, base.?.scheme, href.items(0))) {
153158 @panic("TODO");
154159 // 1. Assert: base is special (and therefore does not have an opaque path).
155160 // 2. Set state to special relative or authority state.
156161 }
157162 // 7. Otherwise, if url is special, set state to special authority slashes state.
158 else if (isSchemeSpecial(scheme.items)) {
163 else if (isSchemeSpecial(href.items(0))) {
159164 state = .special_authority_slashes;
160165 }
161166 // 8. Otherwise, if remaining starts with an U+002F (/), set state to path or authority state and increase pointer by 1.
......@@ -223,13 +228,13 @@ pub const URL = struct {
223228 // 1. Assert: base’s scheme is not "file".
224229 std.debug.assert(!std.mem.eql(u8, base.?.scheme, "file"));
225230 // 2. Set url’s scheme to base’s scheme.
226 try scheme.appendSlice(base.?.scheme);
231 try href.set(0, base.?.scheme);
227232 // 3. If c is U+002F (/), then set state to relative slash state.
228233 if (c == '/') {
229234 state = .relative_slash;
230235 }
231236 // 4. Otherwise, if url is special and c is U+005C (\), invalid-reverse-solidus validation error, set state to relative slash state.
232 else if (isSchemeSpecial(scheme.items) and c == '\\') {
237 else if (isSchemeSpecial(href.items(0)) and c == '\\') {
233238 state = .relative_slash;
234239 }
235240 // 5. Otherwise:
......@@ -267,7 +272,7 @@ pub const URL = struct {
267272 },
268273 .relative_slash => {
269274 // 1. If url is special and c is U+002F (/) or U+005C (\), then:
270 if (isSchemeSpecial(scheme.items) and (c == '/' or c == '\\')) {
275 if (isSchemeSpecial(href.items(0)) and (c == '/' or c == '\\')) {
271276 // 1. If c is U+005C (\), invalid-reverse-solidus validation error.
272277 // 2. Set state to special authority ignore slashes state.
273278 state = .special_authority_ignore_slashes;
......@@ -340,7 +345,7 @@ pub const URL = struct {
340345 // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
341346 // - url is special and c is U+005C (\)
342347 // then:
343 else if ((i == length or c == '/' or c == '?' or c == '#') or (isSchemeSpecial(scheme.items) and c == '\\')) {
348 else if ((i == length or c == '/' or c == '?' or c == '#') or (isSchemeSpecial(href.items(0)) and c == '\\')) {
344349 // 1. If atSignSeen is true and buffer is the empty string, host-missing validation error, return failure.
345350 if (atSignSeen and buffer.items.len == 0) return error.InvalidURL;
346351 // 2. Decrease pointer by buffer’s code point length + 1, set buffer to the empty string, and set state to host state.
......@@ -359,7 +364,7 @@ pub const URL = struct {
359364 },
360365 .host, .hostname => {
361366 // 1. If state override is given and url’s scheme is "file", then decrease pointer by 1 and set state to file host state.
362 if (state_override != null and std.mem.eql(u8, scheme.items, "file")) {
367 if (state_override != null and std.mem.eql(u8, href.items(0), "file")) {
363368 pointer -= 1;
364369 i = lastcpi(inputl.items[0..i]);
365370 c = inputl.items[i];
......@@ -373,7 +378,7 @@ pub const URL = struct {
373378 if (state_override != null and state_override.? == .hostname) return error.InvalidURL;
374379 // 3. Let host be the result of host parsing buffer with url is not special.
375380 // 4. If host is failure, then return failure.
376 const h = try parseHost(alloc, buffer.items, !isSchemeSpecial(scheme.items));
381 const h = try parseHost(alloc, buffer.items, !isSchemeSpecial(href.items(0)));
377382 // 5. Set url’s host to host, buffer to the empty string, and state to port state.
378383 host = h;
379384 buffer.clearRetainingCapacity();
......@@ -383,17 +388,17 @@ pub const URL = struct {
383388 // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
384389 // - url is special and c is U+005C (\)
385390 // then decrease pointer by 1, and:
386 else if ((i == length or c == '/' or c == '?' or c == '#') or (isSchemeSpecial(scheme.items) and c == '\\')) {
391 else if ((i == length or c == '/' or c == '?' or c == '#') or (isSchemeSpecial(href.items(0)) and c == '\\')) {
387392 pointer -= 1;
388393 i = lastcpi(inputl.items[0..i]);
389394 c = inputl.items[i];
390395 // 1. If url is special and buffer is the empty string, host-missing validation error, return failure.
391 if (isSchemeSpecial(scheme.items) and buffer.items.len == 0) return error.InvalidURL
396 if (isSchemeSpecial(href.items(0)) and buffer.items.len == 0) return error.InvalidURL
392397 // 2. Otherwise, if state override is given, buffer is the empty string, and either url includes credentials or url’s port is non-null, then return failure.
393398 else if (state_override != null and buffer.items.len == 0 and (username.items.len > 0 or password.items.len > 0)) return error.InvalidURL;
394399 // 3. Let host be the result of host parsing buffer with url is not special.
395400 // 4. If host is failure, then return failure.
396 const h = try parseHost(alloc, buffer.items, !isSchemeSpecial(scheme.items));
401 const h = try parseHost(alloc, buffer.items, !isSchemeSpecial(href.items(0)));
397402 // 5. Set url’s host to host, buffer to the empty string, and state to path start state.
398403 host = h;
399404 buffer.clearRetainingCapacity();
......@@ -421,14 +426,14 @@ pub const URL = struct {
421426 // - url is special and c is U+005C (\); or
422427 // - state override is given,
423428 // then:
424 else if ((i == length or c == '/' or c == '?' or c == '#') or (isSchemeSpecial(scheme.items) and c == '\\') or (state_override != null)) {
429 else if ((i == length or c == '/' or c == '?' or c == '#') or (isSchemeSpecial(href.items(0)) and c == '\\') or (state_override != null)) {
425430 // 1. If buffer is not the empty string:
426431 if (buffer.items.len > 0) {
427432 // 1. Let port be the mathematical integer value that is represented by buffer in radix-10 using ASCII digits for digits with values 0 through 9.
428433 // 2. If port is not a 16-bit unsigned integer, port-out-of-range validation error, return failure.
429434 const p = std.fmt.parseInt(u16, buffer.items, 10) catch return error.InvalidURL;
430435 // 3. Set url’s port to null, if port is url’s scheme’s default port; otherwise to port.
431 if (schemeDefaultPort(scheme.items) != p) try port.writer().print("{d}", .{p});
436 if (schemeDefaultPort(href.items(0)) != p) try port.writer().print("{d}", .{p});
432437 // 4. Set buffer to the empty string.
433438 buffer.clearRetainingCapacity();
434439 // 5. If state override is given, then return.
......@@ -449,8 +454,7 @@ pub const URL = struct {
449454 },
450455 .file => {
451456 // 1. Set url’s scheme to "file".
452 scheme.clearRetainingCapacity();
453 try scheme.appendSlice("file");
457 try href.set(0, "file");
454458 // 2. Set url’s host to the empty string.
455459 host = .{ .name = "" };
456460 // 3. If c is U+002F (/) or U+005C (\), then:
......@@ -552,7 +556,7 @@ pub const URL = struct {
552556 else {
553557 // 1. Let host be the result of host parsing buffer with url is not special.
554558 // 2. If host is failure, then return failure.
555 var h = try parseHost(alloc, buffer.items, !isSchemeSpecial(scheme.items));
559 var h = try parseHost(alloc, buffer.items, !isSchemeSpecial(href.items(0)));
556560 // 3. If host is "localhost", then set host to the empty string.
557561 if (h == .name and std.mem.eql(u8, h.name, "localhost")) {
558562 alloc.free(h.name);
......@@ -572,7 +576,7 @@ pub const URL = struct {
572576 },
573577 .path_start => {
574578 // 1. If url is special, then:
575 if (isSchemeSpecial(scheme.items)) {
579 if (isSchemeSpecial(href.items(0))) {
576580 // 1. If c is U+005C (\), invalid-reverse-solidus validation error.
577581 {}
578582 // 2. Set state to path state.
......@@ -616,7 +620,7 @@ pub const URL = struct {
616620 // - url is special and c is U+005C (\)
617621 // - state override is not given and c is U+003F (?) or U+0023 (#)
618622 // then:
619 if ((i == length or c == '/') or (isSchemeSpecial(scheme.items) and c == '\\') or (state_override == null and (c == '?' or c == '#'))) {
623 if ((i == length or c == '/') or (isSchemeSpecial(href.items(0)) and c == '\\') or (state_override == null and (c == '?' or c == '#'))) {
620624 // 1. If url is special and c is U+005C (\), invalid-reverse-solidus validation error.
621625 {}
622626 // 2. If buffer is a double-dot URL path segment, then:
......@@ -627,14 +631,14 @@ pub const URL = struct {
627631 // > This means that for input /usr/.. the result is / and not a lack of a path.
628632 }
629633 // 3. Otherwise, if buffer is a single-dot URL path segment and if neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string to url’s path.
630 else if (isSingleDotPathSeg(buffer.items) and (c != '/' or !(isSchemeSpecial(scheme.items) and c == '\\'))) {
634 else if (isSingleDotPathSeg(buffer.items) and (c != '/' or !(isSchemeSpecial(href.items(0)) and c == '\\'))) {
631635 // @panic("TODO");
632636 }
633637 // 4. Otherwise, if buffer is not a single-dot URL path segment, then:
634638 else if (!isSingleDotPathSeg(buffer.items)) {
635639 // 1. If url’s scheme is "file", url’s path is empty, and buffer is a Windows drive letter, then replace the second code point in buffer with U+003A (:).
636640 // > This is a (platform-independent) Windows drive letter quirk.
637 if (std.mem.eql(u8, scheme.items, "file") and path.items.len == 0 and isWindowsDriveLetter(buffer.items)) {
641 if (std.mem.eql(u8, href.items(0), "file") and path.items.len == 0 and isWindowsDriveLetter(buffer.items)) {
638642 buffer.items[1] = ':';
639643 }
640644 // 2. Append buffer to url’s path.
......@@ -715,7 +719,7 @@ pub const URL = struct {
715719 // 1. Let queryPercentEncodeSet be the special-query percent-encode set if url is special; otherwise the query percent-encode set.
716720 // 2. Percent-encode after encoding, with encoding, buffer, and queryPercentEncodeSet, and append the result to url’s query.
717721 // > This operation cannot be invoked code-point-for-code-point due to the stateful ISO-2022-JP encoder.
718 if (isSchemeSpecial(scheme.items)) {
722 if (isSchemeSpecial(href.items(0))) {
719723 try percentEncodeAL(&query, buffer.items, is_special_query_percent_char);
720724 } else {
721725 try percentEncodeAL(&query, buffer.items, is_query_percent_char);
......@@ -762,10 +766,11 @@ pub const URL = struct {
762766 }
763767 }
764768
765 const _href = try href.toOwnedSlice();
769 const _href = try href.list.toOwnedSlice();
766770
767771 const url: URL = .{
768772 .href = _href,
773 .protocol = _href[0..extras.sum(usize, href.lengths[0..2])],
769774 };
770775 return url;
771776 }
......@@ -1376,3 +1381,33 @@ fn percentEncodeAL(list: *std.ArrayList(u8), input: []const u8, comptime set: fn
13761381 }
13771382 }
13781383}
1384
1385pub fn ManyArrayList(N: usize, T: type) type {
1386 return struct {
1387 list: std.ArrayList(T),
1388 lengths: [N]usize,
1389
1390 pub fn init(allocator: std.mem.Allocator) @This() {
1391 return .{
1392 .list = .init(allocator),
1393 .lengths = @splat(0),
1394 };
1395 }
1396
1397 pub fn deinit(self: *@This()) void {
1398 self.list.deinit();
1399 }
1400
1401 pub fn set(self: *@This(), n: usize, slice: []const T) !void {
1402 const real_n = extras.sum(usize, self.lengths[0..n]);
1403 try self.list.replaceRange(real_n, self.lengths[n], slice);
1404 self.lengths[n] = slice.len;
1405 }
1406
1407 pub fn items(self: *@This(), n: usize) []T {
1408 const real_n = extras.sum(usize, self.lengths[0..n]);
1409 const len = self.lengths[n];
1410 return self.list.items[real_n..][0..len];
1411 }
1412 };
1413}
zig-out/test.zig+1-1
......@@ -19,7 +19,7 @@ pub fn parsePass(input: []const u8, base: ?[]const u8, href: []const u8, origin:
1919 defer allocator.free(u.href);
2020 _ = href;
2121 _ = origin;
22 _ = protocol;
22 try expect(u.protocol).toEqualString(protocol);
2323 _ = username;
2424 _ = password;
2525 _ = host;