blazingly_json/
canonical.rs1#![allow(clippy::inline_always)]
4
5#[derive(Clone, Copy, Debug)]
12pub struct CanonicalScanner<'a> {
13 remaining: &'a str,
14}
15
16#[derive(Clone, Copy, Debug)]
25pub struct CanonicalBytesScanner<'a> {
26 remaining: &'a [u8],
27}
28
29impl<'a> CanonicalScanner<'a> {
30 #[must_use]
32 #[inline(always)]
33 pub const fn new(input: &'a str) -> Self {
34 Self { remaining: input }
35 }
36
37 #[inline(always)]
41 pub fn literal(&mut self, expected: &str) -> Option<()> {
42 self.remaining = self.remaining.strip_prefix(expected)?;
43 Some(())
44 }
45
46 #[inline(always)]
51 pub fn plain_string(&mut self) -> Option<&'a str> {
52 let input = self.remaining.strip_prefix('"')?;
53 let end = memchr::memchr(b'"', input.as_bytes())?;
54 let value = &input[..end];
55 if value
56 .as_bytes()
57 .iter()
58 .any(|byte| *byte == b'\\' || *byte < 0x20)
59 {
60 return None;
61 }
62 self.remaining = &input[end + 1..];
63 Some(value)
64 }
65
66 #[inline(always)]
68 pub fn unsigned(&mut self) -> Option<u64> {
69 let bytes = self.remaining.as_bytes();
70 let first = *bytes.first()?;
71 if !first.is_ascii_digit() {
72 return None;
73 }
74 if first == b'0' && bytes.get(1).is_some_and(u8::is_ascii_digit) {
75 return None;
76 }
77
78 let mut value = 0_u64;
79 let mut length = 0;
80 for &digit in bytes {
81 if !digit.is_ascii_digit() {
82 break;
83 }
84 value = value
85 .checked_mul(10)?
86 .checked_add(u64::from(digit - b'0'))?;
87 length += 1;
88 }
89 self.remaining = &self.remaining[length..];
90 Some(value)
91 }
92
93 #[inline(always)]
95 pub fn boolean(&mut self) -> Option<bool> {
96 if let Some(remaining) = self.remaining.strip_prefix("true") {
97 self.remaining = remaining;
98 Some(true)
99 } else if let Some(remaining) = self.remaining.strip_prefix("false") {
100 self.remaining = remaining;
101 Some(false)
102 } else {
103 None
104 }
105 }
106
107 #[must_use]
109 #[inline(always)]
110 pub const fn remaining(&self) -> &'a str {
111 self.remaining
112 }
113
114 #[must_use]
116 #[inline(always)]
117 pub const fn is_finished(&self) -> bool {
118 self.remaining.is_empty()
119 }
120}
121
122impl<'a> CanonicalBytesScanner<'a> {
123 #[must_use]
125 #[inline(always)]
126 pub const fn new(input: &'a [u8]) -> Self {
127 Self { remaining: input }
128 }
129
130 #[inline(always)]
132 pub fn literal(&mut self, expected: &str) -> Option<()> {
133 self.remaining = self.remaining.strip_prefix(expected.as_bytes())?;
134 Some(())
135 }
136
137 #[inline(always)]
139 pub fn plain_string(&mut self) -> Option<&'a str> {
140 let input = self.remaining.strip_prefix(b"\"")?;
141 let end = memchr::memchr(b'"', input)?;
142 let value = &input[..end];
143 if value.iter().any(|byte| *byte == b'\\' || *byte < 0x20) {
144 return None;
145 }
146 let value = std::str::from_utf8(value).ok()?;
147 self.remaining = &input[end + 1..];
148 Some(value)
149 }
150
151 #[inline(always)]
157 pub fn plain_ascii_string(&mut self) -> Option<&'a [u8]> {
158 let input = self.remaining.strip_prefix(b"\"")?;
159 let end = memchr::memchr(b'"', input)?;
160 let value = &input[..end];
161 if value
162 .iter()
163 .any(|byte| !byte.is_ascii() || *byte == b'\\' || *byte < 0x20)
164 {
165 return None;
166 }
167 self.remaining = &input[end + 1..];
168 Some(value)
169 }
170
171 #[inline(always)]
173 pub fn unsigned(&mut self) -> Option<u64> {
174 let first = *self.remaining.first()?;
175 if !first.is_ascii_digit() {
176 return None;
177 }
178 if first == b'0' && self.remaining.get(1).is_some_and(u8::is_ascii_digit) {
179 return None;
180 }
181
182 let mut value = 0_u64;
183 let mut length = 0;
184 for &digit in self.remaining {
185 if !digit.is_ascii_digit() {
186 break;
187 }
188 value = value
189 .checked_mul(10)?
190 .checked_add(u64::from(digit - b'0'))?;
191 length += 1;
192 }
193 self.remaining = &self.remaining[length..];
194 Some(value)
195 }
196
197 #[inline(always)]
199 pub fn boolean(&mut self) -> Option<bool> {
200 if let Some(remaining) = self.remaining.strip_prefix(b"true") {
201 self.remaining = remaining;
202 Some(true)
203 } else if let Some(remaining) = self.remaining.strip_prefix(b"false") {
204 self.remaining = remaining;
205 Some(false)
206 } else {
207 None
208 }
209 }
210
211 #[must_use]
213 #[inline(always)]
214 pub const fn remaining(&self) -> &'a [u8] {
215 self.remaining
216 }
217
218 #[must_use]
220 #[inline(always)]
221 pub const fn is_finished(&self) -> bool {
222 self.remaining.is_empty()
223 }
224}