| ... | ... | @@ -78,27 +78,22 @@ pub const URL = struct { |
| 78 | 78 | |
| 79 | 79 | var href = ManyArrayList(8 + 7, u8).init(alloc); |
| 80 | 80 | defer href.deinit(); |
| 81 | // scheme |
| 81 | 82 | // : |
| 82 | 83 | // // |
| 83 | | var username = std.ArrayList(u8).init(alloc); |
| 84 | | defer username.deinit(); |
| 84 | // username |
| 85 | 85 | // : |
| 86 | | var password = std.ArrayList(u8).init(alloc); |
| 87 | | defer password.deinit(); |
| 86 | // password |
| 88 | 87 | // @ |
| 89 | 88 | var host: Host = .{ .name = "" }; |
| 90 | 89 | defer if (host == .name) alloc.free(host.name); |
| 91 | 90 | // : |
| 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 |
| 96 | 93 | // ? |
| 97 | | var query = std.ArrayList(u8).init(alloc); |
| 98 | | defer query.deinit(); |
| 94 | // query |
| 99 | 95 | // # |
| 100 | | var fragment = std.ArrayList(u8).init(alloc); |
| 101 | | defer fragment.deinit(); |
| 96 | // fragment |
| 102 | 97 | |
| 103 | 98 | // 9. |
| 104 | 99 | while (true) { |
| ... | ... | @@ -172,7 +167,7 @@ pub const URL = struct { |
| 172 | 167 | } |
| 173 | 168 | // 9. Otherwise, set url’s path to the empty string and set state to opaque path state. |
| 174 | 169 | else { |
| 175 | | path.clearRetainingCapacity(); |
| 170 | href.clear(10); |
| 176 | 171 | state = .opaque_path; |
| 177 | 172 | } |
| 178 | 173 | } |
| ... | ... | @@ -248,18 +243,18 @@ pub const URL = struct { |
| 248 | 243 | // try query.appendSlice(base.?.query.?); |
| 249 | 244 | // 2. If c is U+003F (?), then set url’s query to the empty string, and state to query state. |
| 250 | 245 | if (c == '?') { |
| 251 | | query.clearRetainingCapacity(); |
| 246 | href.clear(12); |
| 252 | 247 | state = .query; |
| 253 | 248 | } |
| 254 | 249 | // 3. Otherwise, if c is U+0023 (#), set url’s fragment to the empty string and state to fragment state. |
| 255 | 250 | else if (c == '#') { |
| 256 | | fragment.clearRetainingCapacity(); |
| 251 | href.clear(14); |
| 257 | 252 | state = .fragment; |
| 258 | 253 | } |
| 259 | 254 | // 4. Otherwise, if c is not the EOF code point: |
| 260 | 255 | else if (i < length) { |
| 261 | 256 | // 1. Set url’s query to null. |
| 262 | | query.clearRetainingCapacity(); |
| 257 | href.clear(12); |
| 263 | 258 | // 2. Shorten url’s path. |
| 264 | 259 | @panic("TODO"); |
| 265 | 260 | // 3. Set state to path state and decrease pointer by 1. |
| ... | ... | @@ -380,6 +375,7 @@ pub const URL = struct { |
| 380 | 375 | // 4. If host is failure, then return failure. |
| 381 | 376 | const h = try parseHost(alloc, buffer.items, !isSchemeSpecial(href.items(0))); |
| 382 | 377 | // 5. Set url’s host to host, buffer to the empty string, and state to port state. |
| 378 | // try href.set(7, h); |
| 383 | 379 | host = h; |
| 384 | 380 | buffer.clearRetainingCapacity(); |
| 385 | 381 | state = .port; |
| ... | ... | @@ -395,11 +391,12 @@ pub const URL = struct { |
| 395 | 391 | // 1. If url is special and buffer is the empty string, host-missing validation error, return failure. |
| 396 | 392 | if (isSchemeSpecial(href.items(0)) and buffer.items.len == 0) return error.InvalidURL |
| 397 | 393 | // 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; |
| 399 | 395 | // 3. Let host be the result of host parsing buffer with url is not special. |
| 400 | 396 | // 4. If host is failure, then return failure. |
| 401 | 397 | const h = try parseHost(alloc, buffer.items, !isSchemeSpecial(href.items(0))); |
| 402 | 398 | // 5. Set url’s host to host, buffer to the empty string, and state to path start state. |
| 399 | // try href.set(7, h); |
| 403 | 400 | host = h; |
| 404 | 401 | buffer.clearRetainingCapacity(); |
| 405 | 402 | state = .path_start; |
| ... | ... | @@ -433,7 +430,7 @@ pub const URL = struct { |
| 433 | 430 | // 2. If port is not a 16-bit unsigned integer, port-out-of-range validation error, return failure. |
| 434 | 431 | const p = std.fmt.parseInt(u16, buffer.items, 10) catch return error.InvalidURL; |
| 435 | 432 | // 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}); |
| 437 | 434 | // 4. Set buffer to the empty string. |
| 438 | 435 | buffer.clearRetainingCapacity(); |
| 439 | 436 | // 5. If state override is given, then return. |
| ... | ... | @@ -456,7 +453,7 @@ pub const URL = struct { |
| 456 | 453 | // 1. Set url’s scheme to "file". |
| 457 | 454 | try href.set(0, "file"); |
| 458 | 455 | // 2. Set url’s host to the empty string. |
| 459 | | host = .{ .name = "" }; |
| 456 | href.clear(7); |
| 460 | 457 | // 3. If c is U+002F (/) or U+005C (\), then: |
| 461 | 458 | if (c == '/' or c == '\\') { |
| 462 | 459 | // 1. If c is U+005C (\), invalid-reverse-solidus validation error. |
| ... | ... | @@ -472,18 +469,18 @@ pub const URL = struct { |
| 472 | 469 | // try query.appendSlice(base.?.query.?); |
| 473 | 470 | // 2. If c is U+003F (?), then set url’s query to the empty string and state to query state. |
| 474 | 471 | if (c == '?') { |
| 475 | | query.clearRetainingCapacity(); |
| 472 | href.clear(12); |
| 476 | 473 | state = .query; |
| 477 | 474 | } |
| 478 | 475 | // 3. Otherwise, if c is U+0023 (#), set url’s fragment to the empty string and state to fragment state. |
| 479 | 476 | else if (c == '#') { |
| 480 | | fragment.clearRetainingCapacity(); |
| 477 | href.clear(14); |
| 481 | 478 | state = .fragment; |
| 482 | 479 | } |
| 483 | 480 | // 4. Otherwise, if c is not the EOF code point: |
| 484 | 481 | else if (i < length) { |
| 485 | 482 | // 1. Set url’s query to null. |
| 486 | | query.clearRetainingCapacity(); |
| 483 | href.clear(12); |
| 487 | 484 | // 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. |
| 488 | 485 | // 3. Otherwise: |
| 489 | 486 | // This is a (platform-independent) Windows drive letter quirk. |
| ... | ... | @@ -546,7 +543,7 @@ pub const URL = struct { |
| 546 | 543 | // 2. Otherwise, if buffer is the empty string, then: |
| 547 | 544 | else if (buffer.items.len == 0) { |
| 548 | 545 | // 1. Set url’s host to the empty string. |
| 549 | | host = .{ .name = "" }; |
| 546 | href.clear(7); |
| 550 | 547 | // 2. If state override is given, then return. |
| 551 | 548 | if (state_override != null) break; |
| 552 | 549 | // 3. Set state to path start state. |
| ... | ... | @@ -563,6 +560,7 @@ pub const URL = struct { |
| 563 | 560 | h = .{ .name = "" }; |
| 564 | 561 | } |
| 565 | 562 | // 4. Set url’s host to host. |
| 563 | // try href.set(7, h); |
| 566 | 564 | host = h; |
| 567 | 565 | // 5. If state override is given, then return. |
| 568 | 566 | if (state_override != null) break; |
| ... | ... | @@ -590,12 +588,12 @@ pub const URL = struct { |
| 590 | 588 | } |
| 591 | 589 | // 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. |
| 592 | 590 | else if (state_override == null and c == '?') { |
| 593 | | query.clearRetainingCapacity(); |
| 591 | href.clear(12); |
| 594 | 592 | state = .query; |
| 595 | 593 | } |
| 596 | 594 | // 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. |
| 597 | 595 | else if (state_override == null and c == '#') { |
| 598 | | fragment.clearRetainingCapacity(); |
| 596 | href.clear(14); |
| 599 | 597 | state = .fragment; |
| 600 | 598 | } |
| 601 | 599 | // 4. Otherwise, if c is not the EOF code point: |
| ... | ... | @@ -638,22 +636,22 @@ pub const URL = struct { |
| 638 | 636 | else if (!isSingleDotPathSeg(buffer.items)) { |
| 639 | 637 | // 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 (:). |
| 640 | 638 | // > 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)) { |
| 642 | 640 | buffer.items[1] = ':'; |
| 643 | 641 | } |
| 644 | 642 | // 2. Append buffer to url’s path. |
| 645 | | try path.appendSlice(buffer.items); |
| 643 | try href.appendSlice(10, buffer.items); |
| 646 | 644 | } |
| 647 | 645 | // 5. Set buffer to the empty string. |
| 648 | 646 | buffer.clearRetainingCapacity(); |
| 649 | 647 | // 6. If c is U+003F (?), then set url’s query to the empty string and state to query state. |
| 650 | 648 | if (c == '?') { |
| 651 | | query.clearRetainingCapacity(); |
| 649 | href.clear(12); |
| 652 | 650 | state = .query; |
| 653 | 651 | } |
| 654 | 652 | // 7. If c is U+0023 (#), then set url’s fragment to the empty string and state to fragment state. |
| 655 | 653 | if (c == '#') { |
| 656 | | fragment.clearRetainingCapacity(); |
| 654 | href.clear(14); |
| 657 | 655 | state = .fragment; |
| 658 | 656 | } |
| 659 | 657 | } |
| ... | ... | @@ -676,12 +674,12 @@ pub const URL = struct { |
| 676 | 674 | .opaque_path => { |
| 677 | 675 | // 1. If c is U+003F (?), then set url’s query to the empty string and state to query state. |
| 678 | 676 | if (c == '?') { |
| 679 | | query.clearRetainingCapacity(); |
| 677 | href.clear(12); |
| 680 | 678 | state = .query; |
| 681 | 679 | } |
| 682 | 680 | // 2. Otherwise, if c is U+0023 (#), then set url’s fragment to the empty string and state to fragment state. |
| 683 | 681 | else if (c == '#') { |
| 684 | | fragment.clearRetainingCapacity(); |
| 682 | href.clear(14); |
| 685 | 683 | state = .fragment; |
| 686 | 684 | } |
| 687 | 685 | // 3. Otherwise, if c is U+0020 SPACE: |
| ... | ... | @@ -689,11 +687,11 @@ pub const URL = struct { |
| 689 | 687 | // 1. If remaining starts with U+003F (?) or U+003F (#), then append "%20" to url’s path. |
| 690 | 688 | const rem = inputl.items[i + l(c) ..]; |
| 691 | 689 | if (std.mem.startsWith(u8, rem, "?") or std.mem.startsWith(u8, rem, "#")) { |
| 692 | | try path.appendSlice("%20"); |
| 690 | try href.appendSlice(10, "%20"); |
| 693 | 691 | } |
| 694 | 692 | // 2. Otherwise, append U+0020 SPACE to url’s path. |
| 695 | 693 | else { |
| 696 | | try path.append(' '); |
| 694 | try href.appendSlice(10, " "); |
| 697 | 695 | } |
| 698 | 696 | } |
| 699 | 697 | // 4. Otherwise, if c is not the EOF code point: |
| ... | ... | @@ -703,7 +701,7 @@ pub const URL = struct { |
| 703 | 701 | // 2. If c is U+0025 (%) and remaining does not start with two ASCII hex digits, invalid-URL-unit validation error. |
| 704 | 702 | {} |
| 705 | 703 | // 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); |
| 707 | 705 | } |
| 708 | 706 | }, |
| 709 | 707 | .query => { |
| ... | ... | @@ -720,15 +718,15 @@ pub const URL = struct { |
| 720 | 718 | // 2. Percent-encode after encoding, with encoding, buffer, and queryPercentEncodeSet, and append the result to url’s query. |
| 721 | 719 | // > This operation cannot be invoked code-point-for-code-point due to the stateful ISO-2022-JP encoder. |
| 722 | 720 | 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); |
| 724 | 722 | } else { |
| 725 | | try percentEncodeAL(&query, buffer.items, is_query_percent_char); |
| 723 | try percentEncodeML(&href, 12, buffer.items, is_query_percent_char); |
| 726 | 724 | } |
| 727 | 725 | // 3. Set buffer to the empty string. |
| 728 | 726 | buffer.clearRetainingCapacity(); |
| 729 | 727 | // 4. If c is U+0023 (#), then set url’s fragment to the empty string and state to fragment state. |
| 730 | 728 | if (c == '#') { |
| 731 | | fragment.clearRetainingCapacity(); |
| 729 | href.clear(14); |
| 732 | 730 | state = .fragment; |
| 733 | 731 | } |
| 734 | 732 | } |
| ... | ... | @@ -750,7 +748,7 @@ pub const URL = struct { |
| 750 | 748 | // 2. If c is U+0025 (%) and remaining does not start with two ASCII hex digits, invalid-URL-unit validation error. |
| 751 | 749 | {} |
| 752 | 750 | // 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); |
| 754 | 752 | } |
| 755 | 753 | }, |
| 756 | 754 | } |
| ... | ... | @@ -1381,6 +1379,29 @@ fn percentEncodeAL(list: *std.ArrayList(u8), input: []const u8, comptime set: fn |
| 1381 | 1379 | } |
| 1382 | 1380 | } |
| 1383 | 1381 | } |
| 1382 | fn 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 | } |
| 1392 | fn 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 | } |
| 1384 | 1405 | |
| 1385 | 1406 | pub fn ManyArrayList(N: usize, T: type) type { |
| 1386 | 1407 | return struct { |
| ... | ... | @@ -1409,5 +1430,23 @@ pub fn ManyArrayList(N: usize, T: type) type { |
| 1409 | 1430 | const len = self.lengths[n]; |
| 1410 | 1431 | return self.list.items[real_n..][0..len]; |
| 1411 | 1432 | } |
| 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 | } |
| 1412 | 1451 | }; |
| 1413 | 1452 | } |