1
1
//! Helper code for character escaping.
2
2
3
3
use crate :: ascii;
4
+ use crate :: mem:: MaybeUninit ;
4
5
use crate :: num:: NonZero ;
5
6
use crate :: ops:: Range ;
6
7
7
8
const HEX_DIGITS : [ ascii:: Char ; 16 ] = * b"0123456789abcdef" . as_ascii ( ) . unwrap ( ) ;
8
9
9
- #[ inline]
10
- const fn backslash < const N : usize > ( a : ascii:: Char ) -> ( [ ascii:: Char ; N ] , Range < u8 > ) {
11
- const { assert ! ( N >= 2 ) } ;
12
-
13
- let mut output = [ ascii:: Char :: Null ; N ] ;
14
-
15
- output[ 0 ] = ascii:: Char :: ReverseSolidus ;
16
- output[ 1 ] = a;
17
-
18
- ( output, 0 ..2 )
19
- }
20
-
21
- /// Escapes an ASCII character.
22
- ///
23
- /// Returns a buffer and the length of the escaped representation.
24
- const fn escape_ascii < const N : usize > ( byte : u8 ) -> ( [ ascii:: Char ; N ] , Range < u8 > ) {
25
- const { assert ! ( N >= 4 ) } ;
26
-
27
- match byte {
28
- b'\t' => backslash ( ascii:: Char :: SmallT ) ,
29
- b'\r' => backslash ( ascii:: Char :: SmallR ) ,
30
- b'\n' => backslash ( ascii:: Char :: SmallN ) ,
31
- b'\\' => backslash ( ascii:: Char :: ReverseSolidus ) ,
32
- b'\'' => backslash ( ascii:: Char :: Apostrophe ) ,
33
- b'\"' => backslash ( ascii:: Char :: QuotationMark ) ,
34
- byte => {
35
- let mut output = [ ascii:: Char :: Null ; N ] ;
36
-
37
- if let Some ( c) = byte. as_ascii ( )
38
- && !byte. is_ascii_control ( )
39
- {
40
- output[ 0 ] = c;
41
- ( output, 0 ..1 )
42
- } else {
43
- let hi = HEX_DIGITS [ ( byte >> 4 ) as usize ] ;
44
- let lo = HEX_DIGITS [ ( byte & 0xf ) as usize ] ;
45
-
46
- output[ 0 ] = ascii:: Char :: ReverseSolidus ;
47
- output[ 1 ] = ascii:: Char :: SmallX ;
48
- output[ 2 ] = hi;
49
- output[ 3 ] = lo;
50
-
51
- ( output, 0 ..4 )
52
- }
53
- }
54
- }
55
- }
56
-
57
- /// Escapes a character `\u{NNNN}` representation.
58
- ///
59
- /// Returns a buffer and the length of the escaped representation.
60
- const fn escape_unicode < const N : usize > ( c : char ) -> ( [ ascii:: Char ; N ] , Range < u8 > ) {
61
- const { assert ! ( N >= 10 && N < u8 :: MAX as usize ) } ;
62
-
63
- let c = u32:: from ( c) ;
64
-
65
- // OR-ing `1` ensures that for `c == 0` the code computes that
66
- // one digit should be printed.
67
- let start = ( c | 1 ) . leading_zeros ( ) as usize / 4 - 2 ;
68
-
69
- let mut output = [ ascii:: Char :: Null ; N ] ;
70
- output[ 3 ] = HEX_DIGITS [ ( ( c >> 20 ) & 15 ) as usize ] ;
71
- output[ 4 ] = HEX_DIGITS [ ( ( c >> 16 ) & 15 ) as usize ] ;
72
- output[ 5 ] = HEX_DIGITS [ ( ( c >> 12 ) & 15 ) as usize ] ;
73
- output[ 6 ] = HEX_DIGITS [ ( ( c >> 8 ) & 15 ) as usize ] ;
74
- output[ 7 ] = HEX_DIGITS [ ( ( c >> 4 ) & 15 ) as usize ] ;
75
- output[ 8 ] = HEX_DIGITS [ ( ( c >> 0 ) & 15 ) as usize ] ;
76
- output[ 9 ] = ascii:: Char :: RightCurlyBracket ;
77
- output[ start + 0 ] = ascii:: Char :: ReverseSolidus ;
78
- output[ start + 1 ] = ascii:: Char :: SmallU ;
79
- output[ start + 2 ] = ascii:: Char :: LeftCurlyBracket ;
80
-
81
- ( output, ( start as u8 ) ..( N as u8 ) )
82
- }
83
-
84
10
/// An iterator over an fixed-size array.
85
11
///
86
12
/// This is essentially equivalent to array’s IntoIter except that indexes are
87
13
/// limited to u8 to reduce size of the structure.
88
14
#[ derive( Clone , Debug ) ]
89
15
pub ( crate ) struct EscapeIterInner < const N : usize > {
90
- // The element type ensures this is always ASCII, and thus also valid UTF-8.
91
- data : [ ascii:: Char ; N ] ,
16
+ // Invariant: all elements inside the range indexed by `alive` are initialized
17
+ data : [ MaybeUninit < ascii:: Char > ; N ] ,
92
18
93
19
// Invariant: `alive.start <= alive.end <= N`
94
20
alive : Range < u8 > ,
95
21
}
96
22
97
23
impl < const N : usize > EscapeIterInner < N > {
98
24
pub const fn backslash ( c : ascii:: Char ) -> Self {
99
- let ( data, range) = backslash ( c) ;
100
- Self { data, alive : range }
25
+ const { assert ! ( N >= 2 ) } ;
26
+
27
+ let mut data = [ MaybeUninit :: uninit ( ) ; N ] ;
28
+
29
+ data[ 0 ] = MaybeUninit :: new ( ascii:: Char :: ReverseSolidus ) ;
30
+ data[ 1 ] = MaybeUninit :: new ( c) ;
31
+
32
+ Self { data, alive : 0 ..2 }
101
33
}
102
34
35
+ /// Escapes an ASCII character.
103
36
pub const fn ascii ( c : u8 ) -> Self {
104
- let ( data, range) = escape_ascii ( c) ;
105
- Self { data, alive : range }
37
+ const { assert ! ( N >= 4 ) } ;
38
+
39
+ match c {
40
+ b'\t' => Self :: backslash ( ascii:: Char :: SmallT ) ,
41
+ b'\r' => Self :: backslash ( ascii:: Char :: SmallR ) ,
42
+ b'\n' => Self :: backslash ( ascii:: Char :: SmallN ) ,
43
+ b'\\' => Self :: backslash ( ascii:: Char :: ReverseSolidus ) ,
44
+ b'\'' => Self :: backslash ( ascii:: Char :: Apostrophe ) ,
45
+ b'\"' => Self :: backslash ( ascii:: Char :: QuotationMark ) ,
46
+ byte => {
47
+ let mut data = [ MaybeUninit :: uninit ( ) ; N ] ;
48
+
49
+ if let Some ( c) = byte. as_ascii ( )
50
+ && !byte. is_ascii_control ( )
51
+ {
52
+ data[ 0 ] = MaybeUninit :: new ( c) ;
53
+ Self { data, alive : 0 ..1 }
54
+ } else {
55
+ let hi = HEX_DIGITS [ ( byte >> 4 ) as usize ] ;
56
+ let lo = HEX_DIGITS [ ( byte & 0xf ) as usize ] ;
57
+
58
+ data[ 0 ] = MaybeUninit :: new ( ascii:: Char :: ReverseSolidus ) ;
59
+ data[ 1 ] = MaybeUninit :: new ( ascii:: Char :: SmallX ) ;
60
+ data[ 2 ] = MaybeUninit :: new ( hi) ;
61
+ data[ 3 ] = MaybeUninit :: new ( lo) ;
62
+
63
+ Self { data, alive : 0 ..4 }
64
+ }
65
+ }
66
+ }
106
67
}
107
68
69
+ /// Escapes a character `\u{NNNN}` representation.
108
70
pub const fn unicode ( c : char ) -> Self {
109
- let ( data, range) = escape_unicode ( c) ;
110
- Self { data, alive : range }
71
+ const { assert ! ( N >= 10 && N < u8 :: MAX as usize ) } ;
72
+
73
+ let c = c as u32 ;
74
+
75
+ // OR-ing `1` ensures that for `c == 0` the code computes that
76
+ // one digit should be printed.
77
+ let start = ( c | 1 ) . leading_zeros ( ) as usize / 4 - 2 ;
78
+
79
+ let mut data = [ MaybeUninit :: uninit ( ) ; N ] ;
80
+ data[ 3 ] = MaybeUninit :: new ( HEX_DIGITS [ ( ( c >> 20 ) & 15 ) as usize ] ) ;
81
+ data[ 4 ] = MaybeUninit :: new ( HEX_DIGITS [ ( ( c >> 16 ) & 15 ) as usize ] ) ;
82
+ data[ 5 ] = MaybeUninit :: new ( HEX_DIGITS [ ( ( c >> 12 ) & 15 ) as usize ] ) ;
83
+ data[ 6 ] = MaybeUninit :: new ( HEX_DIGITS [ ( ( c >> 8 ) & 15 ) as usize ] ) ;
84
+ data[ 7 ] = MaybeUninit :: new ( HEX_DIGITS [ ( ( c >> 4 ) & 15 ) as usize ] ) ;
85
+ data[ 8 ] = MaybeUninit :: new ( HEX_DIGITS [ ( ( c >> 0 ) & 15 ) as usize ] ) ;
86
+ data[ 9 ] = MaybeUninit :: new ( ascii:: Char :: RightCurlyBracket ) ;
87
+ data[ start + 0 ] = MaybeUninit :: new ( ascii:: Char :: ReverseSolidus ) ;
88
+ data[ start + 1 ] = MaybeUninit :: new ( ascii:: Char :: SmallU ) ;
89
+ data[ start + 2 ] = MaybeUninit :: new ( ascii:: Char :: LeftCurlyBracket ) ;
90
+
91
+ Self { data, alive : start as u8 ..10 }
111
92
}
112
93
113
94
#[ inline]
114
95
pub const fn empty ( ) -> Self {
115
- Self { data : [ ascii :: Char :: Null ; N ] , alive : 0 ..0 }
96
+ Self { data : [ MaybeUninit :: uninit ( ) ; N ] , alive : 0 ..0 }
116
97
}
117
98
118
99
#[ inline]
119
100
pub fn as_ascii ( & self ) -> & [ ascii:: Char ] {
120
- // SAFETY: `self.alive` is guaranteed to be a valid range for indexing `self. data` .
101
+ // SAFETY: the range indexed by `self.alive` is guaranteed to contain valid data.
121
102
unsafe {
122
- self . data . get_unchecked ( usize:: from ( self . alive . start ) ..usize:: from ( self . alive . end ) )
103
+ let data = self . data . get_unchecked ( self . alive . start as usize ..self . alive . end as usize ) ;
104
+ MaybeUninit :: slice_assume_init_ref ( data)
123
105
}
124
106
}
125
107
@@ -130,27 +112,31 @@ impl<const N: usize> EscapeIterInner<N> {
130
112
131
113
#[ inline]
132
114
pub fn len ( & self ) -> usize {
133
- usize :: from ( self . alive . end - self . alive . start )
115
+ self . alive . len ( )
134
116
}
135
117
118
+ #[ inline]
136
119
pub fn next ( & mut self ) -> Option < u8 > {
137
120
let i = self . alive . next ( ) ?;
138
121
139
- // SAFETY: `i ` is guaranteed to be a valid index for `self. data` .
140
- unsafe { Some ( self . data . get_unchecked ( usize :: from ( i ) ) . to_u8 ( ) ) }
122
+ // SAFETY: the range indexed by `self.alive ` is guaranteed to contain initialized data.
123
+ unsafe { Some ( MaybeUninit :: assume_init_ref ( self . data . get_unchecked ( i as usize ) ) . to_u8 ( ) ) }
141
124
}
142
125
126
+ #[ inline]
143
127
pub fn next_back ( & mut self ) -> Option < u8 > {
144
128
let i = self . alive . next_back ( ) ?;
145
129
146
- // SAFETY: `i ` is guaranteed to be a valid index for `self. data` .
147
- unsafe { Some ( self . data . get_unchecked ( usize :: from ( i ) ) . to_u8 ( ) ) }
130
+ // SAFETY: the range indexed by `self.alive ` is guaranteed to contain initialized data.
131
+ unsafe { Some ( MaybeUninit :: assume_init_ref ( self . data . get_unchecked ( i as usize ) ) . to_u8 ( ) ) }
148
132
}
149
133
134
+ #[ inline]
150
135
pub fn advance_by ( & mut self , n : usize ) -> Result < ( ) , NonZero < usize > > {
151
136
self . alive . advance_by ( n)
152
137
}
153
138
139
+ #[ inline]
154
140
pub fn advance_back_by ( & mut self , n : usize ) -> Result < ( ) , NonZero < usize > > {
155
141
self . alive . advance_back_by ( n)
156
142
}
0 commit comments