authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-01-22 16:11:00 -08:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-01-22 16:11:00 -08:00
log9392e9e3186ef5f84d2a37432a0116fbd96a803d
tree3e9519c022e8c42b084278158be4aa5cf6ed257a
parente5ac23bf48c1d0ef54b95490723a0419b68abc6c

move ArrayLists into single StaticManyArrayList


1 files changed, 77 insertions(+), 38 deletions(-)

url.zig+77-38
......@@ -78,27 +78,22 @@ pub const URL = struct {
7878
7979 var href = ManyArrayList(8 + 7, u8).init(alloc);
8080 defer href.deinit();
81 // scheme
8182 // :
8283 // //
83 var username = std.ArrayList(u8).init(alloc);
84 defer username.deinit();
84 // username
8585 // :
86 var password = std.ArrayList(u8).init(alloc);
87 defer password.deinit();
86 // password
8887 // @
8988 var host: Host = .{ .name = "" };
9089 defer if (host == .name) alloc.free(host.name);
9190 // :
92 var port = std.ArrayList(u8).init(alloc);
93 defer port.deinit();
94 var path = std.ArrayList(u8).init(alloc);
95 defer path.deinit();
91 // port
92 // path
9693 // ?
97 var query = std.ArrayList(u8).init(alloc);
98 defer query.deinit();
94 // query
9995 // #
100 var fragment = std.ArrayList(u8).init(alloc);
101 defer fragment.deinit();
96 // fragment
10297
10398 // 9.
10499 while (true) {
......@@ -172,7 +167,7 @@ pub const URL = struct {
172167 }
173168 // 9. Otherwise, set url’s path to the empty string and set state to opaque path state.
174169 else {
175 path.clearRetainingCapacity();
170 href.clear(10);
176171 state = .opaque_path;
177172 }
178173 }
......@@ -248,18 +243,18 @@ pub const URL = struct {
248243 // try query.appendSlice(base.?.query.?);
249244 // 2. If c is U+003F (?), then set url’s query to the empty string, and state to query state.
250245 if (c == '?') {
251 query.clearRetainingCapacity();
246 href.clear(12);
252247 state = .query;
253248 }
254249 // 3. Otherwise, if c is U+0023 (#), set url’s fragment to the empty string and state to fragment state.
255250 else if (c == '#') {
256 fragment.clearRetainingCapacity();
251 href.clear(14);
257252 state = .fragment;
258253 }
259254 // 4. Otherwise, if c is not the EOF code point:
260255 else if (i < length) {
261256 // 1. Set url’s query to null.
262 query.clearRetainingCapacity();
257 href.clear(12);
263258 // 2. Shorten url’s path.
264259 @panic("TODO");
265260 // 3. Set state to path state and decrease pointer by 1.
......@@ -380,6 +375,7 @@ pub const URL = struct {
380375 // 4. If host is failure, then return failure.
381376 const h = try parseHost(alloc, buffer.items, !isSchemeSpecial(href.items(0)));
382377 // 5. Set url’s host to host, buffer to the empty string, and state to port state.
378 // try href.set(7, h);
383379 host = h;
384380 buffer.clearRetainingCapacity();
385381 state = .port;
......@@ -395,11 +391,12 @@ pub const URL = struct {
395391 // 1. If url is special and buffer is the empty string, host-missing validation error, return failure.
396392 if (isSchemeSpecial(href.items(0)) and buffer.items.len == 0) return error.InvalidURL
397393 // 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.
398 else if (state_override != null and buffer.items.len == 0 and (username.items.len > 0 or password.items.len > 0)) return error.InvalidURL;
394 else if (state_override != null and buffer.items.len == 0 and (href.items(3).len > 0 or href.items(5).len > 0)) return error.InvalidURL;
399395 // 3. Let host be the result of host parsing buffer with url is not special.
400396 // 4. If host is failure, then return failure.
401397 const h = try parseHost(alloc, buffer.items, !isSchemeSpecial(href.items(0)));
402398 // 5. Set url’s host to host, buffer to the empty string, and state to path start state.
399 // try href.set(7, h);
403400 host = h;
404401 buffer.clearRetainingCapacity();
405402 state = .path_start;
......@@ -433,7 +430,7 @@ pub const URL = struct {
433430 // 2. If port is not a 16-bit unsigned integer, port-out-of-range validation error, return failure.
434431 const p = std.fmt.parseInt(u16, buffer.items, 10) catch return error.InvalidURL;
435432 // 3. Set url’s port to null, if port is url’s scheme’s default port; otherwise to port.
436 if (schemeDefaultPort(href.items(0)) != p) try port.writer().print("{d}", .{p});
433 if (schemeDefaultPort(href.items(0)) != p) try href.print(9, "{d}", .{p});
437434 // 4. Set buffer to the empty string.
438435 buffer.clearRetainingCapacity();
439436 // 5. If state override is given, then return.
......@@ -456,7 +453,7 @@ pub const URL = struct {
456453 // 1. Set url’s scheme to "file".
457454 try href.set(0, "file");
458455 // 2. Set url’s host to the empty string.
459 host = .{ .name = "" };
456 href.clear(7);
460457 // 3. If c is U+002F (/) or U+005C (\), then:
461458 if (c == '/' or c == '\\') {
462459 // 1. If c is U+005C (\), invalid-reverse-solidus validation error.
......@@ -472,18 +469,18 @@ pub const URL = struct {
472469 // try query.appendSlice(base.?.query.?);
473470 // 2. If c is U+003F (?), then set url’s query to the empty string and state to query state.
474471 if (c == '?') {
475 query.clearRetainingCapacity();
472 href.clear(12);
476473 state = .query;
477474 }
478475 // 3. Otherwise, if c is U+0023 (#), set url’s fragment to the empty string and state to fragment state.
479476 else if (c == '#') {
480 fragment.clearRetainingCapacity();
477 href.clear(14);
481478 state = .fragment;
482479 }
483480 // 4. Otherwise, if c is not the EOF code point:
484481 else if (i < length) {
485482 // 1. Set url’s query to null.
486 query.clearRetainingCapacity();
483 href.clear(12);
487484 // 2. If the code point substring from pointer to the end of input does not start with a Windows drive letter, then shorten url’s path.
488485 // 3. Otherwise:
489486 // This is a (platform-independent) Windows drive letter quirk.
......@@ -546,7 +543,7 @@ pub const URL = struct {
546543 // 2. Otherwise, if buffer is the empty string, then:
547544 else if (buffer.items.len == 0) {
548545 // 1. Set url’s host to the empty string.
549 host = .{ .name = "" };
546 href.clear(7);
550547 // 2. If state override is given, then return.
551548 if (state_override != null) break;
552549 // 3. Set state to path start state.
......@@ -563,6 +560,7 @@ pub const URL = struct {
563560 h = .{ .name = "" };
564561 }
565562 // 4. Set url’s host to host.
563 // try href.set(7, h);
566564 host = h;
567565 // 5. If state override is given, then return.
568566 if (state_override != null) break;
......@@ -590,12 +588,12 @@ pub const URL = struct {
590588 }
591589 // 2. Otherwise, if state override is not given and c is U+003F (?), set url’s query to the empty string and state to query state.
592590 else if (state_override == null and c == '?') {
593 query.clearRetainingCapacity();
591 href.clear(12);
594592 state = .query;
595593 }
596594 // 3. Otherwise, if state override is not given and c is U+0023 (#), set url’s fragment to the empty string and state to fragment state.
597595 else if (state_override == null and c == '#') {
598 fragment.clearRetainingCapacity();
596 href.clear(14);
599597 state = .fragment;
600598 }
601599 // 4. Otherwise, if c is not the EOF code point:
......@@ -638,22 +636,22 @@ pub const URL = struct {
638636 else if (!isSingleDotPathSeg(buffer.items)) {
639637 // 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 (:).
640638 // > This is a (platform-independent) Windows drive letter quirk.
641 if (std.mem.eql(u8, href.items(0), "file") and path.items.len == 0 and isWindowsDriveLetter(buffer.items)) {
639 if (std.mem.eql(u8, href.items(0), "file") and href.items(10).len == 0 and isWindowsDriveLetter(buffer.items)) {
642640 buffer.items[1] = ':';
643641 }
644642 // 2. Append buffer to url’s path.
645 try path.appendSlice(buffer.items);
643 try href.appendSlice(10, buffer.items);
646644 }
647645 // 5. Set buffer to the empty string.
648646 buffer.clearRetainingCapacity();
649647 // 6. If c is U+003F (?), then set url’s query to the empty string and state to query state.
650648 if (c == '?') {
651 query.clearRetainingCapacity();
649 href.clear(12);
652650 state = .query;
653651 }
654652 // 7. If c is U+0023 (#), then set url’s fragment to the empty string and state to fragment state.
655653 if (c == '#') {
656 fragment.clearRetainingCapacity();
654 href.clear(14);
657655 state = .fragment;
658656 }
659657 }
......@@ -676,12 +674,12 @@ pub const URL = struct {
676674 .opaque_path => {
677675 // 1. If c is U+003F (?), then set url’s query to the empty string and state to query state.
678676 if (c == '?') {
679 query.clearRetainingCapacity();
677 href.clear(12);
680678 state = .query;
681679 }
682680 // 2. Otherwise, if c is U+0023 (#), then set url’s fragment to the empty string and state to fragment state.
683681 else if (c == '#') {
684 fragment.clearRetainingCapacity();
682 href.clear(14);
685683 state = .fragment;
686684 }
687685 // 3. Otherwise, if c is U+0020 SPACE:
......@@ -689,11 +687,11 @@ pub const URL = struct {
689687 // 1. If remaining starts with U+003F (?) or U+003F (#), then append "%20" to url’s path.
690688 const rem = inputl.items[i + l(c) ..];
691689 if (std.mem.startsWith(u8, rem, "?") or std.mem.startsWith(u8, rem, "#")) {
692 try path.appendSlice("%20");
690 try href.appendSlice(10, "%20");
693691 }
694692 // 2. Otherwise, append U+0020 SPACE to url’s path.
695693 else {
696 try path.append(' ');
694 try href.appendSlice(10, " ");
697695 }
698696 }
699697 // 4. Otherwise, if c is not the EOF code point:
......@@ -703,7 +701,7 @@ pub const URL = struct {
703701 // 2. If c is U+0025 (%) and remaining does not start with two ASCII hex digits, invalid-URL-unit validation error.
704702 {}
705703 // 3. UTF-8 percent-encode c using the C0 control percent-encode set and append the result to url’s path.
706 try percentEncodeScalarAL(&path, inputl.items[i..][0..l(c)], is_c0control_percent_char);
704 try percentEncodeScalarML(&href, 10, inputl.items[i..][0..l(c)], is_c0control_percent_char);
707705 }
708706 },
709707 .query => {
......@@ -720,15 +718,15 @@ pub const URL = struct {
720718 // 2. Percent-encode after encoding, with encoding, buffer, and queryPercentEncodeSet, and append the result to url’s query.
721719 // > This operation cannot be invoked code-point-for-code-point due to the stateful ISO-2022-JP encoder.
722720 if (isSchemeSpecial(href.items(0))) {
723 try percentEncodeAL(&query, buffer.items, is_special_query_percent_char);
721 try percentEncodeML(&href, 12, buffer.items, is_special_query_percent_char);
724722 } else {
725 try percentEncodeAL(&query, buffer.items, is_query_percent_char);
723 try percentEncodeML(&href, 12, buffer.items, is_query_percent_char);
726724 }
727725 // 3. Set buffer to the empty string.
728726 buffer.clearRetainingCapacity();
729727 // 4. If c is U+0023 (#), then set url’s fragment to the empty string and state to fragment state.
730728 if (c == '#') {
731 fragment.clearRetainingCapacity();
729 href.clear(14);
732730 state = .fragment;
733731 }
734732 }
......@@ -750,7 +748,7 @@ pub const URL = struct {
750748 // 2. If c is U+0025 (%) and remaining does not start with two ASCII hex digits, invalid-URL-unit validation error.
751749 {}
752750 // 3. UTF-8 percent-encode c using the fragment percent-encode set and append the result to url’s fragment.
753 try percentEncodeScalarAL(&fragment, inputl.items[i..][0..l(c)], is_fragment_percent_char);
751 try percentEncodeScalarML(&href, 14, inputl.items[i..][0..l(c)], is_fragment_percent_char);
754752 }
755753 },
756754 }
......@@ -1381,6 +1379,29 @@ fn percentEncodeAL(list: *std.ArrayList(u8), input: []const u8, comptime set: fn
13811379 }
13821380 }
13831381}
1382fn percentEncodeScalarML(list: *ManyArrayList(15, u8), n: usize, cp: []const u8, comptime set: fn (u8) bool) !void {
1383 if (set(cp[0])) {
1384 for (cp) |b| {
1385 try list.appendSlice(n, &.{'%'});
1386 try list.print(n, "{d:0<2}", .{b});
1387 }
1388 } else {
1389 try list.appendSlice(n, &.{cp[0]});
1390 }
1391}
1392fn percentEncodeML(list: *ManyArrayList(15, u8), n: usize, input: []const u8, comptime set: fn (u8) bool) !void {
1393 var it = std.unicode.Utf8View.initUnchecked(input).iterator();
1394 while (it.nextCodepointSlice()) |sl| {
1395 if (set(sl[0])) {
1396 for (sl) |b| {
1397 try list.appendSlice(n, &.{'%'});
1398 try list.print(n, "{d:0<2}", .{b});
1399 }
1400 } else {
1401 try list.appendSlice(n, sl);
1402 }
1403 }
1404}
13841405
13851406pub fn ManyArrayList(N: usize, T: type) type {
13861407 return struct {
......@@ -1409,5 +1430,23 @@ pub fn ManyArrayList(N: usize, T: type) type {
14091430 const len = self.lengths[n];
14101431 return self.list.items[real_n..][0..len];
14111432 }
1433
1434 pub fn clear(self: *@This(), n: usize) void {
1435 const real_n = extras.sum(usize, self.lengths[0..n]);
1436 self.list.replaceRangeAssumeCapacity(real_n, self.lengths[n], &.{});
1437 self.lengths[n] = 0;
1438 }
1439
1440 pub fn print(self: *@This(), n: usize, comptime fmt: []const u8, args: anytype) !void {
1441 var buffer: [64]u8 = undefined;
1442 const slice = std.fmt.bufPrint(&buffer, fmt, args) catch unreachable;
1443 return self.appendSlice(n, slice);
1444 }
1445
1446 pub fn appendSlice(self: *@This(), n: usize, slice: []const T) !void {
1447 const real_n = extras.sum(usize, self.lengths[0 .. n + 1]);
1448 try self.list.insertSlice(real_n, slice);
1449 self.lengths[n] += slice.len;
1450 }
14121451 };
14131452}