@@ -6,56 +6,79 @@ use crate::ops::Range;
66
77const HEX_DIGITS : [ ascii:: Char ; 16 ] = * b"0123456789abcdef" . as_ascii ( ) . unwrap ( ) ;
88
9- /// Escapes a byte into provided buffer; returns length of escaped
10- /// representation.
11- pub ( crate ) fn escape_ascii_into ( output : & mut [ ascii:: Char ; 4 ] , byte : u8 ) -> Range < u8 > {
12- #[ inline]
13- fn backslash ( a : ascii:: Char ) -> ( [ ascii:: Char ; 4 ] , u8 ) {
14- ( [ ascii:: Char :: ReverseSolidus , a, ascii:: Char :: Null , ascii:: Char :: Null ] , 2 )
15- }
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+ }
1620
17- let ( data, len) = match byte {
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 {
1828 b'\t' => backslash ( ascii:: Char :: SmallT ) ,
1929 b'\r' => backslash ( ascii:: Char :: SmallR ) ,
2030 b'\n' => backslash ( ascii:: Char :: SmallN ) ,
2131 b'\\' => backslash ( ascii:: Char :: ReverseSolidus ) ,
2232 b'\'' => backslash ( ascii:: Char :: Apostrophe ) ,
2333 b'\"' => backslash ( ascii:: Char :: QuotationMark ) ,
24- _ => {
25- if let Some ( a) = byte. as_ascii ( )
34+ byte => {
35+ let mut output = [ ascii:: Char :: Null ; N ] ;
36+
37+ if let Some ( c) = byte. as_ascii ( )
2638 && !byte. is_ascii_control ( )
2739 {
28- ( [ a, ascii:: Char :: Null , ascii:: Char :: Null , ascii:: Char :: Null ] , 1 )
40+ output[ 0 ] = c;
41+ ( output, 0 ..1 )
2942 } else {
30- let hi = HEX_DIGITS [ usize:: from ( byte >> 4 ) ] ;
31- let lo = HEX_DIGITS [ usize:: from ( byte & 0xf ) ] ;
32- ( [ ascii:: Char :: ReverseSolidus , ascii:: Char :: SmallX , hi, lo] , 4 )
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 )
3352 }
3453 }
35- } ;
36- * output = data;
37- 0 ..len
54+ }
3855}
3956
40- /// Escapes a character into provided buffer using `\u{NNNN}` representation.
41- pub ( crate ) fn escape_unicode_into ( output : & mut [ ascii:: Char ; 10 ] , ch : char ) -> Range < u8 > {
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 ] ;
4276 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 ;
4380
44- let ch = ch as u32 ;
45- output[ 3 ] = HEX_DIGITS [ ( ( ch >> 20 ) & 15 ) as usize ] ;
46- output[ 4 ] = HEX_DIGITS [ ( ( ch >> 16 ) & 15 ) as usize ] ;
47- output[ 5 ] = HEX_DIGITS [ ( ( ch >> 12 ) & 15 ) as usize ] ;
48- output[ 6 ] = HEX_DIGITS [ ( ( ch >> 8 ) & 15 ) as usize ] ;
49- output[ 7 ] = HEX_DIGITS [ ( ( ch >> 4 ) & 15 ) as usize ] ;
50- output[ 8 ] = HEX_DIGITS [ ( ( ch >> 0 ) & 15 ) as usize ] ;
51-
52- // or-ing 1 ensures that for ch==0 the code computes that one digit should
53- // be printed.
54- let start = ( ch | 1 ) . leading_zeros ( ) as usize / 4 - 2 ;
55- const UNICODE_ESCAPE_PREFIX : & [ ascii:: Char ; 3 ] = b"\\ u{" . as_ascii ( ) . unwrap ( ) ;
56- output[ start..] [ ..3 ] . copy_from_slice ( UNICODE_ESCAPE_PREFIX ) ;
57-
58- ( start as u8 ) ..10
81+ ( output, ( start as u8 ) ..( N as u8 ) )
5982}
6083
6184/// An iterator over an fixed-size array.
@@ -65,45 +88,63 @@ pub(crate) fn escape_unicode_into(output: &mut [ascii::Char; 10], ch: char) -> R
6588#[ derive( Clone , Debug ) ]
6689pub ( crate ) struct EscapeIterInner < const N : usize > {
6790 // The element type ensures this is always ASCII, and thus also valid UTF-8.
68- pub ( crate ) data : [ ascii:: Char ; N ] ,
91+ data : [ ascii:: Char ; N ] ,
6992
70- // Invariant: alive.start <= alive.end <= N.
71- pub ( crate ) alive : Range < u8 > ,
93+ // Invariant: ` alive.start <= alive.end <= N`
94+ alive : Range < u8 > ,
7295}
7396
7497impl < const N : usize > EscapeIterInner < N > {
75- pub fn new ( data : [ ascii:: Char ; N ] , alive : Range < u8 > ) -> Self {
76- const { assert ! ( N < 256 ) } ;
77- debug_assert ! ( alive. start <= alive. end && usize :: from( alive. end) <= N , "{alive:?}" ) ;
78- Self { data, alive }
98+ pub const fn backslash ( c : ascii:: Char ) -> Self {
99+ let ( data, range) = backslash ( c) ;
100+ Self { data, alive : range }
101+ }
102+
103+ pub const fn ascii ( c : u8 ) -> Self {
104+ let ( data, range) = escape_ascii ( c) ;
105+ Self { data, alive : range }
79106 }
80107
81- pub fn from_array < const M : usize > ( array : [ ascii:: Char ; M ] ) -> Self {
82- const { assert ! ( M <= N ) } ;
108+ pub const fn unicode ( c : char ) -> Self {
109+ let ( data, range) = escape_unicode ( c) ;
110+ Self { data, alive : range }
111+ }
83112
84- let mut data = [ ascii :: Char :: Null ; N ] ;
85- data [ .. M ] . copy_from_slice ( & array ) ;
86- Self :: new ( data , 0 ..M as u8 )
113+ # [ inline ]
114+ pub const fn empty ( ) -> Self {
115+ Self { data : [ ascii :: Char :: Null ; N ] , alive : 0 ..0 }
87116 }
88117
118+ #[ inline]
89119 pub fn as_ascii ( & self ) -> & [ ascii:: Char ] {
90- & self . data [ usize:: from ( self . alive . start ) ..usize:: from ( self . alive . end ) ]
120+ // SAFETY: `self.alive` is guaranteed to be a valid range for indexing `self.data`.
121+ unsafe {
122+ self . data . get_unchecked ( usize:: from ( self . alive . start ) ..usize:: from ( self . alive . end ) )
123+ }
91124 }
92125
126+ #[ inline]
93127 pub fn as_str ( & self ) -> & str {
94128 self . as_ascii ( ) . as_str ( )
95129 }
96130
131+ #[ inline]
97132 pub fn len ( & self ) -> usize {
98133 usize:: from ( self . alive . end - self . alive . start )
99134 }
100135
101136 pub fn next ( & mut self ) -> Option < u8 > {
102- self . alive . next ( ) . map ( |i| self . data [ usize:: from ( i) ] . to_u8 ( ) )
137+ let i = self . alive . next ( ) ?;
138+
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 ( ) ) }
103141 }
104142
105143 pub fn next_back ( & mut self ) -> Option < u8 > {
106- self . alive . next_back ( ) . map ( |i| self . data [ usize:: from ( i) ] . to_u8 ( ) )
144+ let i = self . alive . next_back ( ) ?;
145+
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 ( ) ) }
107148 }
108149
109150 pub fn advance_by ( & mut self , n : usize ) -> Result < ( ) , NonZero < usize > > {
0 commit comments