1use crate::ascii;
4use crate::fmt::{self, Write};
5use crate::marker::PhantomData;
6use crate::num::NonZero;
7use crate::ops::Range;
8
9const HEX_DIGITS: [ascii::Char; 16] = *b"0123456789abcdef".as_ascii().unwrap();
10
11#[inline]
15const fn backslash<const N: usize>(a: ascii::Char) -> ([ascii::Char; N], Range<u8>) {
16 const { assert!(N >= 2) };
17
18 let mut output = [ascii::Char::Null; N];
19
20 output[0] = ascii::Char::ReverseSolidus;
21 output[1] = a;
22
23 (output, 0..2)
24}
25
26#[inline]
30const fn hex_escape<const N: usize>(byte: u8) -> ([ascii::Char; N], Range<u8>) {
31 const { assert!(N >= 4) };
32
33 let mut output = [ascii::Char::Null; N];
34
35 let hi = HEX_DIGITS[(byte >> 4) as usize];
36 let lo = HEX_DIGITS[(byte & 0xf) as usize];
37
38 output[0] = ascii::Char::ReverseSolidus;
39 output[1] = ascii::Char::SmallX;
40 output[2] = hi;
41 output[3] = lo;
42
43 (output, 0..4)
44}
45
46#[inline]
48const fn verbatim<const N: usize>(a: ascii::Char) -> ([ascii::Char; N], Range<u8>) {
49 const { assert!(N >= 1) };
50
51 let mut output = [ascii::Char::Null; N];
52
53 output[0] = a;
54
55 (output, 0..1)
56}
57
58const fn escape_ascii<const N: usize>(byte: u8) -> ([ascii::Char; N], Range<u8>) {
62 const { assert!(N >= 4) };
63
64 #[cfg(feature = "optimize_for_size")]
65 {
66 match byte {
67 b'\t' => backslash(ascii::Char::SmallT),
68 b'\r' => backslash(ascii::Char::SmallR),
69 b'\n' => backslash(ascii::Char::SmallN),
70 b'\\' => backslash(ascii::Char::ReverseSolidus),
71 b'\'' => backslash(ascii::Char::Apostrophe),
72 b'"' => backslash(ascii::Char::QuotationMark),
73 0x00..=0x1F | 0x7F => hex_escape(byte),
74 _ => match ascii::Char::from_u8(byte) {
75 Some(a) => verbatim(a),
76 None => hex_escape(byte),
77 },
78 }
79 }
80
81 #[cfg(not(feature = "optimize_for_size"))]
82 {
83 const LOOKUP: [u8; 256] = {
91 let mut arr = [0; 256];
92 let mut idx = 0;
93 while idx <= 255 {
94 arr[idx] = match idx as u8 {
95 b'\t' => 0x80 | b't',
97 b'\r' => 0x80 | b'r',
98 b'\n' => 0x80 | b'n',
99 b'\\' => 0x80 | b'\\',
100 b'\'' => 0x80 | b'\'',
101 b'"' => 0x80 | b'"',
102
103 0x00..=0x1F | 0x7F..=0xFF => 0x80 | b'\0',
105
106 idx => idx,
107 };
108 idx += 1;
109 }
110 arr
111 };
112
113 let lookup = LOOKUP[byte as usize];
114
115 let lookup_escaped = lookup & 0x80 != 0;
117
118 let lookup_ascii = unsafe { ascii::Char::from_u8_unchecked(lookup & 0x7F) };
120
121 if lookup_escaped {
122 if matches!(lookup_ascii, ascii::Char::Null) {
124 hex_escape(byte)
125 } else {
126 backslash(lookup_ascii)
127 }
128 } else {
129 verbatim(lookup_ascii)
130 }
131 }
132}
133
134const fn escape_unicode<const N: usize>(c: char) -> ([ascii::Char; N], Range<u8>) {
138 const { assert!(N >= 10 && N < u8::MAX as usize) };
139
140 let c = c as u32;
141
142 let start = (c | 1).leading_zeros() as usize / 4 - 2;
145
146 let mut output = [ascii::Char::Null; N];
147 output[3] = HEX_DIGITS[((c >> 20) & 15) as usize];
148 output[4] = HEX_DIGITS[((c >> 16) & 15) as usize];
149 output[5] = HEX_DIGITS[((c >> 12) & 15) as usize];
150 output[6] = HEX_DIGITS[((c >> 8) & 15) as usize];
151 output[7] = HEX_DIGITS[((c >> 4) & 15) as usize];
152 output[8] = HEX_DIGITS[((c >> 0) & 15) as usize];
153 output[9] = ascii::Char::RightCurlyBracket;
154 output[start + 0] = ascii::Char::ReverseSolidus;
155 output[start + 1] = ascii::Char::SmallU;
156 output[start + 2] = ascii::Char::LeftCurlyBracket;
157
158 (output, (start as u8)..(N as u8))
159}
160
161#[derive(Clone, Copy)]
162union MaybeEscapedCharacter<const N: usize> {
163 pub escape_seq: [ascii::Char; N],
164 pub literal: char,
165}
166
167#[derive(Clone, Copy)]
170#[non_exhaustive]
171pub(crate) struct AlwaysEscaped;
172
173#[derive(Clone, Copy)]
176#[non_exhaustive]
177pub(crate) struct MaybeEscaped;
178
179#[derive(Clone)]
181pub(crate) struct EscapeIterInner<const N: usize, ESCAPING> {
182 data: MaybeEscapedCharacter<N>,
191 alive: Range<u8>,
192 escaping: PhantomData<ESCAPING>,
193}
194
195impl<const N: usize, ESCAPING> EscapeIterInner<N, ESCAPING> {
196 const LITERAL_ESCAPE_START: u8 = 128;
197
198 #[inline]
202 const unsafe fn new(data: MaybeEscapedCharacter<N>, alive: Range<u8>) -> Self {
203 const { assert!(N < Self::LITERAL_ESCAPE_START as usize) };
206
207 debug_assert!(alive.end <= (N + 1) as u8);
210
211 Self { data, alive, escaping: PhantomData }
212 }
213
214 pub(crate) const fn backslash(c: ascii::Char) -> Self {
215 let (escape_seq, alive) = backslash(c);
216 unsafe { Self::new(MaybeEscapedCharacter { escape_seq }, alive) }
218 }
219
220 pub(crate) const fn ascii(c: u8) -> Self {
221 let (escape_seq, alive) = escape_ascii(c);
222 unsafe { Self::new(MaybeEscapedCharacter { escape_seq }, alive) }
224 }
225
226 pub(crate) const fn unicode(c: char) -> Self {
227 let (escape_seq, alive) = escape_unicode(c);
228 unsafe { Self::new(MaybeEscapedCharacter { escape_seq }, alive) }
230 }
231
232 #[inline]
233 pub(crate) const fn empty() -> Self {
234 unsafe { Self::new(MaybeEscapedCharacter { escape_seq: [ascii::Char::Null; N] }, 0..0) }
236 }
237
238 #[inline]
239 pub(crate) fn len(&self) -> usize {
240 usize::from(self.alive.end - self.alive.start)
241 }
242
243 #[inline]
244 pub(crate) fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
245 self.alive.advance_by(n)
246 }
247
248 #[inline]
249 pub(crate) fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
250 self.alive.advance_back_by(n)
251 }
252
253 #[inline]
255 const fn to_char(&self) -> Option<char> {
256 if self.alive.end > Self::LITERAL_ESCAPE_START {
257 return Some(unsafe { self.data.literal });
260 }
261
262 None
263 }
264
265 #[inline]
273 unsafe fn to_str_unchecked(&self) -> &str {
274 debug_assert!(self.alive.end <= Self::LITERAL_ESCAPE_START);
275
276 unsafe {
280 self.data
281 .escape_seq
282 .get_unchecked(usize::from(self.alive.start)..usize::from(self.alive.end))
283 .as_str()
284 }
285 }
286}
287
288impl<const N: usize> EscapeIterInner<N, AlwaysEscaped> {
289 pub(crate) fn next(&mut self) -> Option<u8> {
290 let i = self.alive.next()?;
291
292 unsafe { Some(self.data.escape_seq.get_unchecked(usize::from(i)).to_u8()) }
297 }
298
299 pub(crate) fn next_back(&mut self) -> Option<u8> {
300 let i = self.alive.next_back()?;
301
302 unsafe { Some(self.data.escape_seq.get_unchecked(usize::from(i)).to_u8()) }
307 }
308}
309
310impl<const N: usize> EscapeIterInner<N, MaybeEscaped> {
311 pub(crate) const fn printable(c: char) -> Self {
316 Self {
317 data: MaybeEscapedCharacter { literal: c },
318 alive: Self::LITERAL_ESCAPE_START..(Self::LITERAL_ESCAPE_START + 1),
321 escaping: PhantomData,
322 }
323 }
324
325 pub(crate) fn next(&mut self) -> Option<char> {
326 let i = self.alive.next()?;
327
328 if let Some(c) = self.to_char() {
329 return Some(c);
330 }
331
332 Some(char::from(unsafe { self.data.escape_seq.get_unchecked(usize::from(i)).to_u8() }))
336 }
337}
338
339impl<const N: usize> fmt::Display for EscapeIterInner<N, AlwaysEscaped> {
340 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341 f.write_str(unsafe { self.to_str_unchecked() })
345 }
346}
347
348impl<const N: usize> fmt::Display for EscapeIterInner<N, MaybeEscaped> {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 if let Some(c) = self.to_char() {
351 return f.write_char(c);
352 }
353
354 f.write_str(unsafe { self.to_str_unchecked() })
358 }
359}
360
361impl<const N: usize> fmt::Debug for EscapeIterInner<N, AlwaysEscaped> {
362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363 f.debug_tuple("EscapeIterInner").field(&format_args!("'{}'", self)).finish()
364 }
365}
366
367impl<const N: usize> fmt::Debug for EscapeIterInner<N, MaybeEscaped> {
368 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
369 f.debug_tuple("EscapeIterInner").field(&format_args!("'{}'", self)).finish()
370 }
371}