| ... | @@ -2,6 +2,7 @@ const std = @import("std"); | ... | @@ -2,6 +2,7 @@ const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const extras = @import("extras"); | 3 | const extras = @import("extras"); |
| 4 | const unicode_idna = @import("unicode-idna"); | 4 | const unicode_idna = @import("unicode-idna"); |
| | 5 | const nio = @import("nio"); |
| 5 | | 6 | |
| 6 | pub const URL = struct { | 7 | pub const URL = struct { |
| 7 | href: []const u8, | 8 | href: []const u8, |
| ... | @@ -47,13 +48,13 @@ pub const URL = struct { | ... | @@ -47,13 +48,13 @@ pub const URL = struct { |
| 47 | // input is a scalar value string | 48 | // input is a scalar value string |
| 48 | if (!std.unicode.utf8ValidateSlice(input)) return error.InvalidURL; | 49 | if (!std.unicode.utf8ValidateSlice(input)) return error.InvalidURL; |
| 49 | | 50 | |
| 50 | var inputl = std.array_list.Managed(u8).init(alloc); | 51 | var inputl = nio.AllocatingWriter.init(alloc); |
| 51 | defer inputl.deinit(); | 52 | defer inputl.deinit(); |
| 52 | try inputl.appendSlice(input); | 53 | try inputl.writeAll(input); |
| 53 | | 54 | |
| 54 | // 1. | 55 | // 1. |
| 55 | while (inputl.items.len > 0 and is_c0control_or_space(inputl.items[0])) _ = inputl.orderedRemove(0); | 56 | while (inputl.items.len > 0 and is_c0control_or_space(inputl.items[0])) _ = inputl.orderedRemove(0); |
| 56 | while (inputl.items.len > 0 and is_c0control_or_space(inputl.getLast())) inputl.items.len -= 1; | 57 | while (inputl.items.len > 0 and is_c0control_or_space(inputl.last())) inputl.items.len -= 1; |
| 57 | | 58 | |
| 58 | // 3. | 59 | // 3. |
| 59 | while (std.mem.indexOfScalar(u8, inputl.items, '\t')) |i| _ = inputl.orderedRemove(i); | 60 | while (std.mem.indexOfScalar(u8, inputl.items, '\t')) |i| _ = inputl.orderedRemove(i); |
| ... | @@ -70,7 +71,7 @@ pub const URL = struct { | ... | @@ -70,7 +71,7 @@ pub const URL = struct { |
| 70 | // we always do utf-8 | 71 | // we always do utf-8 |
| 71 | | 72 | |
| 72 | // 6. | 73 | // 6. |
| 73 | var buffer = std.array_list.Managed(u8).init(alloc); | 74 | var buffer = nio.AllocatingWriter.init(alloc); |
| 74 | defer buffer.deinit(); | 75 | defer buffer.deinit(); |
| 75 | | 76 | |
| 76 | // 7. | 77 | // 7. |
| ... | @@ -118,7 +119,7 @@ pub const URL = struct { | ... | @@ -118,7 +119,7 @@ pub const URL = struct { |
| 118 | std.debug.assert(pointer == 0); | 119 | std.debug.assert(pointer == 0); |
| 119 | // 1. If c is an ASCII alpha, append c, lowercased, to buffer, and set state to scheme state. | 120 | // 1. If c is an ASCII alpha, append c, lowercased, to buffer, and set state to scheme state. |
| 120 | if (i < length and std.ascii.isAlphabetic(c)) { | 121 | if (i < length and std.ascii.isAlphabetic(c)) { |
| 121 | try buffer.append(std.ascii.toLower(c)); | 122 | try buffer.writeAll(&.{std.ascii.toLower(c)}); |
| 122 | state = .scheme; | 123 | state = .scheme; |
| 123 | } | 124 | } |
| 124 | // 2. Otherwise, if state override is not given, set state to no scheme state and decrease pointer by 1. | 125 | // 2. Otherwise, if state override is not given, set state to no scheme state and decrease pointer by 1. |
| ... | @@ -134,7 +135,7 @@ pub const URL = struct { | ... | @@ -134,7 +135,7 @@ pub const URL = struct { |
| 134 | .scheme => { | 135 | .scheme => { |
| 135 | // 1. If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.), append c, lowercased, to buffer. | 136 | // 1. If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.), append c, lowercased, to buffer. |
| 136 | if (std.ascii.isAlphanumeric(c) or c == '+' or c == '-' or c == '.') { | 137 | if (std.ascii.isAlphanumeric(c) or c == '+' or c == '-' or c == '.') { |
| 137 | try buffer.append(std.ascii.toLower(c)); | 138 | try buffer.writeAll(&.{std.ascii.toLower(c)}); |
| 138 | } | 139 | } |
| 139 | // 2. Otherwise, if c is U+003A (:), then: | 140 | // 2. Otherwise, if c is U+003A (:), then: |
| 140 | else if (c == ':') { | 141 | else if (c == ':') { |
| ... | @@ -156,7 +157,7 @@ pub const URL = struct { | ... | @@ -156,7 +157,7 @@ pub const URL = struct { |
| 156 | // 2. Return. | 157 | // 2. Return. |
| 157 | } | 158 | } |
| 158 | // 4. Set buffer to the empty string. | 159 | // 4. Set buffer to the empty string. |
| 159 | buffer.clearRetainingCapacity(); | 160 | buffer.items.len = 0; |
| 160 | // 5. If url’s scheme is "file", then: | 161 | // 5. If url’s scheme is "file", then: |
| 161 | if (std.mem.eql(u8, href.items(0), "file")) { | 162 | if (std.mem.eql(u8, href.items(0), "file")) { |
| 162 | // 1. If remaining does not start with "//", special-scheme-missing-following-solidus validation error. | 163 | // 1. If remaining does not start with "//", special-scheme-missing-following-solidus validation error. |
| ... | @@ -192,7 +193,7 @@ pub const URL = struct { | ... | @@ -192,7 +193,7 @@ pub const URL = struct { |
| 192 | } | 193 | } |
| 193 | // 3. Otherwise, if state override is not given, set buffer to the empty string, state to no scheme state, and start over (from the first code point in input). | 194 | // 3. Otherwise, if state override is not given, set buffer to the empty string, state to no scheme state, and start over (from the first code point in input). |
| 194 | else if (state_override == null) { | 195 | else if (state_override == null) { |
| 195 | buffer.clearRetainingCapacity(); | 196 | buffer.items.len = 0; |
| 196 | state = .no_scheme; | 197 | state = .no_scheme; |
| 197 | pointer = 0; | 198 | pointer = 0; |
| 198 | i = 0; | 199 | i = 0; |
| ... | @@ -380,7 +381,7 @@ pub const URL = struct { | ... | @@ -380,7 +381,7 @@ pub const URL = struct { |
| 380 | // 1. Invalid-credentials validation error. | 381 | // 1. Invalid-credentials validation error. |
| 381 | {} | 382 | {} |
| 382 | // 2. If atSignSeen is true, then prepend "%40" to buffer. | 383 | // 2. If atSignSeen is true, then prepend "%40" to buffer. |
| 383 | if (atSignSeen) try buffer.insertSlice(0, "%40"); | 384 | if (atSignSeen) try buffer.insertAt(0, "%40"); |
| 384 | // 3. Set atSignSeen to true. | 385 | // 3. Set atSignSeen to true. |
| 385 | atSignSeen = true; | 386 | atSignSeen = true; |
| 386 | // 4. For each codePoint in buffer: | 387 | // 4. For each codePoint in buffer: |
| ... | @@ -401,7 +402,7 @@ pub const URL = struct { | ... | @@ -401,7 +402,7 @@ pub const URL = struct { |
| 401 | } | 402 | } |
| 402 | } | 403 | } |
| 403 | // 5. Set buffer to the empty string. | 404 | // 5. Set buffer to the empty string. |
| 404 | buffer.clearRetainingCapacity(); | 405 | buffer.items.len = 0; |
| 405 | } | 406 | } |
| 406 | // 2. Otherwise, if one of the following is true: | 407 | // 2. Otherwise, if one of the following is true: |
| 407 | // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#) | 408 | // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#) |
| ... | @@ -416,12 +417,12 @@ pub const URL = struct { | ... | @@ -416,12 +417,12 @@ pub const URL = struct { |
| 416 | i = lastcpi(inputl.items[0..i]); | 417 | i = lastcpi(inputl.items[0..i]); |
| 417 | c = inputl.items[i]; | 418 | c = inputl.items[i]; |
| 418 | } | 419 | } |
| 419 | buffer.clearRetainingCapacity(); | 420 | buffer.items.len = 0; |
| 420 | state = .host; | 421 | state = .host; |
| 421 | } | 422 | } |
| 422 | // 3. Otherwise, append c to buffer. | 423 | // 3. Otherwise, append c to buffer. |
| 423 | else { | 424 | else { |
| 424 | try buffer.appendSlice(inputl.items[i..][0..l(c)]); | 425 | try buffer.writeAll(inputl.items[i..][0..l(c)]); |
| 425 | } | 426 | } |
| 426 | }, | 427 | }, |
| 427 | .host, .hostname => { | 428 | .host, .hostname => { |
| ... | @@ -445,7 +446,7 @@ pub const URL = struct { | ... | @@ -445,7 +446,7 @@ pub const URL = struct { |
| 445 | // 5. Set url’s host to host, buffer to the empty string, and state to port state. | 446 | // 5. Set url’s host to host, buffer to the empty string, and state to port state. |
| 446 | try setHost(&href, h); | 447 | try setHost(&href, h); |
| 447 | hostname_kind = h; | 448 | hostname_kind = h; |
| 448 | buffer.clearRetainingCapacity(); | 449 | buffer.items.len = 0; |
| 449 | state = .port; | 450 | state = .port; |
| 450 | } | 451 | } |
| 451 | // 3. Otherwise, if one of the following is true: | 452 | // 3. Otherwise, if one of the following is true: |
| ... | @@ -467,7 +468,7 @@ pub const URL = struct { | ... | @@ -467,7 +468,7 @@ pub const URL = struct { |
| 467 | // 5. Set url’s host to host, buffer to the empty string, and state to path start state. | 468 | // 5. Set url’s host to host, buffer to the empty string, and state to path start state. |
| 468 | try setHost(&href, h); | 469 | try setHost(&href, h); |
| 469 | hostname_kind = h; | 470 | hostname_kind = h; |
| 470 | buffer.clearRetainingCapacity(); | 471 | buffer.items.len = 0; |
| 471 | state = .path_start; | 472 | state = .path_start; |
| 472 | // 6. If state override is given, then return. | 473 | // 6. If state override is given, then return. |
| 473 | if (state_override != null) break; | 474 | if (state_override != null) break; |
| ... | @@ -479,13 +480,13 @@ pub const URL = struct { | ... | @@ -479,13 +480,13 @@ pub const URL = struct { |
| 479 | // 2. If c is U+005D (]), then set insideBrackets to false. | 480 | // 2. If c is U+005D (]), then set insideBrackets to false. |
| 480 | if (c == ']') insideBrackets = false; | 481 | if (c == ']') insideBrackets = false; |
| 481 | // 3. Append c to buffer. | 482 | // 3. Append c to buffer. |
| 482 | try buffer.appendSlice(inputl.items[i..][0..l(c)]); | 483 | try buffer.writeAll(inputl.items[i..][0..l(c)]); |
| 483 | } | 484 | } |
| 484 | }, | 485 | }, |
| 485 | .port => { | 486 | .port => { |
| 486 | // 1. If c is an ASCII digit, append c to buffer. | 487 | // 1. If c is an ASCII digit, append c to buffer. |
| 487 | if (std.ascii.isDigit(c)) { | 488 | if (std.ascii.isDigit(c)) { |
| 488 | try buffer.appendSlice(inputl.items[i..][0..l(c)]); | 489 | try buffer.writeAll(inputl.items[i..][0..l(c)]); |
| 489 | } | 490 | } |
| 490 | // 2. Otherwise, if one of the following is true: | 491 | // 2. Otherwise, if one of the following is true: |
| 491 | // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#); | 492 | // - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#); |
| ... | @@ -501,7 +502,7 @@ pub const URL = struct { | ... | @@ -501,7 +502,7 @@ pub const URL = struct { |
| 501 | // 3. Set url’s port to null, if port is url’s scheme’s default port; otherwise to port. | 502 | // 3. Set url’s port to null, if port is url’s scheme’s default port; otherwise to port. |
| 502 | if (schemeDefaultPort(href.items(0)) != p) try href.print(9, "{d}", .{p}); | 503 | if (schemeDefaultPort(href.items(0)) != p) try href.print(9, "{d}", .{p}); |
| 503 | // 4. Set buffer to the empty string. | 504 | // 4. Set buffer to the empty string. |
| 504 | buffer.clearRetainingCapacity(); | 505 | buffer.items.len = 0; |
| 505 | // 5. If state override is given, then return. | 506 | // 5. If state override is given, then return. |
| 506 | if (state_override != null) break; | 507 | if (state_override != null) break; |
| 507 | } | 508 | } |
| ... | @@ -655,13 +656,13 @@ pub const URL = struct { | ... | @@ -655,13 +656,13 @@ pub const URL = struct { |
| 655 | // 5. If state override is given, then return. | 656 | // 5. If state override is given, then return. |
| 656 | if (state_override != null) break; | 657 | if (state_override != null) break; |
| 657 | // 6. Set buffer to the empty string and state to path start state. | 658 | // 6. Set buffer to the empty string and state to path start state. |
| 658 | buffer.clearRetainingCapacity(); | 659 | buffer.items.len = 0; |
| 659 | state = .path_start; | 660 | state = .path_start; |
| 660 | } | 661 | } |
| 661 | } | 662 | } |
| 662 | // 2. Otherwise, append c to buffer. | 663 | // 2. Otherwise, append c to buffer. |
| 663 | else { | 664 | else { |
| 664 | try buffer.appendSlice(inputl.items[i..][0..l(c)]); | 665 | try buffer.writeAll(inputl.items[i..][0..l(c)]); |
| 665 | } | 666 | } |
| 666 | }, | 667 | }, |
| 667 | .path_start => { | 668 | .path_start => { |
| ... | @@ -743,7 +744,7 @@ pub const URL = struct { | ... | @@ -743,7 +744,7 @@ pub const URL = struct { |
| 743 | try href.appendSlice(10, buffer.items); | 744 | try href.appendSlice(10, buffer.items); |
| 744 | } | 745 | } |
| 745 | // 5. Set buffer to the empty string. | 746 | // 5. Set buffer to the empty string. |
| 746 | buffer.clearRetainingCapacity(); | 747 | buffer.items.len = 0; |
| 747 | // 6. If c is U+003F (?), then set url’s query to the empty string and state to query state. | 748 | // 6. If c is U+003F (?), then set url’s query to the empty string and state to query state. |
| 748 | if (c == '?') { | 749 | if (c == '?') { |
| 749 | href.clear(12); | 750 | href.clear(12); |
| ... | @@ -767,9 +768,9 @@ pub const URL = struct { | ... | @@ -767,9 +768,9 @@ pub const URL = struct { |
| 767 | if (is_path_percent_char(c)) { | 768 | if (is_path_percent_char(c)) { |
| 768 | const pe = try percentEncode(alloc, inputl.items[i..][0..l(c)], is_path_percent_char); | 769 | const pe = try percentEncode(alloc, inputl.items[i..][0..l(c)], is_path_percent_char); |
| 769 | defer alloc.free(pe); | 770 | defer alloc.free(pe); |
| 770 | try buffer.appendSlice(pe); | 771 | try buffer.writeAll(pe); |
| 771 | } else { | 772 | } else { |
| 772 | try buffer.appendSlice(inputl.items[i..][0..l(c)]); | 773 | try buffer.writeAll(inputl.items[i..][0..l(c)]); |
| 773 | } | 774 | } |
| 774 | } | 775 | } |
| 775 | }, | 776 | }, |
| ... | @@ -828,7 +829,7 @@ pub const URL = struct { | ... | @@ -828,7 +829,7 @@ pub const URL = struct { |
| 828 | try percentEncodeML(&href, 12, buffer.items, is_query_percent_char); | 829 | try percentEncodeML(&href, 12, buffer.items, is_query_percent_char); |
| 829 | } | 830 | } |
| 830 | // 3. Set buffer to the empty string. | 831 | // 3. Set buffer to the empty string. |
| 831 | buffer.clearRetainingCapacity(); | 832 | buffer.items.len = 0; |
| 832 | // 4. If c is U+0023 (#), then set url’s fragment to the empty string and state to fragment state. | 833 | // 4. If c is U+0023 (#), then set url’s fragment to the empty string and state to fragment state. |
| 833 | if (c == '#') { | 834 | if (c == '#') { |
| 834 | href.clear(14); | 835 | href.clear(14); |
| ... | @@ -843,7 +844,7 @@ pub const URL = struct { | ... | @@ -843,7 +844,7 @@ pub const URL = struct { |
| 843 | // 2. If c is U+0025 (%) and remaining does not start with two ASCII hex digits, invalid-URL-unit validation error. | 844 | // 2. If c is U+0025 (%) and remaining does not start with two ASCII hex digits, invalid-URL-unit validation error. |
| 844 | {} | 845 | {} |
| 845 | // 3. Append c to buffer. | 846 | // 3. Append c to buffer. |
| 846 | try buffer.appendSlice(inputl.items[i..][0..l(c)]); | 847 | try buffer.writeAll(inputl.items[i..][0..l(c)]); |
| 847 | } | 848 | } |
| 848 | }, | 849 | }, |
| 849 | .fragment => { | 850 | .fragment => { |
| ... | @@ -1140,12 +1141,12 @@ pub const SearchParams = struct { | ... | @@ -1140,12 +1141,12 @@ pub const SearchParams = struct { |
| 1140 | | 1141 | |
| 1141 | pub fn encode(self: *const SearchParams) ![]u8 { | 1142 | pub fn encode(self: *const SearchParams) ![]u8 { |
| 1142 | const alloc = self.allocator; | 1143 | const alloc = self.allocator; |
| 1143 | var list = std.array_list.Managed(u8).init(alloc); | 1144 | var list = nio.AllocatingWriter.init(alloc); |
| 1144 | errdefer list.deinit(); | 1145 | errdefer list.deinit(); |
| 1145 | for (self.inner.items(.key), self.inner.items(.value), 0..) |k, v, i| { | 1146 | for (self.inner.items(.key), self.inner.items(.value), 0..) |k, v, i| { |
| 1146 | if (i > 0) try list.writer().writeAll("&"); | 1147 | if (i > 0) try list.writeAll("&"); |
| 1147 | try percentEncodeAL(&list, k, is_formurlencoded_percent_char); | 1148 | try percentEncodeAL(&list, k, is_formurlencoded_percent_char); |
| 1148 | try list.append('='); | 1149 | try list.writeAll("="); |
| 1149 | try percentEncodeAL(&list, v, is_formurlencoded_percent_char); | 1150 | try percentEncodeAL(&list, v, is_formurlencoded_percent_char); |
| 1150 | } | 1151 | } |
| 1151 | list.items.len -= std.mem.replace(u8, list.items, "%20", "+", list.items) * 2; | 1152 | list.items.len -= std.mem.replace(u8, list.items, "%20", "+", list.items) * 2; |
| ... | @@ -1458,7 +1459,7 @@ fn parseHostOpaque(allocator: std.mem.Allocator, input: []const u8) ![]const u8 | ... | @@ -1458,7 +1459,7 @@ fn parseHostOpaque(allocator: std.mem.Allocator, input: []const u8) ![]const u8 |
| 1458 | /// https://url.spec.whatwg.org/#utf-8-percent-encode | 1459 | /// https://url.spec.whatwg.org/#utf-8-percent-encode |
| 1459 | fn percentEncode(allocator: std.mem.Allocator, input: []const u8, comptime set: fn (u8) bool) ![]u8 { | 1460 | fn percentEncode(allocator: std.mem.Allocator, input: []const u8, comptime set: fn (u8) bool) ![]u8 { |
| 1460 | if (!extras.matchesAny(u8, input, set)) return allocator.dupe(u8, input); | 1461 | if (!extras.matchesAny(u8, input, set)) return allocator.dupe(u8, input); |
| 1461 | var result = std.array_list.Managed(u8).init(allocator); | 1462 | var result = nio.AllocatingWriter.init(allocator); |
| 1462 | errdefer result.deinit(); | 1463 | errdefer result.deinit(); |
| 1463 | try result.ensureUnusedCapacity(input.len); | 1464 | try result.ensureUnusedCapacity(input.len); |
| 1464 | try percentEncodeAL(&result, input, set); | 1465 | try percentEncodeAL(&result, input, set); |
| ... | @@ -1467,10 +1468,10 @@ fn percentEncode(allocator: std.mem.Allocator, input: []const u8, comptime set: | ... | @@ -1467,10 +1468,10 @@ fn percentEncode(allocator: std.mem.Allocator, input: []const u8, comptime set: |
| 1467 | | 1468 | |
| 1468 | /// https://url.spec.whatwg.org/#string-percent-decode | 1469 | /// https://url.spec.whatwg.org/#string-percent-decode |
| 1469 | pub fn percentDecode(allocator: std.mem.Allocator, input: []const u8) ![]u8 { | 1470 | pub fn percentDecode(allocator: std.mem.Allocator, input: []const u8) ![]u8 { |
| 1470 | var result = std.array_list.Managed(u8).init(allocator); | 1471 | var result = nio.AllocatingWriter.init(allocator); |
| 1471 | errdefer result.deinit(); | 1472 | errdefer result.deinit(); |
| 1472 | try result.ensureUnusedCapacity(input.len); | 1473 | try result.ensureUnusedCapacity(input.len); |
| 1473 | try percentDecodeW(result.writer(), input); | 1474 | try percentDecodeW(&result, input); |
| 1474 | return result.toOwnedSlice(); | 1475 | return result.toOwnedSlice(); |
| 1475 | } | 1476 | } |
| 1476 | pub fn percentDecodeW(writer: anytype, input: []const u8) !void { | 1477 | pub fn percentDecodeW(writer: anytype, input: []const u8) !void { |
| ... | @@ -1792,11 +1793,11 @@ fn lastcpi(haystack: []const u8) usize { | ... | @@ -1792,11 +1793,11 @@ fn lastcpi(haystack: []const u8) usize { |
| 1792 | while (haystack[i] & 0xC0 == 0x80) : (i -= 1) {} | 1793 | while (haystack[i] & 0xC0 == 0x80) : (i -= 1) {} |
| 1793 | return i; | 1794 | return i; |
| 1794 | } | 1795 | } |
| 1795 | pub fn percentEncodeScalarAL(list: *std.array_list.Managed(u8), cp: []const u8, comptime set: fn (u8) bool) !void { | 1796 | pub fn percentEncodeScalarAL(list: *nio.AllocatingWriter, cp: []const u8, comptime set: fn (u8) bool) !void { |
| 1796 | if (set(cp[0])) { | 1797 | if (set(cp[0])) { |
| 1797 | for (cp) |b| { | 1798 | for (cp) |b| { |
| 1798 | try list.append('%'); | 1799 | try list.append('%'); |
| 1799 | try list.writer().print("{X:0>2}", .{b}); | 1800 | try list.print("{X:0>2}", .{b}); |
| 1800 | } | 1801 | } |
| 1801 | } else { | 1802 | } else { |
| 1802 | try list.append(cp[0]); | 1803 | try list.append(cp[0]); |
| ... | @@ -1815,16 +1816,16 @@ pub fn percentEncodeW(writer: anytype, input: []const u8, comptime set: fn (u8) | ... | @@ -1815,16 +1816,16 @@ pub fn percentEncodeW(writer: anytype, input: []const u8, comptime set: fn (u8) |
| 1815 | } | 1816 | } |
| 1816 | } | 1817 | } |
| 1817 | } | 1818 | } |
| 1818 | pub fn percentEncodeAL(list: *std.array_list.Managed(u8), input: []const u8, comptime set: fn (u8) bool) !void { | 1819 | pub fn percentEncodeAL(list: *nio.AllocatingWriter, input: []const u8, comptime set: fn (u8) bool) !void { |
| 1819 | var it = std.unicode.Utf8View.initUnchecked(input).iterator(); | 1820 | var it = std.unicode.Utf8View.initUnchecked(input).iterator(); |
| 1820 | while (it.nextCodepointSlice()) |sl| { | 1821 | while (it.nextCodepointSlice()) |sl| { |
| 1821 | if (set(sl[0])) { | 1822 | if (set(sl[0])) { |
| 1822 | for (sl) |b| { | 1823 | for (sl) |b| { |
| 1823 | try list.append('%'); | 1824 | try list.writeAll("%"); |
| 1824 | try list.writer().print("{X:0>2}", .{b}); | 1825 | try list.print("{X:0>2}", .{b}); |
| 1825 | } | 1826 | } |
| 1826 | } else { | 1827 | } else { |
| 1827 | try list.appendSlice(sl); | 1828 | try list.writeAll(sl); |
| 1828 | } | 1829 | } |
| 1829 | } | 1830 | } |
| 1830 | } | 1831 | } |