Skip to main content

oci_zero/
json.rs

1use core::{char, fmt, 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 source = self.encoded.as_bytes();
33        let mut input = 0;
34        let mut output = 0;
35        while input < source.len() {
36            if source[input] != b'\\' {
37                let start = input;
38                while input < source.len() && source[input] != b'\\' {
39                    input += 1;
40                }
41                copy_decoded(buffer, &mut output, &source[start..input])?;
42                continue;
43            }
44            let (character, next) = escaped_character(source, input)?;
45            input = next;
46            let mut encoded = [0; 4];
47            copy_decoded(
48                buffer,
49                &mut output,
50                character.encode_utf8(&mut encoded).as_bytes(),
51            )?;
52        }
53        str::from_utf8(&buffer[..output]).map_err(|_| JsonError::InvalidUtf8)
54    }
55
56    pub(crate) fn decoded_eq_ascii(&self, expected: &str) -> bool {
57        if !expected.is_ascii() {
58            return false;
59        }
60        if !self.escaped {
61            return self.encoded == expected;
62        }
63        let source = self.encoded.as_bytes();
64        let expected = expected.as_bytes();
65        let mut input = 0;
66        let mut output = 0;
67        while input < source.len() {
68            if source[input] == b'\\' {
69                let Ok((character, next)) = escaped_character(source, input) else {
70                    return false;
71                };
72                if !character.is_ascii() || expected.get(output).copied() != Some(character as u8) {
73                    return false;
74                }
75                input = next;
76                output += 1;
77            } else {
78                if expected.get(output).copied() != Some(source[input]) {
79                    return false;
80                }
81                input += 1;
82                output += 1;
83            }
84        }
85        output == expected.len()
86    }
87}
88
89#[derive(Clone, Copy, Debug, Eq, PartialEq)]
90pub enum JsonError {
91    InvalidUtf8,
92    InvalidSyntax,
93    NestingTooDeep,
94    InvalidEscape,
95    InvalidNumber,
96    WrongType,
97    MissingField(&'static str),
98    DuplicateField(&'static str),
99    BufferTooSmall,
100}
101
102impl fmt::Display for JsonError {
103    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
104        match self {
105            Self::InvalidUtf8 => formatter.write_str("JSON is not UTF-8"),
106            Self::InvalidSyntax => formatter.write_str("invalid JSON syntax"),
107            Self::NestingTooDeep => formatter.write_str("JSON nesting is too deep"),
108            Self::InvalidEscape => formatter.write_str("invalid JSON string escape"),
109            Self::InvalidNumber => formatter.write_str("invalid JSON number"),
110            Self::WrongType => formatter.write_str("unexpected JSON value type"),
111            Self::MissingField(field) => write!(formatter, "missing JSON field {field}"),
112            Self::DuplicateField(field) => write!(formatter, "duplicate JSON field {field}"),
113            Self::BufferTooSmall => formatter.write_str("JSON string output buffer is too small"),
114        }
115    }
116}
117
118#[derive(Clone, Copy, Debug, Eq, PartialEq)]
119pub(crate) struct Value<'a> {
120    bytes: &'a [u8],
121}
122
123impl<'a> Value<'a> {
124    pub(crate) fn parse_document(bytes: &'a [u8]) -> Result<Self, JsonError> {
125        str::from_utf8(bytes).map_err(|_| JsonError::InvalidUtf8)?;
126        let start = whitespace(bytes, 0);
127        let end = parse_value(bytes, start, 0)?;
128        if whitespace(bytes, end) != bytes.len() {
129            return Err(JsonError::InvalidSyntax);
130        }
131        Ok(Self {
132            bytes: &bytes[start..end],
133        })
134    }
135
136    pub(crate) fn object(self) -> Result<Object<'a>, JsonError> {
137        if self.bytes.first() != Some(&b'{') {
138            return Err(JsonError::WrongType);
139        }
140        Ok(Object { bytes: self.bytes })
141    }
142
143    pub(crate) fn array(self) -> Result<Array<'a>, JsonError> {
144        if self.bytes.first() != Some(&b'[') {
145            return Err(JsonError::WrongType);
146        }
147        Ok(Array { bytes: self.bytes })
148    }
149
150    pub(crate) fn string(self) -> Result<JsonString<'a>, JsonError> {
151        if self.bytes.first() != Some(&b'"') || self.bytes.last() != Some(&b'"') {
152            return Err(JsonError::WrongType);
153        }
154        let encoded = str::from_utf8(&self.bytes[1..self.bytes.len() - 1])
155            .map_err(|_| JsonError::InvalidUtf8)?;
156        Ok(JsonString {
157            encoded,
158            escaped: encoded.as_bytes().contains(&b'\\'),
159        })
160    }
161
162    pub(crate) fn u64(self) -> Result<u64, JsonError> {
163        let value = str::from_utf8(self.bytes).map_err(|_| JsonError::InvalidUtf8)?;
164        if value.is_empty() || !value.bytes().all(|byte| byte.is_ascii_digit()) {
165            return Err(JsonError::WrongType);
166        }
167        value.parse().map_err(|_| JsonError::InvalidNumber)
168    }
169
170    pub(crate) fn is_null(self) -> bool {
171        self.bytes == b"null"
172    }
173}
174
175#[derive(Clone, Copy, Debug, Eq, PartialEq)]
176pub(crate) struct Object<'a> {
177    bytes: &'a [u8],
178}
179
180impl<'a> Object<'a> {
181    pub(crate) fn get(self, name: &'static str) -> Result<Option<Value<'a>>, JsonError> {
182        let mut found = None;
183        for member in self.iter() {
184            let (key, value) = member?;
185            if key.decoded_eq_ascii(name) {
186                if found.is_some() {
187                    return Err(JsonError::DuplicateField(name));
188                }
189                found = Some(value);
190            }
191        }
192        Ok(found)
193    }
194
195    pub(crate) fn required(self, name: &'static str) -> Result<Value<'a>, JsonError> {
196        self.get(name)?.ok_or(JsonError::MissingField(name))
197    }
198
199    pub(crate) fn iter(self) -> ObjectIter<'a> {
200        ObjectIter {
201            bytes: self.bytes,
202            position: 1,
203            first: true,
204            finished: false,
205        }
206    }
207}
208
209pub(crate) struct ObjectIter<'a> {
210    bytes: &'a [u8],
211    position: usize,
212    first: bool,
213    finished: bool,
214}
215
216impl<'a> Iterator for ObjectIter<'a> {
217    type Item = Result<(JsonString<'a>, Value<'a>), JsonError>;
218
219    fn next(&mut self) -> Option<Self::Item> {
220        if self.finished {
221            return None;
222        }
223        let result = self.next_inner();
224        if result.is_err() {
225            self.finished = true;
226        }
227        result.transpose()
228    }
229}
230
231impl<'a> ObjectIter<'a> {
232    fn next_inner(&mut self) -> Result<Option<(JsonString<'a>, Value<'a>)>, JsonError> {
233        self.position = whitespace(self.bytes, self.position);
234        if self.bytes.get(self.position) == Some(&b'}') {
235            self.finished = true;
236            return Ok(None);
237        }
238        if !self.first {
239            if self.bytes.get(self.position) != Some(&b',') {
240                return Err(JsonError::InvalidSyntax);
241            }
242            self.position = whitespace(self.bytes, self.position + 1);
243        }
244        self.first = false;
245        if self.bytes.get(self.position) != Some(&b'"') {
246            return Err(JsonError::InvalidSyntax);
247        }
248        let key_end = parse_string(self.bytes, self.position)?;
249        let key = Value {
250            bytes: &self.bytes[self.position..key_end],
251        }
252        .string()?;
253        self.position = whitespace(self.bytes, key_end);
254        if self.bytes.get(self.position) != Some(&b':') {
255            return Err(JsonError::InvalidSyntax);
256        }
257        let start = whitespace(self.bytes, self.position + 1);
258        let end = parse_value(self.bytes, start, 0)?;
259        self.position = end;
260        Ok(Some((
261            key,
262            Value {
263                bytes: &self.bytes[start..end],
264            },
265        )))
266    }
267}
268
269#[derive(Clone, Copy, Debug, Eq, PartialEq)]
270pub(crate) struct Array<'a> {
271    bytes: &'a [u8],
272}
273
274impl<'a> Array<'a> {
275    pub(crate) fn iter(self) -> ArrayIter<'a> {
276        ArrayIter {
277            bytes: self.bytes,
278            position: 1,
279            first: true,
280            finished: false,
281        }
282    }
283}
284
285pub(crate) struct ArrayIter<'a> {
286    bytes: &'a [u8],
287    position: usize,
288    first: bool,
289    finished: bool,
290}
291
292impl<'a> Iterator for ArrayIter<'a> {
293    type Item = Result<Value<'a>, JsonError>;
294
295    fn next(&mut self) -> Option<Self::Item> {
296        if self.finished {
297            return None;
298        }
299        let result = self.next_inner();
300        if result.is_err() {
301            self.finished = true;
302        }
303        result.transpose()
304    }
305}
306
307impl<'a> ArrayIter<'a> {
308    fn next_inner(&mut self) -> Result<Option<Value<'a>>, JsonError> {
309        self.position = whitespace(self.bytes, self.position);
310        if self.bytes.get(self.position) == Some(&b']') {
311            self.finished = true;
312            return Ok(None);
313        }
314        if !self.first {
315            if self.bytes.get(self.position) != Some(&b',') {
316                return Err(JsonError::InvalidSyntax);
317            }
318            self.position = whitespace(self.bytes, self.position + 1);
319        }
320        self.first = false;
321        let start = self.position;
322        let end = parse_value(self.bytes, start, 0)?;
323        self.position = end;
324        Ok(Some(Value {
325            bytes: &self.bytes[start..end],
326        }))
327    }
328}
329
330fn parse_value(bytes: &[u8], position: usize, depth: usize) -> Result<usize, JsonError> {
331    if depth > MAX_DEPTH {
332        return Err(JsonError::NestingTooDeep);
333    }
334    match bytes.get(position).copied() {
335        Some(b'"') => parse_string(bytes, position),
336        Some(b'{') => parse_object(bytes, position, depth + 1),
337        Some(b'[') => parse_array(bytes, position, depth + 1),
338        Some(b't') => literal(bytes, position, b"true"),
339        Some(b'f') => literal(bytes, position, b"false"),
340        Some(b'n') => literal(bytes, position, b"null"),
341        Some(b'-' | b'0'..=b'9') => parse_number(bytes, position),
342        _ => Err(JsonError::InvalidSyntax),
343    }
344}
345
346fn parse_object(bytes: &[u8], mut position: usize, depth: usize) -> Result<usize, JsonError> {
347    position = whitespace(bytes, position + 1);
348    if bytes.get(position) == Some(&b'}') {
349        return Ok(position + 1);
350    }
351    loop {
352        if bytes.get(position) != Some(&b'"') {
353            return Err(JsonError::InvalidSyntax);
354        }
355        position = whitespace(bytes, parse_string(bytes, position)?);
356        if bytes.get(position) != Some(&b':') {
357            return Err(JsonError::InvalidSyntax);
358        }
359        position = whitespace(
360            bytes,
361            parse_value(bytes, whitespace(bytes, position + 1), depth)?,
362        );
363        match bytes.get(position) {
364            Some(b'}') => return Ok(position + 1),
365            Some(b',') => position = whitespace(bytes, position + 1),
366            _ => return Err(JsonError::InvalidSyntax),
367        }
368    }
369}
370
371fn parse_array(bytes: &[u8], mut position: usize, depth: usize) -> Result<usize, JsonError> {
372    position = whitespace(bytes, position + 1);
373    if bytes.get(position) == Some(&b']') {
374        return Ok(position + 1);
375    }
376    loop {
377        position = whitespace(bytes, parse_value(bytes, position, depth)?);
378        match bytes.get(position) {
379            Some(b']') => return Ok(position + 1),
380            Some(b',') => position = whitespace(bytes, position + 1),
381            _ => return Err(JsonError::InvalidSyntax),
382        }
383    }
384}
385
386fn parse_string(bytes: &[u8], position: usize) -> Result<usize, JsonError> {
387    let mut index = position + 1;
388    while let Some(byte) = bytes.get(index).copied() {
389        match byte {
390            b'"' => return Ok(index + 1),
391            b'\\' => {
392                let (_, next) = escaped_character(bytes, index)?;
393                index = next;
394            }
395            0..=0x1f => return Err(JsonError::InvalidSyntax),
396            _ => index += 1,
397        }
398    }
399    Err(JsonError::InvalidSyntax)
400}
401
402fn escaped_character(bytes: &[u8], slash: usize) -> Result<(char, usize), JsonError> {
403    match bytes.get(slash + 1).copied() {
404        Some(b'"') => Ok(('"', slash + 2)),
405        Some(b'\\') => Ok(('\\', slash + 2)),
406        Some(b'/') => Ok(('/', slash + 2)),
407        Some(b'b') => Ok(('\u{0008}', slash + 2)),
408        Some(b'f') => Ok(('\u{000c}', slash + 2)),
409        Some(b'n') => Ok(('\n', slash + 2)),
410        Some(b'r') => Ok(('\r', slash + 2)),
411        Some(b't') => Ok(('\t', slash + 2)),
412        Some(b'u') => {
413            let first = unicode_escape(bytes, slash + 2)?;
414            let next = slash + 6;
415            let scalar = if (0xd800..=0xdbff).contains(&first) {
416                if bytes.get(next..next + 2) != Some(b"\\u") {
417                    return Err(JsonError::InvalidEscape);
418                }
419                let second = unicode_escape(bytes, next + 2)?;
420                if !(0xdc00..=0xdfff).contains(&second) {
421                    return Err(JsonError::InvalidEscape);
422                }
423                let high = u32::from(first - 0xd800);
424                let low = u32::from(second - 0xdc00);
425                (0x10000 + (high << 10) + low, next + 6)
426            } else if (0xdc00..=0xdfff).contains(&first) {
427                return Err(JsonError::InvalidEscape);
428            } else {
429                (u32::from(first), next)
430            };
431            let character = char::from_u32(scalar.0).ok_or(JsonError::InvalidEscape)?;
432            Ok((character, scalar.1))
433        }
434        _ => Err(JsonError::InvalidEscape),
435    }
436}
437
438fn unicode_escape(bytes: &[u8], start: usize) -> Result<u16, JsonError> {
439    let digits = bytes
440        .get(start..start + 4)
441        .ok_or(JsonError::InvalidEscape)?;
442    let mut value = 0u16;
443    for digit in digits {
444        value = value
445            .checked_mul(16)
446            .and_then(|value| hex(*digit).map(|digit| value + u16::from(digit)))
447            .ok_or(JsonError::InvalidEscape)?;
448    }
449    Ok(value)
450}
451
452fn hex(byte: u8) -> Option<u8> {
453    match byte {
454        b'0'..=b'9' => Some(byte - b'0'),
455        b'a'..=b'f' => Some(byte - b'a' + 10),
456        b'A'..=b'F' => Some(byte - b'A' + 10),
457        _ => None,
458    }
459}
460
461fn parse_number(bytes: &[u8], mut position: usize) -> Result<usize, JsonError> {
462    if bytes.get(position) == Some(&b'-') {
463        position += 1;
464    }
465    match bytes.get(position) {
466        Some(b'0') => position += 1,
467        Some(b'1'..=b'9') => {
468            position += 1;
469            while matches!(bytes.get(position), Some(b'0'..=b'9')) {
470                position += 1;
471            }
472        }
473        _ => return Err(JsonError::InvalidNumber),
474    }
475    if bytes.get(position) == Some(&b'.') {
476        position += 1;
477        let start = position;
478        while matches!(bytes.get(position), Some(b'0'..=b'9')) {
479            position += 1;
480        }
481        if position == start {
482            return Err(JsonError::InvalidNumber);
483        }
484    }
485    if matches!(bytes.get(position), Some(b'e' | b'E')) {
486        position += 1;
487        if matches!(bytes.get(position), Some(b'+' | b'-')) {
488            position += 1;
489        }
490        let start = position;
491        while matches!(bytes.get(position), Some(b'0'..=b'9')) {
492            position += 1;
493        }
494        if position == start {
495            return Err(JsonError::InvalidNumber);
496        }
497    }
498    Ok(position)
499}
500
501fn literal(bytes: &[u8], position: usize, literal: &[u8]) -> Result<usize, JsonError> {
502    if bytes.get(position..position + literal.len()) == Some(literal) {
503        Ok(position + literal.len())
504    } else {
505        Err(JsonError::InvalidSyntax)
506    }
507}
508
509fn whitespace(bytes: &[u8], mut position: usize) -> usize {
510    while matches!(bytes.get(position), Some(b' ' | b'\n' | b'\r' | b'\t')) {
511        position += 1;
512    }
513    position
514}
515
516fn copy_decoded(buffer: &mut [u8], output: &mut usize, bytes: &[u8]) -> Result<(), JsonError> {
517    let end = output
518        .checked_add(bytes.len())
519        .ok_or(JsonError::BufferTooSmall)?;
520    let destination = buffer
521        .get_mut(*output..end)
522        .ok_or(JsonError::BufferTooSmall)?;
523    destination.copy_from_slice(bytes);
524    *output = end;
525    Ok(())
526}
527
528#[cfg(test)]
529mod tests {
530    use super::{JsonError, Value};
531
532    #[test]
533    fn validates_and_iterates_nested_json() {
534        let value = Value::parse_document(br#" {"a":[1,{"b":true}],"skip":null} "#).unwrap();
535        let object = value.object().unwrap();
536        let array = object.required("a").unwrap().array().unwrap();
537        assert_eq!(array.iter().count(), 2);
538        assert!(object.get("missing").unwrap().is_none());
539    }
540
541    #[test]
542    fn decodes_strings_and_rejects_bad_surrogates() {
543        let value = Value::parse_document(br#""hello\n\u263a\ud83d\ude00""#).unwrap();
544        let string = value.string().unwrap();
545        let mut output = [0; 32];
546        assert_eq!(string.decode_into(&mut output).unwrap(), "hello\n☺😀");
547        assert_eq!(
548            Value::parse_document(br#""\ud83d!""#),
549            Err(JsonError::InvalidEscape)
550        );
551    }
552
553    #[test]
554    fn rejects_trailing_and_duplicate_requested_fields() {
555        assert!(Value::parse_document(b"{} x").is_err());
556        let object = Value::parse_document(br#"{"a":1,"\u0061":2}"#)
557            .unwrap()
558            .object()
559            .unwrap();
560        assert_eq!(object.get("a"), Err(JsonError::DuplicateField("a")));
561    }
562}