@@ -13,9 +13,102 @@ use crate::{
1313} ;
1414
1515/// Byte string without UTF-8 validity guarantee.
16- ///
17- /// `BStr` is simply an alias to `[u8]`, but has a more evident semantical meaning.
18- pub type BStr = [ u8 ] ;
16+ #[ repr( transparent) ]
17+ pub struct BStr ( [ u8 ] ) ;
18+
19+ impl BStr {
20+ /// Returns the length of this string.
21+ #[ inline]
22+ pub const fn len ( & self ) -> usize {
23+ self . 0 . len ( )
24+ }
25+
26+ /// Returns `true` if the string is empty.
27+ #[ inline]
28+ pub const fn is_empty ( & self ) -> bool {
29+ self . len ( ) == 0
30+ }
31+
32+ /// Creates a [`BStr`] from a `[u8]`.
33+ #[ inline]
34+ pub const fn from_bytes ( bytes : & [ u8 ] ) -> & Self {
35+ // SAFETY: `BStr` is transparent to `[u8]`.
36+ unsafe { & * ( bytes as * const [ u8 ] as * const BStr ) }
37+ }
38+ }
39+
40+ impl fmt:: Display for BStr {
41+ /// Formats printable ASCII characters, escaping the rest.
42+ ///
43+ /// ```
44+ /// # use kernel::{fmt, b_str, str::{BStr, CString}};
45+ /// let ascii = b_str!("Hello, BStr!");
46+ /// let s = CString::try_from_fmt(fmt!("{}", ascii)).unwrap();
47+ /// assert_eq!(s.as_bytes(), "Hello, BStr!".as_bytes());
48+ ///
49+ /// let non_ascii = b_str!("🦀");
50+ /// let s = CString::try_from_fmt(fmt!("{}", non_ascii)).unwrap();
51+ /// assert_eq!(s.as_bytes(), "\\xf0\\x9f\\xa6\\x80".as_bytes());
52+ /// ```
53+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
54+ for & b in & self . 0 {
55+ match b {
56+ // Common escape codes.
57+ b'\t' => f. write_str ( "\\ t" ) ?,
58+ b'\n' => f. write_str ( "\\ n" ) ?,
59+ b'\r' => f. write_str ( "\\ r" ) ?,
60+ // Printable characters.
61+ 0x20 ..=0x7e => f. write_char ( b as char ) ?,
62+ _ => write ! ( f, "\\ x{:02x}" , b) ?,
63+ }
64+ }
65+ Ok ( ( ) )
66+ }
67+ }
68+
69+ impl fmt:: Debug for BStr {
70+ /// Formats printable ASCII characters with a double quote on either end,
71+ /// escaping the rest.
72+ ///
73+ /// ```
74+ /// # use kernel::{fmt, b_str, str::{BStr, CString}};
75+ /// // Embedded double quotes are escaped.
76+ /// let ascii = b_str!("Hello, \"BStr\"!");
77+ /// let s = CString::try_from_fmt(fmt!("{:?}", ascii)).unwrap();
78+ /// assert_eq!(s.as_bytes(), "\"Hello, \\\"BStr\\\"!\"".as_bytes());
79+ ///
80+ /// let non_ascii = b_str!("😺");
81+ /// let s = CString::try_from_fmt(fmt!("{:?}", non_ascii)).unwrap();
82+ /// assert_eq!(s.as_bytes(), "\"\\xf0\\x9f\\x98\\xba\"".as_bytes());
83+ /// ```
84+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
85+ f. write_char ( '"' ) ?;
86+ for & b in & self . 0 {
87+ match b {
88+ // Common escape codes.
89+ b'\t' => f. write_str ( "\\ t" ) ?,
90+ b'\n' => f. write_str ( "\\ n" ) ?,
91+ b'\r' => f. write_str ( "\\ r" ) ?,
92+ // String escape characters.
93+ b'\"' => f. write_str ( "\\ \" " ) ?,
94+ b'\\' => f. write_str ( "\\ \\ " ) ?,
95+ // Printable characters.
96+ 0x20 ..=0x7e => f. write_char ( b as char ) ?,
97+ _ => write ! ( f, "\\ x{:02x}" , b) ?,
98+ }
99+ }
100+ f. write_char ( '"' )
101+ }
102+ }
103+
104+ impl Deref for BStr {
105+ type Target = [ u8 ] ;
106+
107+ #[ inline]
108+ fn deref ( & self ) -> & Self :: Target {
109+ & self . 0
110+ }
111+ }
19112
20113/// Creates a new [`BStr`] from a string literal.
21114///
@@ -33,7 +126,7 @@ pub type BStr = [u8];
33126macro_rules! b_str {
34127 ( $str: literal) => { {
35128 const S : & ' static str = $str;
36- const C : & ' static $crate:: str :: BStr = S . as_bytes( ) ;
129+ const C : & ' static $crate:: str :: BStr = $crate :: str :: BStr :: from_bytes ( S . as_bytes( ) ) ;
37130 C
38131 } } ;
39132}
@@ -271,7 +364,7 @@ impl fmt::Debug for CStr {
271364impl AsRef < BStr > for CStr {
272365 #[ inline]
273366 fn as_ref ( & self ) -> & BStr {
274- self . as_bytes ( )
367+ BStr :: from_bytes ( self . as_bytes ( ) )
275368 }
276369}
277370
@@ -280,7 +373,7 @@ impl Deref for CStr {
280373
281374 #[ inline]
282375 fn deref ( & self ) -> & Self :: Target {
283- self . as_bytes ( )
376+ self . as_ref ( )
284377 }
285378}
286379
@@ -327,7 +420,7 @@ where
327420
328421 #[ inline]
329422 fn index ( & self , index : Idx ) -> & Self :: Output {
330- & self . as_bytes ( ) [ index]
423+ & self . as_ref ( ) [ index]
331424 }
332425}
333426
@@ -357,6 +450,21 @@ macro_rules! c_str {
357450#[ cfg( test) ]
358451mod tests {
359452 use super :: * ;
453+ use alloc:: format;
454+
455+ const ALL_ASCII_CHARS : & ' static str =
456+ "\\ x01\\ x02\\ x03\\ x04\\ x05\\ x06\\ x07\\ x08\\ x09\\ x0a\\ x0b\\ x0c\\ x0d\\ x0e\\ x0f\
457+ \\ x10\\ x11\\ x12\\ x13\\ x14\\ x15\\ x16\\ x17\\ x18\\ x19\\ x1a\\ x1b\\ x1c\\ x1d\\ x1e\\ x1f \
458+ !\" #$%&'()*+,-./0123456789:;<=>?@\
459+ ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\ ]^_`abcdefghijklmnopqrstuvwxyz{|}~\\ x7f\
460+ \\ x80\\ x81\\ x82\\ x83\\ x84\\ x85\\ x86\\ x87\\ x88\\ x89\\ x8a\\ x8b\\ x8c\\ x8d\\ x8e\\ x8f\
461+ \\ x90\\ x91\\ x92\\ x93\\ x94\\ x95\\ x96\\ x97\\ x98\\ x99\\ x9a\\ x9b\\ x9c\\ x9d\\ x9e\\ x9f\
462+ \\ xa0\\ xa1\\ xa2\\ xa3\\ xa4\\ xa5\\ xa6\\ xa7\\ xa8\\ xa9\\ xaa\\ xab\\ xac\\ xad\\ xae\\ xaf\
463+ \\ xb0\\ xb1\\ xb2\\ xb3\\ xb4\\ xb5\\ xb6\\ xb7\\ xb8\\ xb9\\ xba\\ xbb\\ xbc\\ xbd\\ xbe\\ xbf\
464+ \\ xc0\\ xc1\\ xc2\\ xc3\\ xc4\\ xc5\\ xc6\\ xc7\\ xc8\\ xc9\\ xca\\ xcb\\ xcc\\ xcd\\ xce\\ xcf\
465+ \\ xd0\\ xd1\\ xd2\\ xd3\\ xd4\\ xd5\\ xd6\\ xd7\\ xd8\\ xd9\\ xda\\ xdb\\ xdc\\ xdd\\ xde\\ xdf\
466+ \\ xe0\\ xe1\\ xe2\\ xe3\\ xe4\\ xe5\\ xe6\\ xe7\\ xe8\\ xe9\\ xea\\ xeb\\ xec\\ xed\\ xee\\ xef\
467+ \\ xf0\\ xf1\\ xf2\\ xf3\\ xf4\\ xf5\\ xf6\\ xf7\\ xf8\\ xf9\\ xfa\\ xfb\\ xfc\\ xfd\\ xfe\\ xff";
360468
361469 #[ test]
362470 fn test_cstr_to_str ( ) {
@@ -381,6 +489,69 @@ mod tests {
381489 let unchecked_str = unsafe { checked_cstr. as_str_unchecked ( ) } ;
382490 assert_eq ! ( unchecked_str, "🐧" ) ;
383491 }
492+
493+ #[ test]
494+ fn test_cstr_display ( ) {
495+ let hello_world = CStr :: from_bytes_with_nul ( b"hello, world!\0 " ) . unwrap ( ) ;
496+ assert_eq ! ( format!( "{}" , hello_world) , "hello, world!" ) ;
497+ let non_printables = CStr :: from_bytes_with_nul ( b"\x01 \x09 \x0a \0 " ) . unwrap ( ) ;
498+ assert_eq ! ( format!( "{}" , non_printables) , "\\ x01\\ x09\\ x0a" ) ;
499+ let non_ascii = CStr :: from_bytes_with_nul ( b"d\xe9 j\xe0 vu\0 " ) . unwrap ( ) ;
500+ assert_eq ! ( format!( "{}" , non_ascii) , "d\\ xe9j\\ xe0 vu" ) ;
501+ let good_bytes = CStr :: from_bytes_with_nul ( b"\xf0 \x9f \xa6 \x80 \0 " ) . unwrap ( ) ;
502+ assert_eq ! ( format!( "{}" , good_bytes) , "\\ xf0\\ x9f\\ xa6\\ x80" ) ;
503+ }
504+
505+ #[ test]
506+ fn test_cstr_display_all_bytes ( ) {
507+ let mut bytes: [ u8 ; 256 ] = [ 0 ; 256 ] ;
508+ // fill `bytes` with [1..=255] + [0]
509+ for i in u8:: MIN ..=u8:: MAX {
510+ bytes[ i as usize ] = i. wrapping_add ( 1 ) ;
511+ }
512+ let cstr = CStr :: from_bytes_with_nul ( & bytes) . unwrap ( ) ;
513+ assert_eq ! ( format!( "{}" , cstr) , ALL_ASCII_CHARS ) ;
514+ }
515+
516+ #[ test]
517+ fn test_cstr_debug ( ) {
518+ let hello_world = CStr :: from_bytes_with_nul ( b"hello, world!\0 " ) . unwrap ( ) ;
519+ assert_eq ! ( format!( "{:?}" , hello_world) , "\" hello, world!\" " ) ;
520+ let non_printables = CStr :: from_bytes_with_nul ( b"\x01 \x09 \x0a \0 " ) . unwrap ( ) ;
521+ assert_eq ! ( format!( "{:?}" , non_printables) , "\" \\ x01\\ x09\\ x0a\" " ) ;
522+ let non_ascii = CStr :: from_bytes_with_nul ( b"d\xe9 j\xe0 vu\0 " ) . unwrap ( ) ;
523+ assert_eq ! ( format!( "{:?}" , non_ascii) , "\" d\\ xe9j\\ xe0 vu\" " ) ;
524+ let good_bytes = CStr :: from_bytes_with_nul ( b"\xf0 \x9f \xa6 \x80 \0 " ) . unwrap ( ) ;
525+ assert_eq ! ( format!( "{:?}" , good_bytes) , "\" \\ xf0\\ x9f\\ xa6\\ x80\" " ) ;
526+ }
527+
528+ #[ test]
529+ fn test_bstr_display ( ) {
530+ let hello_world = BStr :: from_bytes ( b"hello, world!" ) ;
531+ assert_eq ! ( format!( "{}" , hello_world) , "hello, world!" ) ;
532+ let escapes = BStr :: from_bytes ( b"_\t _\n _\r _\\ _\' _\" _" ) ;
533+ assert_eq ! ( format!( "{}" , escapes) , "_\\ t_\\ n_\\ r_\\ _'_\" _" ) ;
534+ let others = BStr :: from_bytes ( b"\x01 " ) ;
535+ assert_eq ! ( format!( "{}" , others) , "\\ x01" ) ;
536+ let non_ascii = BStr :: from_bytes ( b"d\xe9 j\xe0 vu" ) ;
537+ assert_eq ! ( format!( "{}" , non_ascii) , "d\\ xe9j\\ xe0 vu" ) ;
538+ let good_bytes = BStr :: from_bytes ( b"\xf0 \x9f \xa6 \x80 " ) ;
539+ assert_eq ! ( format!( "{}" , good_bytes) , "\\ xf0\\ x9f\\ xa6\\ x80" ) ;
540+ }
541+
542+ #[ test]
543+ fn test_bstr_debug ( ) {
544+ let hello_world = BStr :: from_bytes ( b"hello, world!" ) ;
545+ assert_eq ! ( format!( "{:?}" , hello_world) , "\" hello, world!\" " ) ;
546+ let escapes = BStr :: from_bytes ( b"_\t _\n _\r _\\ _\' _\" _" ) ;
547+ assert_eq ! ( format!( "{:?}" , escapes) , "\" _\\ t_\\ n_\\ r_\\ \\ _'_\\ \" _\" " ) ;
548+ let others = BStr :: from_bytes ( b"\x01 " ) ;
549+ assert_eq ! ( format!( "{:?}" , others) , "\" \\ x01\" " ) ;
550+ let non_ascii = BStr :: from_bytes ( b"d\xe9 j\xe0 vu" ) ;
551+ assert_eq ! ( format!( "{:?}" , non_ascii) , "\" d\\ xe9j\\ xe0 vu\" " ) ;
552+ let good_bytes = BStr :: from_bytes ( b"\xf0 \x9f \xa6 \x80 " ) ;
553+ assert_eq ! ( format!( "{:?}" , good_bytes) , "\" \\ xf0\\ x9f\\ xa6\\ x80\" " ) ;
554+ }
384555}
385556
386557/// Allows formatting of [`fmt::Arguments`] into a raw buffer.
0 commit comments