| ... | @@ -557,8 +557,8 @@ pub fn formatIntValue( | ... | @@ -557,8 +557,8 @@ pub fn formatIntValue( |
| 557 | @compileError("Cannot escape character with more than 8 bits"); | 557 | @compileError("Cannot escape character with more than 8 bits"); |
| 558 | } | 558 | } |
| 559 | } else if (comptime std.mem.eql(u8, fmt, "u")) { | 559 | } else if (comptime std.mem.eql(u8, fmt, "u")) { |
| 560 | if (@TypeOf(int_value).bit_count <= 32) { | 560 | if (@typeInfo(@TypeOf(int_value)).Int.bits <= 21) { |
| 561 | return formatUtf8Codepoint(@as(u32, int_value), options, context, Errors, output); | 561 | return formatUnicodeCodepoint(@as(u21, int_value), options, writer); |
| 562 | } else { | 562 | } else { |
| 563 | @compileError("Cannot print integer that is larger than 32 bits as an UTF-8 sequence"); | 563 | @compileError("Cannot print integer that is larger than 32 bits as an UTF-8 sequence"); |
| 564 | } | 564 | } |
| ... | @@ -648,16 +648,22 @@ pub fn formatAsciiChar( | ... | @@ -648,16 +648,22 @@ pub fn formatAsciiChar( |
| 648 | return writer.writeAll(@as(*const [1]u8, &c)); | 648 | return writer.writeAll(@as(*const [1]u8, &c)); |
| 649 | } | 649 | } |
| 650 | | 650 | |
| 651 | pub fn formatUtf8Codepoint( | 651 | pub fn formatUnicodeCodepoint( |
| 652 | c: u32, | 652 | c: u21, |
| 653 | options: FormatOptions, | 653 | options: FormatOptions, |
| 654 | context: anytype, | 654 | writer: anytype, |
| 655 | comptime Errors: type, | 655 | ) !void { |
| 656 | output: fn (@TypeOf(context), []const u8) Errors!void, | | |
| 657 | ) Errors!void { | | |
| 658 | var buf: [4]u8 = undefined; | 656 | var buf: [4]u8 = undefined; |
| 659 | const len = std.unicode.utf8Encode(c, buf[0..]) catch unreachable; | 657 | // In case of error output the replacement char U+FFFD |
| 660 | return output(context, @as(*const [4]u8, &buf)[0..len]); | 658 | const len = std.unicode.utf8Encode(@truncate(u21, c), &buf) catch |err| switch (err) { |
| | 659 | error.Utf8CannotEncodeSurrogateHalf => { |
| | 660 | return writer.writeAll(&[_]u8{ 0xef, 0xbf, 0xbd }); |
| | 661 | }, |
| | 662 | error.CodepointTooLarge => { |
| | 663 | return writer.writeAll(&[_]u8{ 0xef, 0xbf, 0xbd }); |
| | 664 | }, |
| | 665 | }; |
| | 666 | return writer.writeAll(buf[0..len]); |
| 661 | } | 667 | } |
| 662 | | 668 | |
| 663 | pub fn formatBuf( | 669 | pub fn formatBuf( |
| ... | @@ -1409,9 +1415,17 @@ test "int.specifier" { | ... | @@ -1409,9 +1415,17 @@ test "int.specifier" { |
| 1409 | try testFmt("UTF-8: a\n", "UTF-8: {u}\n", .{value}); | 1415 | try testFmt("UTF-8: a\n", "UTF-8: {u}\n", .{value}); |
| 1410 | } | 1416 | } |
| 1411 | { | 1417 | { |
| 1412 | const value: u32 = 0x1F310; | 1418 | const value: u21 = 0x1F310; |
| 1413 | try testFmt("UTF-8: 🌐\n", "UTF-8: {u}\n", .{value}); | 1419 | try testFmt("UTF-8: 🌐\n", "UTF-8: {u}\n", .{value}); |
| 1414 | } | 1420 | } |
| | 1421 | { |
| | 1422 | const value: u21 = 0xD800; |
| | 1423 | try testFmt("UTF-8: �\n", "UTF-8: {u}\n", .{value}); |
| | 1424 | } |
| | 1425 | { |
| | 1426 | const value: u21 = 0x110001; |
| | 1427 | try testFmt("UTF-8: �\n", "UTF-8: {u}\n", .{value}); |
| | 1428 | } |
| 1415 | } | 1429 | } |
| 1416 | | 1430 | |
| 1417 | test "int.padded" { | 1431 | test "int.padded" { |