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