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)]
170pub(crate) struct AlwaysEscaped;
171
172#[derive(Clone, Copy)]
175pub(crate) struct MaybeEscaped;
176
177#[derive(Clone)]
179pub(crate) struct EscapeIterInner<const N: usize, ESCAPING> {
180 data: MaybeEscapedCharacter<N>,
189 alive: Range<u8>,
190 escaping: PhantomData<ESCAPING>,
191}
192
193impl<const N: usize, ESCAPING> EscapeIterInner<N, ESCAPING> {
194 const LITERAL_ESCAPE_START: u8 = 128;
195
196 #[inline]
200 const unsafe fn new(data: MaybeEscapedCharacter<N>, alive: Range<u8>) -> Self {
201 const { assert!(N < Self::LITERAL_ESCAPE_START as usize) };
204
205 debug_assert!(alive.end <= (N + 1) as u8);
208
209 Self { data, alive, escaping: PhantomData }
210 }
211
212 pub(crate) const fn backslash(c: ascii::Char) -> Self {
213 let (escape_seq, alive) = backslash(c);
214 unsafe { Self::new(MaybeEscapedCharacter { escape_seq }, alive) }
216 }
217
218 pub(crate) const fn ascii(c: u8) -> Self {
219 let (escape_seq, alive) = escape_ascii(c);
220 unsafe { Self::new(MaybeEscapedCharacter { escape_seq }, alive) }
222 }
223
224 pub(crate) const fn unicode(c: char) -> Self {
225 let (escape_seq, alive) = escape_unicode(c);
226 unsafe { Self::new(MaybeEscapedCharacter { escape_seq }, alive) }
228 }
229
230 #[inline]
231 pub(crate) const fn empty() -> Self {
232 unsafe { Self::new(MaybeEscapedCharacter { escape_seq: [ascii::Char::Null; N] }, 0..0) }
234 }
235
236 #[inline]
237 pub(crate) fn len(&self) -> usize {
238 usize::from(self.alive.end - self.alive.start)
239 }
240
241 #[inline]
242 pub(crate) fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
243 self.alive.advance_by(n)
244 }
245
246 #[inline]
247 pub(crate) fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
248 self.alive.advance_back_by(n)
249 }
250
251 #[inline]
253 const fn to_char(&self) -> Option<char> {
254 if self.alive.end > Self::LITERAL_ESCAPE_START {
255 return Some(unsafe { self.data.literal });
258 }
259
260 None
261 }
262
263 #[inline]
271 unsafe fn to_str_unchecked(&self) -> &str {
272 debug_assert!(self.alive.end <= Self::LITERAL_ESCAPE_START);
273
274 unsafe {
278 self.data
279 .escape_seq
280 .get_unchecked(usize::from(self.alive.start)..usize::from(self.alive.end))
281 .as_str()
282 }
283 }
284}
285
286impl<const N: usize> EscapeIterInner<N, AlwaysEscaped> {
287 pub(crate) fn next(&mut self) -> Option<u8> {
288 let i = self.alive.next()?;
289
290 unsafe { Some(self.data.escape_seq.get_unchecked(usize::from(i)).to_u8()) }
295 }
296
297 pub(crate) fn next_back(&mut self) -> Option<u8> {
298 let i = self.alive.next_back()?;
299
300 unsafe { Some(self.data.escape_seq.get_unchecked(usize::from(i)).to_u8()) }
305 }
306}
307
308impl<const N: usize> EscapeIterInner<N, MaybeEscaped> {
309 pub(crate) const fn printable(c: char) -> Self {
314 Self {
315 data: MaybeEscapedCharacter { literal: c },
316 alive: Self::LITERAL_ESCAPE_START..(Self::LITERAL_ESCAPE_START + 1),
319 escaping: PhantomData,
320 }
321 }
322
323 pub(crate) fn next(&mut self) -> Option<char> {
324 let i = self.alive.next()?;
325
326 if let Some(c) = self.to_char() {
327 return Some(c);
328 }
329
330 Some(char::from(unsafe { self.data.escape_seq.get_unchecked(usize::from(i)).to_u8() }))
334 }
335}
336
337impl<const N: usize> fmt::Display for EscapeIterInner<N, AlwaysEscaped> {
338 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339 f.write_str(unsafe { self.to_str_unchecked() })
343 }
344}
345
346impl<const N: usize> fmt::Display for EscapeIterInner<N, MaybeEscaped> {
347 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
348 if let Some(c) = self.to_char() {
349 return f.write_char(c);
350 }
351
352 f.write_str(unsafe { self.to_str_unchecked() })
356 }
357}
358
359impl<const N: usize> fmt::Debug for EscapeIterInner<N, AlwaysEscaped> {
360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361 f.debug_tuple("EscapeIterInner").field(&format_args!("'{}'", self)).finish()
362 }
363}
364
365impl<const N: usize> fmt::Debug for EscapeIterInner<N, MaybeEscaped> {
366 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367 f.debug_tuple("EscapeIterInner").field(&format_args!("'{}'", self)).finish()
368 }
369}