@@ -710,6 +710,87 @@ fn formatFloatValue(
710
710
return formatBuf (buf_stream .getWritten (), options , writer );
711
711
}
712
712
713
+ fn formatSliceHexImpl (comptime uppercase : bool ) type {
714
+ const charset = "0123456789" ++ if (uppercase ) "ABCDEF" else "abcdef" ;
715
+
716
+ return struct {
717
+ pub fn f (
718
+ bytes : []const u8 ,
719
+ comptime fmt : []const u8 ,
720
+ options : std.fmt.FormatOptions ,
721
+ writer : anytype ,
722
+ ) ! void {
723
+ var buf : [2 ]u8 = undefined ;
724
+
725
+ for (bytes ) | c | {
726
+ buf [0 ] = charset [c >> 4 ];
727
+ buf [1 ] = charset [c & 15 ];
728
+ try writer .writeAll (& buf );
729
+ }
730
+ }
731
+ };
732
+ }
733
+
734
+ const formatSliceHexLower = formatSliceHexImpl (false ).f ;
735
+ const formatSliceHexUpper = formatSliceHexImpl (true ).f ;
736
+
737
+ /// Return a Formatter for a []const u8 where every byte is formatted as a pair
738
+ /// of lowercase hexadecimal digits.
739
+ pub fn fmtSliceHexLower (bytes : []const u8 ) std.fmt.Formatter (formatSliceHexLower ) {
740
+ return .{ .data = bytes };
741
+ }
742
+
743
+ /// Return a Formatter for a []const u8 where every byte is formatted as a pair
744
+ /// of uppercase hexadecimal digits.
745
+ pub fn fmtSliceHexUpper (bytes : []const u8 ) std.fmt.Formatter (formatSliceHexUpper ) {
746
+ return .{ .data = bytes };
747
+ }
748
+
749
+ fn formatSliceEscapeImpl (comptime uppercase : bool ) type {
750
+ const charset = "0123456789" ++ if (uppercase ) "ABCDEF" else "abcdef" ;
751
+
752
+ return struct {
753
+ pub fn f (
754
+ bytes : []const u8 ,
755
+ comptime fmt : []const u8 ,
756
+ options : std.fmt.FormatOptions ,
757
+ writer : anytype ,
758
+ ) ! void {
759
+ var buf : [4 ]u8 = undefined ;
760
+
761
+ buf [0 ] = '\\ ' ;
762
+ buf [1 ] = 'x' ;
763
+
764
+ for (bytes ) | c | {
765
+ if (std .ascii .isPrint (c )) {
766
+ try writer .writeByte (c );
767
+ } else {
768
+ buf [2 ] = charset [c >> 4 ];
769
+ buf [3 ] = charset [c & 15 ];
770
+ try writer .writeAll (& buf );
771
+ }
772
+ }
773
+ }
774
+ };
775
+ }
776
+
777
+ const formatSliceEscapeLower = formatSliceEscapeImpl (false ).f ;
778
+ const formatSliceEscapeUpper = formatSliceEscapeImpl (true ).f ;
779
+
780
+ /// Return a Formatter for a []const u8 where every non-printable ASCII
781
+ /// character is escaped as \xNN, where NN is the character in lowercase
782
+ /// hexadecimal notation.
783
+ pub fn fmtSliceEscapeLower (bytes : []const u8 ) std.fmt.Formatter (formatSliceEscapeLower ) {
784
+ return .{ .data = bytes };
785
+ }
786
+
787
+ /// Return a Formatter for a []const u8 where every non-printable ASCII
788
+ /// character is escaped as \xNN, where NN is the character in uppercase
789
+ /// hexadecimal notation.
790
+ pub fn fmtSliceEscapeUpper (bytes : []const u8 ) std.fmt.Formatter (formatSliceEscapeUpper ) {
791
+ return .{ .data = bytes };
792
+ }
793
+
713
794
pub fn formatText (
714
795
bytes : []const u8 ,
715
796
comptime fmt : []const u8 ,
@@ -718,21 +799,18 @@ pub fn formatText(
718
799
) ! void {
719
800
if (comptime std .mem .eql (u8 , fmt , "s" )) {
720
801
return formatBuf (bytes , options , writer );
721
- } else if (comptime (std .mem .eql (u8 , fmt , "x" ) or std .mem .eql (u8 , fmt , "X" ))) {
722
- for (bytes ) | c | {
723
- try formatInt (c , 16 , fmt [0 ] == 'X' , FormatOptions { .width = 2 , .fill = '0' }, writer );
724
- }
725
- return ;
726
- } else if (comptime (std .mem .eql (u8 , fmt , "e" ) or std .mem .eql (u8 , fmt , "E" ))) {
727
- for (bytes ) | c | {
728
- if (std .ascii .isPrint (c )) {
729
- try writer .writeByte (c );
730
- } else {
731
- try writer .writeAll ("\\ x" );
732
- try formatInt (c , 16 , fmt [0 ] == 'E' , FormatOptions { .width = 2 , .fill = '0' }, writer );
733
- }
734
- }
735
- return ;
802
+ } else if (comptime (std .mem .eql (u8 , fmt , "x" ))) {
803
+ @compileError ("specifier 'x' has been deprecated, wrap your argument in std.fmt.fmtSliceHexLower instead" );
804
+ } else if (comptime (std .mem .eql (u8 , fmt , "X" ))) {
805
+ @compileError ("specifier 'X' has been deprecated, wrap your argument in std.fmt.fmtSliceHexUpper instead" );
806
+ } else if (comptime (std .mem .eql (u8 , fmt , "e" ))) {
807
+ @compileError ("specifier 'e' has been deprecated, wrap your argument in std.fmt.fmtSliceEscapeLower instead" );
808
+ } else if (comptime (std .mem .eql (u8 , fmt , "E" ))) {
809
+ @compileError ("specifier 'X' has been deprecated, wrap your argument in std.fmt.fmtSliceEscapeUpper instead" );
810
+ } else if (comptime std .mem .eql (u8 , fmt , "z" )) {
811
+ @compileError ("specifier 'z' has been deprecated, wrap your argument in std.zig.fmtId instead" );
812
+ } else if (comptime std .mem .eql (u8 , fmt , "Z" )) {
813
+ @compileError ("specifier 'Z' has been deprecated, wrap your argument in std.zig.fmtEscapes instead" );
736
814
} else {
737
815
@compileError ("Unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName (@TypeOf (value )) ++ "'" );
738
816
}
@@ -1695,9 +1773,9 @@ test "slice" {
1695
1773
}
1696
1774
1697
1775
test "escape non-printable" {
1698
- try expectFmt ("abc" , "{e }" , .{"abc" });
1699
- try expectFmt ("ab\\ xffc" , "{e }" , .{"ab\xff c" });
1700
- try expectFmt ("ab\\ xFFc" , "{E }" , .{"ab\xff c" });
1776
+ try expectFmt ("abc" , "{s }" , .{fmtSliceEscapeLower ( "abc" ) });
1777
+ try expectFmt ("ab\\ xffc" , "{s }" , .{fmtSliceEscapeLower ( "ab\xff c" ) });
1778
+ try expectFmt ("ab\\ xFFc" , "{s }" , .{fmtSliceEscapeUpper ( "ab\xff c" ) });
1701
1779
}
1702
1780
1703
1781
test "pointer" {
@@ -1970,13 +2048,13 @@ test "struct.zero-size" {
1970
2048
1971
2049
test "bytes.hex" {
1972
2050
const some_bytes = "\xCA\xFE\xBA\xBE " ;
1973
- try expectFmt ("lowercase: cafebabe\n " , "lowercase: {x}\n " , .{some_bytes });
1974
- try expectFmt ("uppercase: CAFEBABE\n " , "uppercase: {X}\n " , .{some_bytes });
2051
+ try expectFmt ("lowercase: cafebabe\n " , "lowercase: {x}\n " , .{fmtSliceHexLower ( some_bytes ) });
2052
+ try expectFmt ("uppercase: CAFEBABE\n " , "uppercase: {X}\n " , .{fmtSliceHexUpper ( some_bytes ) });
1975
2053
//Test Slices
1976
- try expectFmt ("uppercase: CAFE\n " , "uppercase: {X}\n " , .{some_bytes [0.. 2]});
1977
- try expectFmt ("lowercase: babe\n " , "lowercase: {x}\n " , .{some_bytes [2.. ]});
2054
+ try expectFmt ("uppercase: CAFE\n " , "uppercase: {X}\n " , .{fmtSliceHexUpper ( some_bytes [0.. 2]) });
2055
+ try expectFmt ("lowercase: babe\n " , "lowercase: {x}\n " , .{fmtSliceHexLower ( some_bytes [2.. ]) });
1978
2056
const bytes_with_zeros = "\x00\x0E\xBA\xBE " ;
1979
- try expectFmt ("lowercase: 000ebabe\n " , "lowercase: {x}\n " , .{bytes_with_zeros });
2057
+ try expectFmt ("lowercase: 000ebabe\n " , "lowercase: {x}\n " , .{fmtSliceHexLower ( bytes_with_zeros ) });
1980
2058
}
1981
2059
1982
2060
pub const trim = @compileError ("deprecated; use std.mem.trim with std.ascii.spaces instead" );
@@ -2004,9 +2082,9 @@ pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 {
2004
2082
2005
2083
test "hexToBytes" {
2006
2084
var buf : [32 ]u8 = undefined ;
2007
- try expectFmt ("90" ** 32 , "{X }" , .{try hexToBytes (& buf , "90" ** 32 )});
2008
- try expectFmt ("ABCD" , "{X }" , .{try hexToBytes (& buf , "ABCD" )});
2009
- try expectFmt ("" , "{X }" , .{try hexToBytes (& buf , "" )});
2085
+ try expectFmt ("90" ** 32 , "{s }" , .{fmtSliceHexUpper ( try hexToBytes (& buf , "90" ** 32 ) )});
2086
+ try expectFmt ("ABCD" , "{s }" , .{fmtSliceHexUpper ( try hexToBytes (& buf , "ABCD" ) )});
2087
+ try expectFmt ("" , "{s }" , .{fmtSliceHexUpper ( try hexToBytes (& buf , "" ) )});
2010
2088
std .testing .expectError (error .InvalidCharacter , hexToBytes (& buf , "012Z" ));
2011
2089
std .testing .expectError (error .InvalidLength , hexToBytes (& buf , "AAA" ));
2012
2090
std .testing .expectError (error .NoSpaceLeft , hexToBytes (buf [0.. 1], "ABAB" ));
0 commit comments