| ... | @@ -76,6 +76,7 @@ fn peekIsAlign(comptime fmt: []const u8) bool { | ... | @@ -76,6 +76,7 @@ fn peekIsAlign(comptime fmt: []const u8) bool { |
| 76 | /// - `b`: output integer value in binary notation | 76 | /// - `b`: output integer value in binary notation |
| 77 | /// - `o`: output integer value in octal notation | 77 | /// - `o`: output integer value in octal notation |
| 78 | /// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max. | 78 | /// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max. |
| | 79 | /// - `u`: output integer as an UTF-8 sequence. Integer type must have 32 bits at max. |
| 79 | /// - `*`: output the address of the value instead of the value itself. | 80 | /// - `*`: output the address of the value instead of the value itself. |
| 80 | /// | 81 | /// |
| 81 | /// If a formatted user type contains a function of the type | 82 | /// If a formatted user type contains a function of the type |
| ... | @@ -555,6 +556,12 @@ pub fn formatIntValue( | ... | @@ -555,6 +556,12 @@ pub fn formatIntValue( |
| 555 | } else { | 556 | } else { |
| 556 | @compileError("Cannot escape character with more than 8 bits"); | 557 | @compileError("Cannot escape character with more than 8 bits"); |
| 557 | } | 558 | } |
| | 559 | } else if (comptime std.mem.eql(u8, fmt, "u")) { |
| | 560 | if (@TypeOf(int_value).bit_count <= 32) { |
| | 561 | return formatUtf8Codepoint(@as(u32, int_value), options, context, Errors, output); |
| | 562 | } else { |
| | 563 | @compileError("Cannot print integer that is larger than 32 bits as an UTF-8 sequence"); |
| | 564 | } |
| 558 | } else if (comptime std.mem.eql(u8, fmt, "b")) { | 565 | } else if (comptime std.mem.eql(u8, fmt, "b")) { |
| 559 | radix = 2; | 566 | radix = 2; |
| 560 | uppercase = false; | 567 | uppercase = false; |
| ... | @@ -641,6 +648,18 @@ pub fn formatAsciiChar( | ... | @@ -641,6 +648,18 @@ pub fn formatAsciiChar( |
| 641 | return writer.writeAll(@as(*const [1]u8, &c)); | 648 | return writer.writeAll(@as(*const [1]u8, &c)); |
| 642 | } | 649 | } |
| 643 | | 650 | |
| | 651 | pub fn formatUtf8Codepoint( |
| | 652 | c: u32, |
| | 653 | options: FormatOptions, |
| | 654 | context: anytype, |
| | 655 | comptime Errors: type, |
| | 656 | output: fn (@TypeOf(context), []const u8) Errors!void, |
| | 657 | ) Errors!void { |
| | 658 | var buf: [4]u8 = undefined; |
| | 659 | const len = std.unicode.utf8Encode(c, buf[0..]) catch unreachable; |
| | 660 | return output(context, @as(*const [4]u8, &buf)[0..len]); |
| | 661 | } |
| | 662 | |
| 644 | pub fn formatBuf( | 663 | pub fn formatBuf( |
| 645 | buf: []const u8, | 664 | buf: []const u8, |
| 646 | options: FormatOptions, | 665 | options: FormatOptions, |
| ... | @@ -1385,6 +1404,14 @@ test "int.specifier" { | ... | @@ -1385,6 +1404,14 @@ test "int.specifier" { |
| 1385 | const value: u16 = 0o1234; | 1404 | const value: u16 = 0o1234; |
| 1386 | try testFmt("u16: 0o1234\n", "u16: 0o{o}\n", .{value}); | 1405 | try testFmt("u16: 0o1234\n", "u16: 0o{o}\n", .{value}); |
| 1387 | } | 1406 | } |
| | 1407 | { |
| | 1408 | const value: u8 = 'a'; |
| | 1409 | try testFmt("UTF-8: a\n", "UTF-8: {u}\n", .{value}); |
| | 1410 | } |
| | 1411 | { |
| | 1412 | const value: u32 = 0x1F310; |
| | 1413 | try testFmt("UTF-8: 🌐\n", "UTF-8: {u}\n", .{value}); |
| | 1414 | } |
| 1388 | } | 1415 | } |
| 1389 | | 1416 | |
| 1390 | test "int.padded" { | 1417 | test "int.padded" { |