Skip to main content

sark_json/
input.rs

1use std::ops;
2
3use o3::buffer::write::consume;
4
5use crate::{
6    Error, Nesting,
7    parsing::Depth,
8    segment::{Atom, ValueKind},
9    source::{outputs, string::String},
10};
11
12#[derive(Clone, Copy, Eq, PartialEq)]
13pub enum Taken {
14    Yes,
15    No,
16}
17
18pub trait Input {
19    fn ws(&self, index: &mut usize);
20    fn take_byte(&self, index: &mut usize, want: u8) -> Taken;
21    fn expect_byte(&self, index: &mut usize, want: u8) -> crate::Result<()>;
22    fn expect_prop(&self, index: &mut usize, first: bool, want: &[u8]) -> crate::Result<()>;
23    fn finish(&self, index: &mut usize) -> crate::Result<()>;
24    fn name(&self, index: &mut usize) -> crate::Result<Name<'_>>;
25    fn take_null(&self, index: &mut usize) -> Taken;
26    fn skip_value(&self, index: &mut usize) -> crate::Result<()>;
27    #[doc(hidden)]
28    fn skip_depth(&self, index: &mut usize, depth: Depth) -> crate::Result<()>;
29
30    #[doc(hidden)]
31    fn string_span(&self, index: &mut usize) -> crate::Result<StringSpan<'_>>;
32
33    fn parse_u64(&self, index: &mut usize) -> crate::Result<u64>;
34    fn parse_bool(&self, index: &mut usize) -> crate::Result<bool>;
35}
36
37impl Input for [u8] {
38    fn ws(&self, index: &mut usize) {
39        *index = ws_at(self, *index);
40    }
41
42    fn take_byte(&self, index: &mut usize, want: u8) -> Taken {
43        match byte_at(self, *index, want) {
44            Some(next) => {
45                *index = next;
46                Taken::Yes
47            }
48            None => Taken::No,
49        }
50    }
51
52    fn expect_byte(&self, index: &mut usize, want: u8) -> crate::Result<()> {
53        let next = byte_at(self, *index, want).ok_or(Error::Invalid)?;
54        *index = next;
55        Ok(())
56    }
57
58    fn expect_prop(&self, index: &mut usize, first: bool, want: &[u8]) -> crate::Result<()> {
59        let next = prop_at(self, *index, first, want)?;
60        *index = next;
61        Ok(())
62    }
63
64    fn finish(&self, index: &mut usize) -> crate::Result<()> {
65        let next = ws_at(self, *index);
66        if next != self.len() {
67            return Err(Error::Invalid);
68        }
69        *index = next;
70        Ok(())
71    }
72
73    fn name(&self, index: &mut usize) -> crate::Result<Name<'_>> {
74        let (span, next) = string_at(self, *index)?;
75        *index = next;
76        Ok(Name::new(span))
77    }
78
79    fn take_null(&self, index: &mut usize) -> Taken {
80        match null_at(self, *index) {
81            Some(next) => {
82                *index = next;
83                Taken::Yes
84            }
85            None => Taken::No,
86        }
87    }
88
89    fn skip_value(&self, index: &mut usize) -> crate::Result<()> {
90        self.skip_depth(index, Depth::ROOT)
91    }
92
93    fn skip_depth(&self, index: &mut usize, depth: Depth) -> crate::Result<()> {
94        let next = value_with(self, ws_at(self, *index), None, depth)?;
95        *index = next;
96        Ok(())
97    }
98
99    fn string_span(&self, index: &mut usize) -> crate::Result<StringSpan<'_>> {
100        let (span, next) = string_at(self, *index)?;
101        *index = next;
102        Ok(span)
103    }
104
105    fn parse_u64(&self, index: &mut usize) -> crate::Result<u64> {
106        let first = *self.get(*index).ok_or(Error::Invalid)?;
107        if first == b'-' {
108            return Err(Error::Invalid);
109        }
110        let (ValueKind::Unsigned(value), next) = atom_with::<true>(self, *index, first, None)?
111        else {
112            return Err(Error::Invalid);
113        };
114        *index = next;
115        Ok(value)
116    }
117
118    fn parse_bool(&self, index: &mut usize) -> crate::Result<bool> {
119        let (value, next) = bool_at(self, *index)?;
120        *index = next;
121        Ok(value)
122    }
123}
124
125pub fn parse_i64(input: &[u8], index: &mut usize) -> crate::Result<i64> {
126    let first = *input.get(*index).ok_or(Error::Invalid)?;
127    let (kind, next) = atom_with::<true>(input, *index, first, None)?;
128    let value = kind.i64()?;
129    *index = next;
130    Ok(value)
131}
132
133#[derive(Clone, Copy)]
134pub(crate) struct Content<'a>(&'a [u8]);
135
136impl<'a> Content<'a> {
137    pub(crate) const fn bytes(self) -> &'a [u8] {
138        self.0
139    }
140}
141
142#[doc(hidden)]
143pub struct StringSpan<'a> {
144    pub(crate) content: Content<'a>,
145    pub(crate) start: usize,
146    pub(crate) end: usize,
147    pub(crate) escaped: bool,
148    pub(crate) decoded_len: usize,
149}
150
151#[doc(hidden)]
152pub struct Name<'a> {
153    content: Content<'a>,
154    decoded_len: usize,
155    escaped: bool,
156}
157
158impl<'a> Name<'a> {
159    fn new(span: StringSpan<'a>) -> Self {
160        Self {
161            content: span.content,
162            decoded_len: span.decoded_len,
163            escaped: span.escaped,
164        }
165    }
166
167    pub const fn len(&self) -> usize {
168        self.decoded_len
169    }
170
171    pub const fn is_empty(&self) -> bool {
172        self.decoded_len == 0
173    }
174
175    pub fn is_equal(&self, expected: &[u8]) -> bool {
176        if !self.escaped {
177            return self.content.bytes() == expected;
178        }
179        if self.decoded_len != expected.len() {
180            return false;
181        }
182        outputs::Decoder::new(self.content).eq(expected.iter().copied())
183    }
184}
185
186fn prop_at(input: &[u8], index: usize, first: bool, want: &[u8]) -> crate::Result<usize> {
187    let mut next = ws_at(input, index);
188    if !first {
189        next = byte_at(input, next, b',').ok_or(Error::Invalid)?;
190        next = ws_at(input, next);
191    }
192    let (span, end) = string_at(input, next)?;
193    if !Name::new(span).is_equal(want) {
194        return Err(Error::Invalid);
195    }
196    next = ws_at(input, end);
197    next = byte_at(input, next, b':').ok_or(Error::Invalid)?;
198    Ok(ws_at(input, next))
199}
200
201#[derive(Clone, Copy)]
202struct Limit {
203    start: usize,
204    capacity: usize,
205    overhead: usize,
206}
207
208impl Limit {
209    fn check(self, end: usize) -> crate::Result<()> {
210        let len = (end - self.start).saturating_sub(self.overhead);
211        consume::Error::check(len, self.capacity).map_err(Error::Token)
212    }
213
214    fn scan_end(self, len: usize) -> usize {
215        len.min(self.start.saturating_add(self.capacity + self.overhead + 1))
216    }
217}
218
219fn check_limit(limit: Option<Limit>, end: usize) -> crate::Result<()> {
220    if let Some(limit) = limit {
221        limit.check(end)?;
222    }
223    Ok(())
224}
225
226pub(crate) fn inline_plain<'a>(
227    input: &'a [u8],
228    index: &mut usize,
229    capacity: u8,
230) -> crate::Result<Content<'a>> {
231    let limit = Limit {
232        start: *index,
233        capacity: usize::from(capacity),
234        overhead: 2,
235    };
236    let (span, end) = string_with(input, *index, Some(limit))?;
237    if span.escaped {
238        return Err(Error::Invalid);
239    }
240    *index = end;
241    Ok(span.content)
242}
243
244pub(crate) fn inline_depth(
245    input: &[u8],
246    index: &mut usize,
247    capacity: u8,
248    depth: Depth,
249) -> crate::Result<ops::Range<usize>> {
250    let start = ws_at(input, *index);
251    let limit = Limit {
252        start,
253        capacity: usize::from(capacity),
254        overhead: 0,
255    };
256    let end = value_with(input, start, Some(limit), depth)?;
257    *index = end;
258    Ok(start..end)
259}
260
261fn value_with(
262    input: &[u8],
263    index: usize,
264    limit: Option<Limit>,
265    depth: Depth,
266) -> crate::Result<usize> {
267    match input.get(index) {
268        Some(b'"') => string_with(input, index, limit).map(|(_, end)| end),
269        Some(&open) if matches!(open, b'{' | b'[') => group_at(input, index, open, limit, depth),
270        Some(&first) if matches!(first, b't' | b'f' | b'n' | b'-' | b'0'..=b'9') => {
271            atom_with::<false>(input, index, first, limit).map(|(_, end)| end)
272        }
273        _ => Err(Error::Invalid),
274    }
275}
276
277#[inline(always)]
278fn atom_with<const INTEGER: bool>(
279    input: &[u8],
280    index: usize,
281    first: u8,
282    limit: Option<Limit>,
283) -> crate::Result<(ValueKind, usize)> {
284    let mut atom = Atom::new(first, INTEGER)?;
285    let mut next = index;
286    while let Some(&byte) = input.get(next) {
287        if is_whitespace(byte) || matches!(byte, b',' | b'}' | b']') {
288            break;
289        }
290        atom = atom.step(byte)?;
291        if INTEGER && !matches!(atom, Atom::Number { value: Some(_), .. }) {
292            return Err(Error::Invalid);
293        }
294        next += 1;
295        check_limit(limit, next)?;
296    }
297    Ok((atom.finish()?, next))
298}
299
300fn string_at(input: &[u8], index: usize) -> crate::Result<(StringSpan<'_>, usize)> {
301    string_with(input, index, None)
302}
303
304fn string_with(
305    input: &[u8],
306    index: usize,
307    limit: Option<Limit>,
308) -> crate::Result<(StringSpan<'_>, usize)> {
309    let start = byte_at(input, index, b'"').ok_or(Error::Invalid)?;
310    check_limit(limit, start)?;
311    let mut string = String::new();
312    let mut next = start;
313    let scan_end = limit.map_or(input.len(), |limit| limit.scan_end(input.len()));
314    while next < input.len() {
315        // SAFETY: the preceding limit check and loop bound admit this same-input suffix.
316        let skipped = string.skip(unsafe { input.get_unchecked(next..scan_end) });
317        next += skipped.len();
318        check_limit(limit, next)?;
319        let Some(&byte) = input.get(next) else {
320            break;
321        };
322        let step = string.step(byte)?;
323        check_limit(limit, next + 1)?;
324        if step.is_none() {
325            let removed = string.removed();
326            return Ok((
327                StringSpan {
328                    // SAFETY: next advanced from start within this unchanged input.
329                    content: Content(unsafe { input.get_unchecked(start..next) }),
330                    start,
331                    end: next,
332                    escaped: removed != 0,
333                    decoded_len: next - start - removed,
334                },
335                next + 1,
336            ));
337        }
338        next += 1;
339    }
340    Err(Error::Invalid)
341}
342
343fn group_at(
344    input: &[u8],
345    index: usize,
346    open: u8,
347    limit: Option<Limit>,
348    depth: Depth,
349) -> crate::Result<usize> {
350    let mut next = byte_at(input, index, open).ok_or(Error::Invalid)?;
351    check_limit(limit, next)?;
352    let mut nesting = Nesting::start_depth(open, depth)?;
353    let scan_end = limit.map_or(input.len(), |limit| limit.scan_end(input.len()));
354    while next < input.len() {
355        // SAFETY: the preceding limit check and loop bound admit this same-input suffix.
356        let skipped = nesting.skip(unsafe { input.get_unchecked(next..scan_end) });
357        next += skipped.len();
358        check_limit(limit, next)?;
359        let Some(&byte) = input.get(next) else {
360            break;
361        };
362        next += 1;
363        let done = nesting.step(byte)?;
364        check_limit(limit, next)?;
365        if done {
366            return Ok(next);
367        }
368    }
369    Err(Error::Invalid)
370}
371
372fn bool_at(input: &[u8], index: usize) -> crate::Result<(bool, usize)> {
373    let first = *input.get(index).ok_or(Error::Invalid)?;
374    if !matches!(first, b't' | b'f') {
375        return Err(Error::Invalid);
376    }
377    let (kind, next) = atom_with::<false>(input, index, first, None)?;
378    match kind {
379        ValueKind::True => Ok((true, next)),
380        ValueKind::False => Ok((false, next)),
381        _ => Err(Error::Invalid),
382    }
383}
384
385pub(crate) const fn is_whitespace(byte: u8) -> bool {
386    matches!(byte, b' ' | b'\t' | b'\n' | b'\r')
387}
388
389fn ws_at(input: &[u8], index: usize) -> usize {
390    let mut next = index;
391    while input.get(next).is_some_and(|byte| is_whitespace(*byte)) {
392        next += 1;
393    }
394    next
395}
396
397fn byte_at(input: &[u8], index: usize, want: u8) -> Option<usize> {
398    if input.get(index) == Some(&want) {
399        index.checked_add(1)
400    } else {
401        None
402    }
403}
404
405fn null_at(input: &[u8], index: usize) -> Option<usize> {
406    let end = index.checked_add(4)?;
407    (input.get(index..end) == Some(b"null")).then_some(end)
408}
409
410const _: () = assert!(size_of::<Taken>() == size_of::<bool>());