Skip to main content

oci_zero/
json.rs

1use core::{char, str};
2
3const MAX_DEPTH: usize = 64;
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6pub struct JsonString<'a> {
7    encoded: &'a str,
8    escaped: bool,
9}
10
11impl<'a> JsonString<'a> {
12    pub const fn encoded(&self) -> &'a str {
13        self.encoded
14    }
15
16    pub fn as_str(&self) -> Option<&'a str> {
17        (!self.escaped).then_some(self.encoded)
18    }
19
20    pub fn decode_into<'buffer>(
21        &self,
22        buffer: &'buffer mut [u8],
23    ) -> Result<&'buffer str, JsonError> {
24        if !self.escaped {
25            let destination = buffer
26                .get_mut(..self.encoded.len())
27                .ok_or(JsonError::BufferTooSmall)?;
28            destination.copy_from_slice(self.encoded.as_bytes());
29            return str::from_utf8(destination).map_err(|_| JsonError::InvalidUtf8);
30        }
31
32        let mut output = 0;
33        for character in self.decoded_chars() {
34            let mut encoded = [0; 4];
35            copy_decoded(
36                buffer,
37                &mut output,
38                character?.encode_utf8(&mut encoded).as_bytes(),
39            )?;
40        }
41        str::from_utf8(&buffer[..output]).map_err(|_| JsonError::InvalidUtf8)
42    }
43
44    pub(crate) fn decoded_eq_ascii(&self, expected: &str) -> bool {
45        if !expected.is_ascii() {
46            return false;
47        }
48        if !self.escaped {
49            return self.encoded == expected;
50        }
51        self.decoded_chars().eq(expected.chars().map(Ok))
52    }
53
54    fn decoded_chars(&self) -> impl Iterator<Item = Result<char, JsonError>> + '_ {
55        let mut source = self.encoded;
56        core::iter::from_fn(move || {
57            let first = source.chars().next()?;
58            let result = if first == '\\' {
59                escaped_character(source.as_bytes(), 0)
60            } else {
61                Ok((first, first.len_utf8()))
62            };
63            source = match result {
64                Ok((_, length)) => &source[length..],
65                Err(_) => "",
66            };
67            Some(result.map(|(character, _)| character))
68        })
69    }
70}
71
72#[derive(Clone, Copy, Debug, Eq, PartialEq, derive_more::Display)]
73pub enum JsonError {
74    #[display("JSON is not UTF-8")]
75    InvalidUtf8,
76    #[display("invalid JSON syntax")]
77    InvalidSyntax,
78    #[display("JSON nesting is too deep")]
79    NestingTooDeep,
80    #[display("invalid JSON string escape")]
81    InvalidEscape,
82    #[display("invalid JSON number")]
83    InvalidNumber,
84    #[display("unexpected JSON value type")]
85    WrongType,
86    #[display("missing JSON field {_0}")]
87    MissingField(&'static str),
88    #[display("duplicate JSON field {_0}")]
89    DuplicateField(&'static str),
90    #[display("JSON string output buffer is too small")]
91    BufferTooSmall,
92}
93
94#[derive(Clone, Copy, Debug, Eq, PartialEq)]
95pub(crate) struct Value<'a> {
96    bytes: &'a [u8],
97}
98
99impl<'a> Value<'a> {
100    pub(crate) fn parse_document(bytes: &'a [u8]) -> Result<Self, JsonError> {
101        str::from_utf8(bytes).map_err(|_| JsonError::InvalidUtf8)?;
102        let start = whitespace(bytes, 0);
103        let end = parse_value(bytes, start, 0)?;
104        if whitespace(bytes, end) != bytes.len() {
105            return Err(JsonError::InvalidSyntax);
106        }
107        Ok(Self {
108            bytes: &bytes[start..end],
109        })
110    }
111
112    pub(crate) fn object(self) -> Result<Object<'a>, JsonError> {
113        if self.bytes.first() != Some(&b'{') {
114            return Err(JsonError::WrongType);
115        }
116        Ok(Object { bytes: self.bytes })
117    }
118
119    pub(crate) fn array(self) -> Result<Array<'a>, JsonError> {
120        if self.bytes.first() != Some(&b'[') {
121            return Err(JsonError::WrongType);
122        }
123        Ok(Array { bytes: self.bytes })
124    }
125
126    pub(crate) fn string(self) -> Result<JsonString<'a>, JsonError> {
127        if self.bytes.first() != Some(&b'"') || self.bytes.last() != Some(&b'"') {
128            return Err(JsonError::WrongType);
129        }
130        let encoded = str::from_utf8(&self.bytes[1..self.bytes.len() - 1])
131            .map_err(|_| JsonError::InvalidUtf8)?;
132        Ok(JsonString {
133            encoded,
134            escaped: encoded.as_bytes().contains(&b'\\'),
135        })
136    }
137
138    pub(crate) fn u64(self) -> Result<u64, JsonError> {
139        let value = str::from_utf8(self.bytes).map_err(|_| JsonError::InvalidUtf8)?;
140        if value.is_empty() || !value.bytes().all(|byte| byte.is_ascii_digit()) {
141            return Err(JsonError::WrongType);
142        }
143        value.parse().map_err(|_| JsonError::InvalidNumber)
144    }
145
146    pub(crate) fn is_null(self) -> bool {
147        self.bytes == b"null"
148    }
149}
150
151#[derive(Clone, Copy, Debug, Eq, PartialEq)]
152pub(crate) struct Object<'a> {
153    bytes: &'a [u8],
154}
155
156impl<'a> Object<'a> {
157    pub(crate) fn get(self, name: &'static str) -> Result<Option<Value<'a>>, JsonError> {
158        let mut found = None;
159        for member in self.iter() {
160            let (key, value) = member?;
161            if key.decoded_eq_ascii(name) {
162                if found.is_some() {
163                    return Err(JsonError::DuplicateField(name));
164                }
165                found = Some(value);
166            }
167        }
168        Ok(found)
169    }
170
171    pub(crate) fn required(self, name: &'static str) -> Result<Value<'a>, JsonError> {
172        self.get(name)?.ok_or(JsonError::MissingField(name))
173    }
174
175    pub(crate) fn iter(self) -> ObjectIter<'a> {
176        ObjectIter(Container::new(self.bytes, 0, b'}'))
177    }
178}
179
180pub(crate) struct ObjectIter<'a>(Container<'a>);
181
182impl<'a> Iterator for ObjectIter<'a> {
183    type Item = Result<(JsonString<'a>, Value<'a>), JsonError>;
184
185    fn next(&mut self) -> Option<Self::Item> {
186        self.0.next(0).map(|member| {
187            let (key, value) = member?;
188            Ok((key.expect("object member has a key"), value))
189        })
190    }
191}
192
193#[derive(Clone, Copy, Debug, Eq, PartialEq)]
194pub(crate) struct Array<'a> {
195    bytes: &'a [u8],
196}
197
198impl<'a> Array<'a> {
199    pub(crate) fn iter(self) -> ArrayIter<'a> {
200        ArrayIter(Container::new(self.bytes, 0, b']'))
201    }
202}
203
204pub(crate) struct ArrayIter<'a>(Container<'a>);
205
206impl<'a> Iterator for ArrayIter<'a> {
207    type Item = Result<Value<'a>, JsonError>;
208
209    fn next(&mut self) -> Option<Self::Item> {
210        self.0.next(0).map(|member| member.map(|(_, value)| value))
211    }
212}
213
214/// Shared cursor for validating containers and iterating their borrowed values.
215struct Container<'a> {
216    bytes: &'a [u8],
217    closing: u8,
218    first: bool,
219    finished: bool,
220}
221
222type Member<'a> = (Option<JsonString<'a>>, Value<'a>);
223
224impl<'a> Container<'a> {
225    fn new(bytes: &'a [u8], opening: usize, closing: u8) -> Self {
226        Self {
227            bytes: &bytes[opening + 1..],
228            closing,
229            first: true,
230            finished: false,
231        }
232    }
233
234    fn next(&mut self, depth: usize) -> Option<Result<Member<'a>, JsonError>> {
235        if self.finished {
236            return None;
237        }
238        let result = self.next_inner(depth);
239        if result.is_err() {
240            self.finished = true;
241        }
242        result.transpose()
243    }
244
245    fn next_inner(&mut self, depth: usize) -> Result<Option<Member<'a>>, JsonError> {
246        self.bytes = &self.bytes[whitespace(self.bytes, 0)..];
247        if self.bytes.first() == Some(&self.closing) {
248            self.bytes = &self.bytes[1..];
249            self.finished = true;
250            return Ok(None);
251        }
252        if !self.first {
253            if self.bytes.first() != Some(&b',') {
254                return Err(JsonError::InvalidSyntax);
255            }
256            self.bytes = &self.bytes[whitespace(self.bytes, 1)..];
257        }
258        self.first = false;
259        let key = if self.closing == b'}' {
260            if self.bytes.first() != Some(&b'"') {
261                return Err(JsonError::InvalidSyntax);
262            }
263            let end = parse_string(self.bytes, 0)?;
264            let key = Value {
265                bytes: &self.bytes[..end],
266            }
267            .string()?;
268            self.bytes = &self.bytes[whitespace(self.bytes, end)..];
269            if self.bytes.first() != Some(&b':') {
270                return Err(JsonError::InvalidSyntax);
271            }
272            self.bytes = &self.bytes[whitespace(self.bytes, 1)..];
273            Some(key)
274        } else {
275            None
276        };
277        let end = parse_value(self.bytes, 0, depth)?;
278        let value = Value {
279            bytes: &self.bytes[..end],
280        };
281        self.bytes = &self.bytes[end..];
282        Ok(Some((key, value)))
283    }
284}
285
286fn parse_value(bytes: &[u8], position: usize, depth: usize) -> Result<usize, JsonError> {
287    if depth > MAX_DEPTH {
288        return Err(JsonError::NestingTooDeep);
289    }
290    match bytes.get(position).copied() {
291        Some(b'"') => parse_string(bytes, position),
292        Some(b'{') => parse_container(bytes, position, b'}', depth + 1),
293        Some(b'[') => parse_container(bytes, position, b']', depth + 1),
294        Some(b't') => literal(bytes, position, b"true"),
295        Some(b'f') => literal(bytes, position, b"false"),
296        Some(b'n') => literal(bytes, position, b"null"),
297        Some(b'-' | b'0'..=b'9') => parse_number(bytes, position),
298        _ => Err(JsonError::InvalidSyntax),
299    }
300}
301
302fn parse_container(
303    bytes: &[u8],
304    position: usize,
305    closing: u8,
306    depth: usize,
307) -> Result<usize, JsonError> {
308    let mut container = Container::new(bytes, position, closing);
309    while let Some(member) = container.next(depth) {
310        member?;
311    }
312    Ok(bytes.len() - container.bytes.len())
313}
314
315fn parse_string(bytes: &[u8], position: usize) -> Result<usize, JsonError> {
316    let mut index = position + 1;
317    while let Some(byte) = bytes.get(index).copied() {
318        match byte {
319            b'"' => return Ok(index + 1),
320            b'\\' => {
321                let (_, next) = escaped_character(bytes, index)?;
322                index = next;
323            }
324            0..=0x1f => return Err(JsonError::InvalidSyntax),
325            _ => index += 1,
326        }
327    }
328    Err(JsonError::InvalidSyntax)
329}
330
331fn escaped_character(bytes: &[u8], slash: usize) -> Result<(char, usize), JsonError> {
332    let character = match bytes.get(slash + 1).copied() {
333        Some(value @ (b'"' | b'\\' | b'/')) => char::from(value),
334        Some(b'b') => '\u{0008}',
335        Some(b'f') => '\u{000c}',
336        Some(b'n') => '\n',
337        Some(b'r') => '\r',
338        Some(b't') => '\t',
339        Some(b'u') => return unicode_character(bytes, slash + 2),
340        _ => return Err(JsonError::InvalidEscape),
341    };
342    Ok((character, slash + 2))
343}
344
345fn unicode_character(bytes: &[u8], start: usize) -> Result<(char, usize), JsonError> {
346    let first = unicode_escape(bytes, start)?;
347    let next = start + 4;
348    let scalar = if (0xd800..=0xdbff).contains(&first) {
349        if bytes.get(next..next + 2) != Some(b"\\u") {
350            return Err(JsonError::InvalidEscape);
351        }
352        let second = unicode_escape(bytes, next + 2)?;
353        if !(0xdc00..=0xdfff).contains(&second) {
354            return Err(JsonError::InvalidEscape);
355        }
356        let high = u32::from(first - 0xd800);
357        let low = u32::from(second - 0xdc00);
358        (0x10000 + (high << 10) + low, next + 6)
359    } else if (0xdc00..=0xdfff).contains(&first) {
360        return Err(JsonError::InvalidEscape);
361    } else {
362        (u32::from(first), next)
363    };
364    let character = char::from_u32(scalar.0).ok_or(JsonError::InvalidEscape)?;
365    Ok((character, scalar.1))
366}
367
368fn unicode_escape(bytes: &[u8], start: usize) -> Result<u16, JsonError> {
369    let digits = bytes
370        .get(start..start + 4)
371        .ok_or(JsonError::InvalidEscape)?;
372    let mut value = 0u16;
373    for digit in digits {
374        // Exactly four hex digits fit in u16.
375        value = (value << 4) | u16::from(hex(*digit).ok_or(JsonError::InvalidEscape)?);
376    }
377    Ok(value)
378}
379
380fn hex(byte: u8) -> Option<u8> {
381    match byte {
382        b'0'..=b'9' => Some(byte - b'0'),
383        b'a'..=b'f' => Some(byte - b'a' + 10),
384        b'A'..=b'F' => Some(byte - b'A' + 10),
385        _ => None,
386    }
387}
388
389fn parse_number(bytes: &[u8], mut position: usize) -> Result<usize, JsonError> {
390    if bytes.get(position) == Some(&b'-') {
391        position += 1;
392    }
393    position = match bytes.get(position) {
394        Some(b'0') => position + 1,
395        Some(b'1'..=b'9') => decimal_digits(bytes, position)?,
396        _ => return Err(JsonError::InvalidNumber),
397    };
398    if bytes.get(position) == Some(&b'.') {
399        position = decimal_digits(bytes, position + 1)?;
400    }
401    if matches!(bytes.get(position), Some(b'e' | b'E')) {
402        position += 1;
403        if matches!(bytes.get(position), Some(b'+' | b'-')) {
404            position += 1;
405        }
406        position = decimal_digits(bytes, position)?;
407    }
408    Ok(position)
409}
410
411fn decimal_digits(bytes: &[u8], start: usize) -> Result<usize, JsonError> {
412    let mut position = start;
413    while matches!(bytes.get(position), Some(b'0'..=b'9')) {
414        position += 1;
415    }
416    if position == start {
417        return Err(JsonError::InvalidNumber);
418    }
419    Ok(position)
420}
421
422fn literal(bytes: &[u8], position: usize, literal: &[u8]) -> Result<usize, JsonError> {
423    if bytes.get(position..position + literal.len()) == Some(literal) {
424        Ok(position + literal.len())
425    } else {
426        Err(JsonError::InvalidSyntax)
427    }
428}
429
430fn whitespace(bytes: &[u8], mut position: usize) -> usize {
431    while matches!(bytes.get(position), Some(b' ' | b'\n' | b'\r' | b'\t')) {
432        position += 1;
433    }
434    position
435}
436
437fn copy_decoded(buffer: &mut [u8], output: &mut usize, bytes: &[u8]) -> Result<(), JsonError> {
438    crate::buffer::append(buffer, output, bytes).map_err(|_| JsonError::BufferTooSmall)
439}
440
441#[cfg(test)]
442mod tests {
443    use std::{format, string::ToString};
444
445    use super::{parse_value, JsonError, Value, MAX_DEPTH};
446
447    #[test]
448    fn validates_and_iterates_nested_json() {
449        let value = Value::parse_document(br#" {"a":[1,{"b":true}],"skip":null} "#).unwrap();
450        let object = value.object().unwrap();
451        let array = object.required("a").unwrap().array().unwrap();
452        assert_eq!(array.iter().count(), 2);
453        assert!(object.get("missing").unwrap().is_none());
454    }
455
456    #[test]
457    fn decodes_strings_and_rejects_bad_surrogates() {
458        let value = Value::parse_document(br#""hello\n\u263a\ud83d\ude00""#).unwrap();
459        let string = value.string().unwrap();
460        let mut output = [0; 32];
461        assert_eq!(string.decode_into(&mut output).unwrap(), "hello\n☺😀");
462        assert_eq!(
463            Value::parse_document(br#""\ud83d!""#),
464            Err(JsonError::InvalidEscape)
465        );
466    }
467
468    #[test]
469    fn rejects_trailing_and_duplicate_requested_fields() {
470        assert!(Value::parse_document(b"{} x").is_err());
471        let object = Value::parse_document(br#"{"a":1,"\u0061":2}"#)
472            .unwrap()
473            .object()
474            .unwrap();
475        assert_eq!(object.get("a"), Err(JsonError::DuplicateField("a")));
476    }
477
478    #[test]
479    fn decodes_every_simple_escape_and_uppercase_hex() {
480        let value = Value::parse_document(br#""\"\\\/\b\f\n\r\t\u004A\u00AF""#).unwrap();
481        let string = value.string().unwrap();
482        let mut output = [0; 32];
483        assert_eq!(
484            string.decode_into(&mut output).unwrap().as_bytes(),
485            b"\"\\/\x08\x0c\n\r\tJ\xc2\xaf"
486        );
487
488        let positioned_backslash = Value::parse_document(br#""a\\b""#)
489            .unwrap()
490            .string()
491            .unwrap();
492        assert_eq!(
493            positioned_backslash.decode_into(&mut output).unwrap(),
494            "a\\b"
495        );
496    }
497
498    #[test]
499    fn exposes_unescaped_strings_and_checks_decoded_ascii_exactly() {
500        let plain = Value::parse_document(br#""plain""#)
501            .unwrap()
502            .string()
503            .unwrap();
504        assert_eq!(plain.encoded(), "plain");
505        assert_eq!(plain.as_str(), Some("plain"));
506        assert!(plain.decoded_eq_ascii("plain"));
507        assert!(!plain.decoded_eq_ascii("other"));
508        assert!(!plain.decoded_eq_ascii("£"));
509
510        let escaped = Value::parse_document(br#""\u0062""#)
511            .unwrap()
512            .string()
513            .unwrap();
514        assert_eq!(escaped.as_str(), None);
515        assert!(escaped.decoded_eq_ascii("b"));
516        assert!(!escaped.decoded_eq_ascii("a"));
517    }
518
519    #[test]
520    fn rejects_raw_control_characters_and_small_decode_buffers() {
521        assert_eq!(
522            Value::parse_document(b"\"line\nfeed\""),
523            Err(JsonError::InvalidSyntax)
524        );
525
526        let plain = Value::parse_document(br#""hello""#)
527            .unwrap()
528            .string()
529            .unwrap();
530        assert_eq!(
531            plain.decode_into(&mut [0; 4]),
532            Err(JsonError::BufferTooSmall)
533        );
534        let escaped = Value::parse_document(br#""\u263a""#)
535            .unwrap()
536            .string()
537            .unwrap();
538        assert_eq!(
539            escaped.decode_into(&mut [0; 2]),
540            Err(JsonError::BufferTooSmall)
541        );
542    }
543
544    #[test]
545    fn accepts_empty_containers_and_false_literal() {
546        assert_eq!(
547            Value::parse_document(b"{}")
548                .unwrap()
549                .object()
550                .unwrap()
551                .iter()
552                .count(),
553            0
554        );
555        assert_eq!(
556            Value::parse_document(b"[]")
557                .unwrap()
558                .array()
559                .unwrap()
560                .iter()
561                .count(),
562            0
563        );
564        assert!(Value::parse_document(b"false").is_ok());
565    }
566
567    #[test]
568    fn rejects_malformed_container_members_and_fuses_iterators() {
569        for bytes in [
570            b"[1,]".as_slice(),
571            b"[,1]",
572            b"[1 2]",
573            b"[1",
574            b"{a:1}",
575            b"{\"a\" 1}",
576            b"{\"a\":}",
577            b"{\"a\":1,}",
578            b"{\"a\":1 \"b\":2}",
579        ] {
580            assert_eq!(
581                Value::parse_document(bytes),
582                Err(JsonError::InvalidSyntax),
583                "{bytes:?}"
584            );
585        }
586        let mut array = Value { bytes: b"[1,]" }.array().unwrap().iter();
587        assert_eq!(array.next().unwrap().unwrap().u64(), Ok(1));
588        assert_eq!(array.next(), Some(Err(JsonError::InvalidSyntax)));
589        assert_eq!(array.next(), None);
590        let mut object = Value {
591            bytes: b"{\"a\" 1}",
592        }
593        .object()
594        .unwrap()
595        .iter();
596        assert_eq!(object.next(), Some(Err(JsonError::InvalidSyntax)));
597        assert_eq!(object.next(), None);
598    }
599
600    #[test]
601    fn enforces_nesting_at_the_exact_limit() {
602        assert_eq!(parse_value(b"null", 0, MAX_DEPTH), Ok(4));
603        assert_eq!(
604            parse_value(b"null", 0, MAX_DEPTH + 1),
605            Err(JsonError::NestingTooDeep)
606        );
607        assert_eq!(
608            parse_value(b"null", 0, MAX_DEPTH + 2),
609            Err(JsonError::NestingTooDeep)
610        );
611
612        let arrays_at_limit = format!("{}null{}", "[".repeat(MAX_DEPTH), "]".repeat(MAX_DEPTH));
613        assert!(Value::parse_document(arrays_at_limit.as_bytes()).is_ok());
614        let arrays_too_deep = format!(
615            "{}null{}",
616            "[".repeat(MAX_DEPTH + 1),
617            "]".repeat(MAX_DEPTH + 1)
618        );
619        assert_eq!(
620            Value::parse_document(arrays_too_deep.as_bytes()),
621            Err(JsonError::NestingTooDeep)
622        );
623
624        let objects_at_limit = format!(
625            "{}null{}",
626            "{\"a\":".repeat(MAX_DEPTH),
627            "}".repeat(MAX_DEPTH)
628        );
629        assert!(Value::parse_document(objects_at_limit.as_bytes()).is_ok());
630        let objects_too_deep = format!(
631            "{}null{}",
632            "{\"a\":".repeat(MAX_DEPTH + 1),
633            "}".repeat(MAX_DEPTH + 1)
634        );
635        assert_eq!(
636            Value::parse_document(objects_too_deep.as_bytes()),
637            Err(JsonError::NestingTooDeep)
638        );
639    }
640
641    #[test]
642    fn validates_each_json_number_form() {
643        for number in ["0", "-1", "10", "1.25", "1e2", "1E+2", "1e-2"] {
644            assert!(
645                Value::parse_document(number.as_bytes()).is_ok(),
646                "number={number:?}"
647            );
648        }
649        for number in ["-", "01", "1.", "1e", "1e+", "1e-"] {
650            assert!(
651                Value::parse_document(number.as_bytes()).is_err(),
652                "number={number:?}"
653            );
654        }
655
656        assert_eq!(Value { bytes: b"" }.u64(), Err(JsonError::WrongType));
657        assert_eq!(Value { bytes: b"12x" }.u64(), Err(JsonError::WrongType));
658        assert_eq!(Value { bytes: b"42" }.u64(), Ok(42));
659        assert_eq!(
660            Value {
661                bytes: b"18446744073709551616"
662            }
663            .u64(),
664            Err(JsonError::InvalidNumber)
665        );
666    }
667
668    #[test]
669    fn string_conversion_checks_both_quotes() {
670        assert_eq!(
671            Value { bytes: b"plain\"" }.string(),
672            Err(JsonError::WrongType)
673        );
674        assert_eq!(
675            Value { bytes: b"\"plain" }.string(),
676            Err(JsonError::WrongType)
677        );
678    }
679
680    #[test]
681    fn formats_every_json_error() {
682        let cases = [
683            (JsonError::InvalidUtf8, "JSON is not UTF-8"),
684            (JsonError::InvalidSyntax, "invalid JSON syntax"),
685            (JsonError::NestingTooDeep, "JSON nesting is too deep"),
686            (JsonError::InvalidEscape, "invalid JSON string escape"),
687            (JsonError::InvalidNumber, "invalid JSON number"),
688            (JsonError::WrongType, "unexpected JSON value type"),
689            (JsonError::MissingField("name"), "missing JSON field name"),
690            (
691                JsonError::DuplicateField("name"),
692                "duplicate JSON field name",
693            ),
694            (
695                JsonError::BufferTooSmall,
696                "JSON string output buffer is too small",
697            ),
698        ];
699        for (error, expected) in cases {
700            assert_eq!(error.to_string(), expected);
701        }
702    }
703}