| ... | @@ -14,6 +14,7 @@ pub const URL = struct { | ... | @@ -14,6 +14,7 @@ pub const URL = struct { |
| 14 | hostname_kind: HostKind = .name, | 14 | hostname_kind: HostKind = .name, |
| 15 | port: []const u8, | 15 | port: []const u8, |
| 16 | host: []const u8, | 16 | host: []const u8, |
| | 17 | pathname: []const u8, |
| 17 | search: []const u8, | 18 | search: []const u8, |
| 18 | hash: []const u8, | 19 | hash: []const u8, |
| 19 | | 20 | |
| ... | @@ -225,6 +226,7 @@ pub const URL = struct { | ... | @@ -225,6 +226,7 @@ pub const URL = struct { |
| 225 | pointer -= 1; | 226 | pointer -= 1; |
| 226 | i = lastcpi(inputl.items[0..i]); | 227 | i = lastcpi(inputl.items[0..i]); |
| 227 | c = inputl.items[i]; | 228 | c = inputl.items[i]; |
| | 229 | try href.appendSlice(10, "/"); |
| 228 | } | 230 | } |
| 229 | }, | 231 | }, |
| 230 | .relative => { | 232 | .relative => { |
| ... | @@ -413,7 +415,7 @@ pub const URL = struct { | ... | @@ -413,7 +415,7 @@ pub const URL = struct { |
| 413 | // 1. If url is special and buffer is the empty string, host-missing validation error, return failure. | 415 | // 1. If url is special and buffer is the empty string, host-missing validation error, return failure. |
| 414 | if (isSchemeSpecial(href.items(0)) and buffer.items.len == 0) return error.InvalidURL | 416 | if (isSchemeSpecial(href.items(0)) and buffer.items.len == 0) return error.InvalidURL |
| 415 | // 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. | 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 | // 3. Let host be the result of host parsing buffer with url is not special. | 419 | // 3. Let host be the result of host parsing buffer with url is not special. |
| 418 | // 4. If host is failure, then return failure. | 420 | // 4. If host is failure, then return failure. |
| 419 | const h = try parseHost(alloc, buffer.items, !isSchemeSpecial(href.items(0))); | 421 | const h = try parseHost(alloc, buffer.items, !isSchemeSpecial(href.items(0))); |
| ... | @@ -527,6 +529,7 @@ pub const URL = struct { | ... | @@ -527,6 +529,7 @@ pub const URL = struct { |
| 527 | pointer -= 1; | 529 | pointer -= 1; |
| 528 | i = lastcpi(inputl.items[0..i]); | 530 | i = lastcpi(inputl.items[0..i]); |
| 529 | c = inputl.items[i]; | 531 | c = inputl.items[i]; |
| | 532 | try href.appendSlice(10, "/"); |
| 530 | } | 533 | } |
| 531 | }, | 534 | }, |
| 532 | .file_slash => { | 535 | .file_slash => { |
| ... | @@ -552,6 +555,7 @@ pub const URL = struct { | ... | @@ -552,6 +555,7 @@ pub const URL = struct { |
| 552 | pointer -= 1; | 555 | pointer -= 1; |
| 553 | i = lastcpi(inputl.items[0..i]); | 556 | i = lastcpi(inputl.items[0..i]); |
| 554 | c = inputl.items[i]; | 557 | c = inputl.items[i]; |
| | 558 | try href.appendSlice(10, "/"); |
| 555 | } | 559 | } |
| 556 | }, | 560 | }, |
| 557 | .file_host => { | 561 | .file_host => { |
| ... | @@ -564,6 +568,7 @@ pub const URL = struct { | ... | @@ -564,6 +568,7 @@ pub const URL = struct { |
| 564 | // > This is a (platform-independent) Windows drive letter quirk. buffer is not reset here and instead used in the path state. | 568 | // > This is a (platform-independent) Windows drive letter quirk. buffer is not reset here and instead used in the path state. |
| 565 | if (state_override == null and isWindowsDriveLetter(buffer.items)) { | 569 | if (state_override == null and isWindowsDriveLetter(buffer.items)) { |
| 566 | state = .path; | 570 | state = .path; |
| | 571 | try href.appendSlice(10, "/"); |
| 567 | } | 572 | } |
| 568 | // 2. Otherwise, if buffer is the empty string, then: | 573 | // 2. Otherwise, if buffer is the empty string, then: |
| 569 | else if (buffer.items.len == 0) { | 574 | else if (buffer.items.len == 0) { |
| ... | @@ -596,7 +601,9 @@ pub const URL = struct { | ... | @@ -596,7 +601,9 @@ pub const URL = struct { |
| 596 | } | 601 | } |
| 597 | } | 602 | } |
| 598 | // 2. Otherwise, append c to buffer. | 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 | .path_start => { | 608 | .path_start => { |
| 602 | // 1. If url is special, then: | 609 | // 1. If url is special, then: |
| ... | @@ -611,6 +618,7 @@ pub const URL = struct { | ... | @@ -611,6 +618,7 @@ pub const URL = struct { |
| 611 | i = lastcpi(inputl.items[0..i]); | 618 | i = lastcpi(inputl.items[0..i]); |
| 612 | c = inputl.items[i]; | 619 | c = inputl.items[i]; |
| 613 | } | 620 | } |
| | 621 | try href.appendSlice(10, "/"); |
| 614 | } | 622 | } |
| 615 | // 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. | 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 | else if (state_override == null and c == '?') { | 624 | else if (state_override == null and c == '?') { |
| ... | @@ -633,6 +641,8 @@ pub const URL = struct { | ... | @@ -633,6 +641,8 @@ pub const URL = struct { |
| 633 | pointer -= 1; | 641 | pointer -= 1; |
| 634 | i = lastcpi(inputl.items[0..i]); | 642 | i = lastcpi(inputl.items[0..i]); |
| 635 | c = inputl.items[i]; | 643 | c = inputl.items[i]; |
| | 644 | } else { |
| | 645 | try href.appendSlice(10, &.{c}); |
| 636 | } | 646 | } |
| 637 | } | 647 | } |
| 638 | // 5. Otherwise, if state override is given and url’s host is null, append the empty string to url’s path. | 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,34 +651,41 @@ pub const URL = struct { |
| 641 | } | 651 | } |
| 642 | }, | 652 | }, |
| 643 | .path => { | 653 | .path => { |
| | 654 | const is_lsep = c == '/'; |
| | 655 | const is_rsep = isSchemeSpecial(href.items(0)) and c == '\\'; |
| 644 | // 1. If one of the following is true: | 656 | // 1. If one of the following is true: |
| 645 | // - c is the EOF code point or U+002F (/) | 657 | // - c is the EOF code point or U+002F (/) |
| 646 | // - url is special and c is U+005C (\) | 658 | // - url is special and c is U+005C (\) |
| 647 | // - state override is not given and c is U+003F (?) or U+0023 (#) | 659 | // - state override is not given and c is U+003F (?) or U+0023 (#) |
| 648 | // then: | 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 | // 1. If url is special and c is U+005C (\), invalid-reverse-solidus validation error. | 662 | // 1. If url is special and c is U+005C (\), invalid-reverse-solidus validation error. |
| 651 | {} | 663 | {} |
| 652 | // 2. If buffer is a double-dot URL path segment, then: | 664 | // 2. If buffer is a double-dot URL path segment, then: |
| 653 | if (isDoubleDotPathSeg(buffer.items)) { | 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 | // 1. Shorten url’s path. | 669 | // 1. Shorten url’s path. |
| 656 | // 2. If neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string to url’s path. | 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 | // > This means that for input /usr/.. the result is / and not a lack of a path. | 671 | // > This means that for input /usr/.. the result is / and not a lack of a path. |
| 658 | } | 672 | } |
| 659 | // 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. | 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 == '\\'))) { | 674 | else if (isSingleDotPathSeg(buffer.items) and (c != '/' or !(is_rsep))) { |
| 661 | // @panic("TODO"); | 675 | // =no action needed |
| 662 | } | 676 | } |
| 663 | // 4. Otherwise, if buffer is not a single-dot URL path segment, then: | 677 | // 4. Otherwise, if buffer is not a single-dot URL path segment, then: |
| 664 | else if (!isSingleDotPathSeg(buffer.items)) { | 678 | else if (!isSingleDotPathSeg(buffer.items)) { |
| 665 | // 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 (:). | 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 | // > This is a (platform-independent) Windows drive letter quirk. | 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 | buffer.items[1] = ':'; | 682 | buffer.items[1] = ':'; |
| 669 | } | 683 | } |
| 670 | // 2. Append buffer to url’s path. | 684 | // 2. Append buffer to url’s path. |
| | 685 | // if (!std.mem.endsWith(u8, href.items(10), "/")) try href.appendSlice(10, "/"); |
| 671 | try href.appendSlice(10, buffer.items); | 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 | // 5. Set buffer to the empty string. | 690 | // 5. Set buffer to the empty string. |
| 674 | buffer.clearRetainingCapacity(); | 691 | buffer.clearRetainingCapacity(); |
| ... | @@ -808,6 +825,7 @@ pub const URL = struct { | ... | @@ -808,6 +825,7 @@ pub const URL = struct { |
| 808 | .hostname_kind = hostname_kind, | 825 | .hostname_kind = hostname_kind, |
| 809 | .port = _href[extras.sum(usize, href.lengths[0..9])..][0..href.lengths[9]], | 826 | .port = _href[extras.sum(usize, href.lengths[0..9])..][0..href.lengths[9]], |
| 810 | .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])], | 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 | .search = if (href.lengths[12] == 0) "" else _href[extras.sum(usize, href.lengths[0..11])..][0..extras.sum(usize, href.lengths[11..][0..2])], | 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 | .hash = if (href.lengths[14] == 0) "" else _href[extras.sum(usize, href.lengths[0..13])..][0..extras.sum(usize, href.lengths[13..][0..2])], | 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,6 +1363,7 @@ fn is_query_percent_char(c: u8) bool { |
| 1345 | /// https://url.spec.whatwg.org/#path-percent-encode-set | 1363 | /// https://url.spec.whatwg.org/#path-percent-encode-set |
| 1346 | fn is_path_percent_char(c: u8) bool { | 1364 | fn is_path_percent_char(c: u8) bool { |
| 1347 | if (c == '?') return true; | 1365 | if (c == '?') return true; |
| | 1366 | if (c == '^') return true; |
| 1348 | if (c == '`') return true; | 1367 | if (c == '`') return true; |
| 1349 | if (c == '{') return true; | 1368 | if (c == '{') return true; |
| 1350 | if (c == '}') return true; | 1369 | if (c == '}') return true; |
| ... | @@ -1371,7 +1390,7 @@ fn is_userinfo_percent_char(c: u8) bool { | ... | @@ -1371,7 +1390,7 @@ fn is_userinfo_percent_char(c: u8) bool { |
| 1371 | if (c == ';') return true; | 1390 | if (c == ';') return true; |
| 1372 | if (c == '=') return true; | 1391 | if (c == '=') return true; |
| 1373 | if (c == '@') return true; | 1392 | if (c == '@') return true; |
| 1374 | if (c >= '[' and c <= '^') return true; | 1393 | if (c >= '[' and c <= ']') return true; |
| 1375 | if (c == '|') return true; | 1394 | if (c == '|') return true; |
| 1376 | return is_path_percent_char(c); | 1395 | return is_path_percent_char(c); |
| 1377 | } | 1396 | } |
| ... | @@ -1494,6 +1513,23 @@ fn setHost(href: *ManyArrayList(15, u8), h: URL.Host) !void { | ... | @@ -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 | pub fn ManyArrayList(N: usize, T: type) type { | 1534 | pub fn ManyArrayList(N: usize, T: type) type { |
| 1499 | return struct { | 1535 | return struct { |
| ... | @@ -1540,5 +1576,13 @@ pub fn ManyArrayList(N: usize, T: type) type { | ... | @@ -1540,5 +1576,13 @@ pub fn ManyArrayList(N: usize, T: type) type { |
| 1540 | try self.list.insertSlice(real_n, slice); | 1576 | try self.list.insertSlice(real_n, slice); |
| 1541 | self.lengths[n] += slice.len; | 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 | } |