| ... | ... | @@ -10,6 +10,8 @@ pub const URL = struct { |
| 10 | 10 | protocol: []const u8, |
| 11 | 11 | username: []const u8, |
| 12 | 12 | password: []const u8, |
| 13 | hostname: []const u8, |
| 14 | hostname_kind: HostKind = .name, |
| 13 | 15 | port: []const u8, |
| 14 | 16 | search: []const u8, |
| 15 | 17 | hash: []const u8, |
| ... | ... | @@ -83,6 +85,7 @@ pub const URL = struct { |
| 83 | 85 | |
| 84 | 86 | var href = ManyArrayList(8 + 7, u8).init(alloc); |
| 85 | 87 | defer href.deinit(); |
| 88 | var hostname_kind: HostKind = .name; |
| 86 | 89 | // scheme |
| 87 | 90 | // : |
| 88 | 91 | // // |
| ... | ... | @@ -90,8 +93,7 @@ pub const URL = struct { |
| 90 | 93 | // : |
| 91 | 94 | // password |
| 92 | 95 | // @ |
| 93 | | var host: Host = .{ .name = "" }; |
| 94 | | defer if (host == .name) alloc.free(host.name); |
| 96 | // hostname |
| 95 | 97 | // : |
| 96 | 98 | // port |
| 97 | 99 | // path |
| ... | ... | @@ -391,9 +393,10 @@ pub const URL = struct { |
| 391 | 393 | // 3. Let host be the result of host parsing buffer with url is not special. |
| 392 | 394 | // 4. If host is failure, then return failure. |
| 393 | 395 | const h = try parseHost(alloc, buffer.items, !isSchemeSpecial(href.items(0))); |
| 396 | defer if (h == .name) alloc.free(h.name); |
| 394 | 397 | // 5. Set url’s host to host, buffer to the empty string, and state to port state. |
| 395 | | // try href.set(7, h); |
| 396 | | host = h; |
| 398 | try setHost(&href, h); |
| 399 | hostname_kind = h; |
| 397 | 400 | buffer.clearRetainingCapacity(); |
| 398 | 401 | state = .port; |
| 399 | 402 | } |
| ... | ... | @@ -412,9 +415,10 @@ pub const URL = struct { |
| 412 | 415 | // 3. Let host be the result of host parsing buffer with url is not special. |
| 413 | 416 | // 4. If host is failure, then return failure. |
| 414 | 417 | const h = try parseHost(alloc, buffer.items, !isSchemeSpecial(href.items(0))); |
| 418 | defer if (h == .name) alloc.free(h.name); |
| 415 | 419 | // 5. Set url’s host to host, buffer to the empty string, and state to path start state. |
| 416 | | // try href.set(7, h); |
| 417 | | host = h; |
| 420 | try setHost(&href, h); |
| 421 | hostname_kind = h; |
| 418 | 422 | buffer.clearRetainingCapacity(); |
| 419 | 423 | state = .path_start; |
| 420 | 424 | // 6. If state override is given, then return. |
| ... | ... | @@ -573,14 +577,15 @@ pub const URL = struct { |
| 573 | 577 | // 1. Let host be the result of host parsing buffer with url is not special. |
| 574 | 578 | // 2. If host is failure, then return failure. |
| 575 | 579 | var h = try parseHost(alloc, buffer.items, !isSchemeSpecial(href.items(0))); |
| 580 | defer if (h == .name) alloc.free(h.name); |
| 576 | 581 | // 3. If host is "localhost", then set host to the empty string. |
| 577 | 582 | if (h == .name and std.mem.eql(u8, h.name, "localhost")) { |
| 578 | 583 | alloc.free(h.name); |
| 579 | 584 | h = .{ .name = "" }; |
| 580 | 585 | } |
| 581 | 586 | // 4. Set url’s host to host. |
| 582 | | // try href.set(7, h); |
| 583 | | host = h; |
| 587 | try setHost(&href, h); |
| 588 | hostname_kind = h; |
| 584 | 589 | // 5. If state override is given, then return. |
| 585 | 590 | if (state_override != null) break; |
| 586 | 591 | // 6. Set buffer to the empty string and state to path start state. |
| ... | ... | @@ -797,6 +802,8 @@ pub const URL = struct { |
| 797 | 802 | .protocol = _href[0..extras.sum(usize, href.lengths[0..2])], |
| 798 | 803 | .username = _href[extras.sum(usize, href.lengths[0..2])..][0..href.lengths[3]], |
| 799 | 804 | .password = _href[extras.sum(usize, href.lengths[0..4])..][0..href.lengths[5]], |
| 805 | .hostname = _href[extras.sum(usize, href.lengths[0..7])..][0..href.lengths[7]], |
| 806 | .hostname_kind = hostname_kind, |
| 800 | 807 | .port = _href[extras.sum(usize, href.lengths[0..8])..][0..href.lengths[9]], |
| 801 | 808 | .search = if (href.lengths[12] == 0) "" else _href[extras.sum(usize, href.lengths[0..11])..][0..extras.sum(usize, href.lengths[11..][0..2])], |
| 802 | 809 | .hash = if (href.lengths[14] == 0) "" else _href[extras.sum(usize, href.lengths[0..13])..][0..extras.sum(usize, href.lengths[13..][0..2])], |
| ... | ... | @@ -1068,7 +1075,7 @@ fn parseHostOpaque(allocator: std.mem.Allocator, input: []const u8) ![]const u8 |
| 1068 | 1075 | // 3. If input contains a U+0025 (%) and the two code points following it are not ASCII hex digits, invalid-URL-unit validation error. |
| 1069 | 1076 | {} |
| 1070 | 1077 | // 4. Return the result of running UTF-8 percent-encode on input using the C0 control percent-encode set. |
| 1071 | | return percentEncode(allocator, input, is_c0control); |
| 1078 | return percentEncode(allocator, input, is_c0control_percent_char); |
| 1072 | 1079 | } |
| 1073 | 1080 | |
| 1074 | 1081 | /// https://url.spec.whatwg.org/#utf-8-percent-encode |
| ... | ... | @@ -1190,7 +1197,7 @@ fn parseIPv4(input: []const u8) !u32 { |
| 1190 | 1197 | numbers_len -= 1; |
| 1191 | 1198 | // 11. Let counter be 0. |
| 1192 | 1199 | // 12. For each n of numbers: |
| 1193 | | for (0..numbers_len, 0..) |counter, n| { |
| 1200 | for (numbers[0..numbers_len], 0..) |n, counter| { |
| 1194 | 1201 | // 1. Increment ipv4 by n × 256^(3 − counter). |
| 1195 | 1202 | ipv4 += @as(u32, @intCast(n)) * std.math.pow(u32, 256, 3 - @as(u8, @intCast(counter))); |
| 1196 | 1203 | // 2. Increment counter by 1. |
| ... | ... | @@ -1435,6 +1442,55 @@ fn percentEncodeML(list: *ManyArrayList(15, u8), n: usize, input: []const u8, co |
| 1435 | 1442 | } |
| 1436 | 1443 | } |
| 1437 | 1444 | } |
| 1445 | fn setHost(href: *ManyArrayList(15, u8), h: URL.Host) !void { |
| 1446 | switch (h) { |
| 1447 | .name => { |
| 1448 | try href.set(7, h.name); |
| 1449 | }, |
| 1450 | .ipv4 => { |
| 1451 | const bytes: [4]u8 = @bitCast(h.ipv4); |
| 1452 | try href.print(7, "{d}.{d}.{d}.{d}", .{ bytes[3], bytes[2], bytes[1], bytes[0] }); |
| 1453 | }, |
| 1454 | .ipv6 => { |
| 1455 | const pieces: [8]u16 = @bitCast(h.ipv6); |
| 1456 | try href.appendSlice(7, "["); |
| 1457 | const compress = blk: { |
| 1458 | var longest: struct { ?usize, u8 } = .{ null, 1 }; |
| 1459 | var found: struct { ?usize, u8 } = .{ null, 0 }; |
| 1460 | for (&pieces, 0..) |piece, pieceIndex| { |
| 1461 | if (piece != 0) { |
| 1462 | if (found[1] > longest[1]) { |
| 1463 | longest[0] = found[0]; |
| 1464 | longest[1] = found[1]; |
| 1465 | } |
| 1466 | found[0] = null; |
| 1467 | found[1] = 0; |
| 1468 | } else { |
| 1469 | if (found[0] == null) found[0] = pieceIndex; |
| 1470 | found[1] += 1; |
| 1471 | } |
| 1472 | } |
| 1473 | if (found[1] > longest[1]) break :blk found[0]; |
| 1474 | break :blk longest[0]; |
| 1475 | }; |
| 1476 | var ignore0 = false; |
| 1477 | for (&pieces, 0..) |piece, pieceIndex| { |
| 1478 | if (ignore0) { |
| 1479 | if (piece == 0) continue; |
| 1480 | ignore0 = false; |
| 1481 | } |
| 1482 | if (compress == pieceIndex) { |
| 1483 | try href.appendSlice(7, if (pieceIndex == 0) "::" else ":"); |
| 1484 | ignore0 = true; |
| 1485 | continue; |
| 1486 | } |
| 1487 | try href.print(7, "{x}", .{piece}); |
| 1488 | if (pieceIndex != 7) try href.appendSlice(7, ":"); |
| 1489 | } |
| 1490 | try href.appendSlice(7, "]"); |
| 1491 | }, |
| 1492 | } |
| 1493 | } |
| 1438 | 1494 | |
| 1439 | 1495 | pub fn ManyArrayList(N: usize, T: type) type { |
| 1440 | 1496 | return struct { |