| ... | ... | @@ -14,6 +14,7 @@ pub const URL = struct { |
| 14 | 14 | hostname_kind: HostKind = .name, |
| 15 | 15 | port: []const u8, |
| 16 | 16 | host: []const u8, |
| 17 | pathname: []const u8, |
| 17 | 18 | search: []const u8, |
| 18 | 19 | hash: []const u8, |
| 19 | 20 | |
| ... | ... | @@ -225,6 +226,7 @@ pub const URL = struct { |
| 225 | 226 | pointer -= 1; |
| 226 | 227 | i = lastcpi(inputl.items[0..i]); |
| 227 | 228 | c = inputl.items[i]; |
| 229 | try href.appendSlice(10, "/"); |
| 228 | 230 | } |
| 229 | 231 | }, |
| 230 | 232 | .relative => { |
| ... | ... | @@ -413,7 +415,7 @@ pub const URL = struct { |
| 413 | 415 | // 1. If url is special and buffer is the empty string, host-missing validation error, return failure. |
| 414 | 416 | if (isSchemeSpecial(href.items(0)) and buffer.items.len == 0) return error.InvalidURL |
| 415 | 417 | // 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. |
| 416 | | 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; |
| 418 | else if (state_override != null and buffer.items.len == 0 and (href.lengths[3] > 0 or href.lengths[5] > 0)) return error.InvalidURL; |
| 417 | 419 | // 3. Let host be the result of host parsing buffer with url is not special. |
| 418 | 420 | // 4. If host is failure, then return failure. |
| 419 | 421 | const h = try parseHost(alloc, buffer.items, !isSchemeSpecial(href.items(0))); |
| ... | ... | @@ -527,6 +529,7 @@ pub const URL = struct { |
| 527 | 529 | pointer -= 1; |
| 528 | 530 | i = lastcpi(inputl.items[0..i]); |
| 529 | 531 | c = inputl.items[i]; |
| 532 | try href.appendSlice(10, "/"); |
| 530 | 533 | } |
| 531 | 534 | }, |
| 532 | 535 | .file_slash => { |
| ... | ... | @@ -552,6 +555,7 @@ pub const URL = struct { |
| 552 | 555 | pointer -= 1; |
| 553 | 556 | i = lastcpi(inputl.items[0..i]); |
| 554 | 557 | c = inputl.items[i]; |
| 558 | try href.appendSlice(10, "/"); |
| 555 | 559 | } |
| 556 | 560 | }, |
| 557 | 561 | .file_host => { |
| ... | ... | @@ -564,6 +568,7 @@ pub const URL = struct { |
| 564 | 568 | // > This is a (platform-independent) Windows drive letter quirk. buffer is not reset here and instead used in the path state. |
| 565 | 569 | if (state_override == null and isWindowsDriveLetter(buffer.items)) { |
| 566 | 570 | state = .path; |
| 571 | try href.appendSlice(10, "/"); |
| 567 | 572 | } |
| 568 | 573 | // 2. Otherwise, if buffer is the empty string, then: |
| 569 | 574 | else if (buffer.items.len == 0) { |
| ... | ... | @@ -596,7 +601,9 @@ pub const URL = struct { |
| 596 | 601 | } |
| 597 | 602 | } |
| 598 | 603 | // 2. Otherwise, append c to buffer. |
| 599 | | try buffer.appendSlice(inputl.items[i..][0..l(c)]); |
| 604 | else { |
| 605 | try buffer.appendSlice(inputl.items[i..][0..l(c)]); |
| 606 | } |
| 600 | 607 | }, |
| 601 | 608 | .path_start => { |
| 602 | 609 | // 1. If url is special, then: |
| ... | ... | @@ -611,6 +618,7 @@ pub const URL = struct { |
| 611 | 618 | i = lastcpi(inputl.items[0..i]); |
| 612 | 619 | c = inputl.items[i]; |
| 613 | 620 | } |
| 621 | try href.appendSlice(10, "/"); |
| 614 | 622 | } |
| 615 | 623 | // 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. |
| 616 | 624 | else if (state_override == null and c == '?') { |
| ... | ... | @@ -633,6 +641,8 @@ pub const URL = struct { |
| 633 | 641 | pointer -= 1; |
| 634 | 642 | i = lastcpi(inputl.items[0..i]); |
| 635 | 643 | c = inputl.items[i]; |
| 644 | } else { |
| 645 | try href.appendSlice(10, &.{c}); |
| 636 | 646 | } |
| 637 | 647 | } |
| 638 | 648 | // 5. Otherwise, if state override is given and url’s host is null, append the empty string to url’s path. |
| ... | ... | @@ -641,34 +651,41 @@ pub const URL = struct { |
| 641 | 651 | } |
| 642 | 652 | }, |
| 643 | 653 | .path => { |
| 654 | const is_lsep = c == '/'; |
| 655 | const is_rsep = isSchemeSpecial(href.items(0)) and c == '\\'; |
| 644 | 656 | // 1. If one of the following is true: |
| 645 | 657 | // - c is the EOF code point or U+002F (/) |
| 646 | 658 | // - url is special and c is U+005C (\) |
| 647 | 659 | // - state override is not given and c is U+003F (?) or U+0023 (#) |
| 648 | 660 | // then: |
| 649 | | if ((i == length or c == '/') or (isSchemeSpecial(href.items(0)) and c == '\\') or (state_override == null and (c == '?' or c == '#'))) { |
| 661 | if ((i == length or is_lsep) or (is_rsep) or (state_override == null and (c == '?' or c == '#'))) { |
| 650 | 662 | // 1. If url is special and c is U+005C (\), invalid-reverse-solidus validation error. |
| 651 | 663 | {} |
| 652 | 664 | // 2. If buffer is a double-dot URL path segment, then: |
| 653 | 665 | if (isDoubleDotPathSeg(buffer.items)) { |
| 654 | | // @panic("TODO"); |
| 666 | const olen = href.lengths[10]; |
| 667 | const idx = std.mem.lastIndexOfScalar(u8, href.items(10)[0 .. olen - 1], '/') orelse 0; |
| 668 | try href.replace(10, idx + 1, olen - idx - 1, ""); |
| 655 | 669 | // 1. Shorten url’s path. |
| 656 | 670 | // 2. If neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string to url’s path. |
| 657 | 671 | // > This means that for input /usr/.. the result is / and not a lack of a path. |
| 658 | 672 | } |
| 659 | 673 | // 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. |
| 660 | | else if (isSingleDotPathSeg(buffer.items) and (c != '/' or !(isSchemeSpecial(href.items(0)) and c == '\\'))) { |
| 661 | | // @panic("TODO"); |
| 674 | else if (isSingleDotPathSeg(buffer.items) and (c != '/' or !(is_rsep))) { |
| 675 | // =no action needed |
| 662 | 676 | } |
| 663 | 677 | // 4. Otherwise, if buffer is not a single-dot URL path segment, then: |
| 664 | 678 | else if (!isSingleDotPathSeg(buffer.items)) { |
| 665 | 679 | // 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 (:). |
| 666 | 680 | // > This is a (platform-independent) Windows drive letter quirk. |
| 667 | | if (std.mem.eql(u8, href.items(0), "file") and href.items(10).len == 0 and isWindowsDriveLetter(buffer.items)) { |
| 681 | if (std.mem.eql(u8, href.items(0), "file") and href.lengths[10] <= 1 and isWindowsDriveLetter(buffer.items)) { |
| 668 | 682 | buffer.items[1] = ':'; |
| 669 | 683 | } |
| 670 | 684 | // 2. Append buffer to url’s path. |
| 685 | // if (!std.mem.endsWith(u8, href.items(10), "/")) try href.appendSlice(10, "/"); |
| 671 | 686 | try href.appendSlice(10, buffer.items); |
| 687 | if (is_lsep) try href.appendSlice(10, "/"); |
| 688 | if (is_rsep) try href.appendSlice(10, "/"); |
| 672 | 689 | } |
| 673 | 690 | // 5. Set buffer to the empty string. |
| 674 | 691 | buffer.clearRetainingCapacity(); |
| ... | ... | @@ -808,6 +825,7 @@ pub const URL = struct { |
| 808 | 825 | .hostname_kind = hostname_kind, |
| 809 | 826 | .port = _href[extras.sum(usize, href.lengths[0..9])..][0..href.lengths[9]], |
| 810 | 827 | .host = _href[extras.sum(usize, href.lengths[0..7])..][0..extras.sum(usize, href.lengths[7..][0..if (href.lengths[9] == 0) 1 else 3])], |
| 828 | .pathname = _href[extras.sum(usize, href.lengths[0..10])..][0..href.lengths[10]], |
| 811 | 829 | .search = if (href.lengths[12] == 0) "" else _href[extras.sum(usize, href.lengths[0..11])..][0..extras.sum(usize, href.lengths[11..][0..2])], |
| 812 | 830 | .hash = if (href.lengths[14] == 0) "" else _href[extras.sum(usize, href.lengths[0..13])..][0..extras.sum(usize, href.lengths[13..][0..2])], |
| 813 | 831 | }; |
| ... | ... | @@ -1345,6 +1363,7 @@ fn is_query_percent_char(c: u8) bool { |
| 1345 | 1363 | /// https://url.spec.whatwg.org/#path-percent-encode-set |
| 1346 | 1364 | fn is_path_percent_char(c: u8) bool { |
| 1347 | 1365 | if (c == '?') return true; |
| 1366 | if (c == '^') return true; |
| 1348 | 1367 | if (c == '`') return true; |
| 1349 | 1368 | if (c == '{') return true; |
| 1350 | 1369 | if (c == '}') return true; |
| ... | ... | @@ -1371,7 +1390,7 @@ fn is_userinfo_percent_char(c: u8) bool { |
| 1371 | 1390 | if (c == ';') return true; |
| 1372 | 1391 | if (c == '=') return true; |
| 1373 | 1392 | if (c == '@') return true; |
| 1374 | | if (c >= '[' and c <= '^') return true; |
| 1393 | if (c >= '[' and c <= ']') return true; |
| 1375 | 1394 | if (c == '|') return true; |
| 1376 | 1395 | return is_path_percent_char(c); |
| 1377 | 1396 | } |
| ... | ... | @@ -1494,6 +1513,23 @@ fn setHost(href: *ManyArrayList(15, u8), h: URL.Host) !void { |
| 1494 | 1513 | }, |
| 1495 | 1514 | } |
| 1496 | 1515 | } |
| 1516 | pub fn replaceInPlace(comptime T: type, input: []T, needle: []const T, replacement: []const T) usize { |
| 1517 | // Empty needle will loop until output buffer overflows. |
| 1518 | std.debug.assert(needle.len > 0); |
| 1519 | std.debug.assert(needle.len >= replacement.len); |
| 1520 | |
| 1521 | var slide: usize = 0; |
| 1522 | var replacements: usize = 0; |
| 1523 | while (std.mem.indexOf(u8, input[slide..], needle)) |idx| { |
| 1524 | slide += idx; |
| 1525 | std.mem.copyForwards(u8, input[slide..], replacement); |
| 1526 | slide += replacement.len; |
| 1527 | std.mem.copyForwards(u8, input[slide..], input[slide..][needle.len - replacement.len ..]); |
| 1528 | slide += needle.len - replacement.len; |
| 1529 | replacements += 1; |
| 1530 | } |
| 1531 | return replacements; |
| 1532 | } |
| 1497 | 1533 | |
| 1498 | 1534 | pub fn ManyArrayList(N: usize, T: type) type { |
| 1499 | 1535 | return struct { |
| ... | ... | @@ -1540,5 +1576,13 @@ pub fn ManyArrayList(N: usize, T: type) type { |
| 1540 | 1576 | try self.list.insertSlice(real_n, slice); |
| 1541 | 1577 | self.lengths[n] += slice.len; |
| 1542 | 1578 | } |
| 1579 | |
| 1580 | pub fn replace(self: *@This(), n: usize, o: usize, c: usize, slice: []const T) !void { |
| 1581 | std.debug.assert(o + c <= self.lengths[n]); |
| 1582 | const real_n = extras.sum(usize, self.lengths[0..n]); |
| 1583 | try self.list.replaceRange(real_n + o, c, slice); |
| 1584 | self.lengths[n] -= c; |
| 1585 | self.lengths[n] += slice.len; |
| 1586 | } |
| 1543 | 1587 | }; |
| 1544 | 1588 | } |