@@ -13,9 +13,102 @@ use crate::{
13
13
} ;
14
14
15
15
/// 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
+ }
19
112
20
113
/// Creates a new [`BStr`] from a string literal.
21
114
///
@@ -33,7 +126,7 @@ pub type BStr = [u8];
33
126
macro_rules! b_str {
34
127
( $str: literal) => { {
35
128
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( ) ) ;
37
130
C
38
131
} } ;
39
132
}
@@ -271,7 +364,7 @@ impl fmt::Debug for CStr {
271
364
impl AsRef < BStr > for CStr {
272
365
#[ inline]
273
366
fn as_ref ( & self ) -> & BStr {
274
- self . as_bytes ( )
367
+ BStr :: from_bytes ( self . as_bytes ( ) )
275
368
}
276
369
}
277
370
@@ -280,7 +373,7 @@ impl Deref for CStr {
280
373
281
374
#[ inline]
282
375
fn deref ( & self ) -> & Self :: Target {
283
- self . as_bytes ( )
376
+ self . as_ref ( )
284
377
}
285
378
}
286
379
@@ -327,7 +420,7 @@ where
327
420
328
421
#[ inline]
329
422
fn index ( & self , index : Idx ) -> & Self :: Output {
330
- & self . as_bytes ( ) [ index]
423
+ & self . as_ref ( ) [ index]
331
424
}
332
425
}
333
426
@@ -357,6 +450,21 @@ macro_rules! c_str {
357
450
#[ cfg( test) ]
358
451
mod tests {
359
452
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";
360
468
361
469
#[ test]
362
470
fn test_cstr_to_str ( ) {
@@ -381,6 +489,69 @@ mod tests {
381
489
let unchecked_str = unsafe { checked_cstr. as_str_unchecked ( ) } ;
382
490
assert_eq ! ( unchecked_str, "🐧" ) ;
383
491
}
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
+ }
384
555
}
385
556
386
557
/// Allows formatting of [`fmt::Arguments`] into a raw buffer.
0 commit comments