Skip to main content

draco_gltf/
json.rs

1//! Small, dependency-free JSON DOM used by the lossless glTF model.
2
3use std::ops::{Index, IndexMut};
4
5/// Dependency-free JSON value that preserves number lexemes and object order.
6#[derive(Clone, Debug, PartialEq)]
7pub enum Value {
8    /// JSON null.
9    Null,
10    /// JSON boolean.
11    Bool(bool),
12    /// JSON number stored as its original lexical representation.
13    Number(String),
14    /// JSON string.
15    String(String),
16    /// JSON array.
17    Array(Vec<Value>),
18    /// JSON object represented as ordered key/value pairs.
19    Object(Vec<(String, Value)>),
20}
21
22impl Value {
23    /// Parses one complete JSON value.
24    pub fn parse(input: &[u8]) -> Result<Self, String> {
25        let mut parser = Parser { input, pos: 0 };
26        let value = parser.value()?;
27        parser.space();
28        if parser.pos != input.len() {
29            return Err("trailing JSON data".into());
30        }
31        Ok(value)
32    }
33    /// Serializes this value as whitespace-free JSON.
34    pub fn to_vec(&self) -> Vec<u8> {
35        let mut out = Vec::new();
36        self.write(&mut out);
37        out
38    }
39    fn write(&self, out: &mut Vec<u8>) {
40        match self {
41            Self::Null => out.extend_from_slice(b"null"),
42            Self::Bool(v) => out.extend_from_slice(if *v { b"true" } else { b"false" }),
43            Self::Number(v) => out.extend_from_slice(v.as_bytes()),
44            Self::String(v) => write_string(out, v),
45            Self::Array(values) => {
46                out.push(b'[');
47                for (i, v) in values.iter().enumerate() {
48                    if i != 0 {
49                        out.push(b',');
50                    }
51                    v.write(out);
52                }
53                out.push(b']');
54            }
55            Self::Object(values) => {
56                out.push(b'{');
57                for (i, (k, v)) in values.iter().enumerate() {
58                    if i != 0 {
59                        out.push(b',');
60                    }
61                    write_string(out, k);
62                    out.push(b':');
63                    v.write(out);
64                }
65                out.push(b'}');
66            }
67        }
68    }
69    /// Borrows object entries when this value is an object.
70    pub fn as_object(&self) -> Option<&[(String, Value)]> {
71        if let Self::Object(v) = self {
72            Some(v)
73        } else {
74            None
75        }
76    }
77    /// Returns whether this value is an object.
78    pub fn is_object(&self) -> bool {
79        matches!(self, Self::Object(_))
80    }
81    /// Mutably borrows object entries when this value is an object.
82    pub fn as_object_mut(&mut self) -> Option<&mut Vec<(String, Value)>> {
83        if let Self::Object(v) = self {
84            Some(v)
85        } else {
86            None
87        }
88    }
89    /// Borrows array entries when this value is an array.
90    pub fn as_array(&self) -> Option<&[Value]> {
91        if let Self::Array(v) = self {
92            Some(v)
93        } else {
94            None
95        }
96    }
97    /// Mutably borrows array entries when this value is an array.
98    pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>> {
99        if let Self::Array(v) = self {
100            Some(v)
101        } else {
102            None
103        }
104    }
105    /// Borrows the string when this value is a string.
106    pub fn as_str(&self) -> Option<&str> {
107        if let Self::String(v) = self {
108            Some(v)
109        } else {
110            None
111        }
112    }
113    /// Parses a non-negative integer without changing its stored lexeme.
114    pub fn as_u64(&self) -> Option<u64> {
115        match self {
116            Self::Number(v) => v.parse().ok(),
117            _ => None,
118        }
119    }
120    /// Parses a JSON number as `f64` without changing its stored lexeme.
121    pub fn as_f64(&self) -> Option<f64> {
122        match self {
123            Self::Number(v) => v.parse().ok(),
124            _ => None,
125        }
126    }
127    /// Looks up an object member by key.
128    pub fn get(&self, key: &str) -> Option<&Value> {
129        self.as_object()?
130            .iter()
131            .find(|(k, _)| k == key)
132            .map(|(_, v)| v)
133    }
134    /// Mutably looks up an object member by key.
135    pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
136        self.as_object_mut()?
137            .iter_mut()
138            .find(|(k, _)| k == key)
139            .map(|(_, v)| v)
140    }
141    /// Constructs an ordered JSON object from key/value entries.
142    pub fn object(entries: impl IntoIterator<Item = (impl Into<String>, Value)>) -> Self {
143        Self::Object(entries.into_iter().map(|(k, v)| (k.into(), v)).collect())
144    }
145}
146impl From<&str> for Value {
147    fn from(v: &str) -> Self {
148        Self::String(v.into())
149    }
150}
151impl From<String> for Value {
152    fn from(v: String) -> Self {
153        Self::String(v)
154    }
155}
156impl From<u64> for Value {
157    fn from(v: u64) -> Self {
158        Self::Number(v.to_string())
159    }
160}
161impl From<usize> for Value {
162    fn from(v: usize) -> Self {
163        Self::Number(v.to_string())
164    }
165}
166impl From<bool> for Value {
167    fn from(v: bool) -> Self {
168        Self::Bool(v)
169    }
170}
171static NULL: Value = Value::Null;
172impl Index<&str> for Value {
173    type Output = Value;
174    fn index(&self, k: &str) -> &Self::Output {
175        self.get(k).unwrap_or(&NULL)
176    }
177}
178impl Index<&String> for Value {
179    type Output = Value;
180    fn index(&self, k: &String) -> &Self::Output {
181        self.get(k).unwrap_or(&NULL)
182    }
183}
184impl Index<usize> for Value {
185    type Output = Value;
186    fn index(&self, i: usize) -> &Self::Output {
187        self.as_array().and_then(|v| v.get(i)).unwrap_or(&NULL)
188    }
189}
190impl IndexMut<&str> for Value {
191    fn index_mut(&mut self, k: &str) -> &mut Self::Output {
192        if !matches!(self, Self::Object(_)) {
193            *self = Self::Object(Vec::new());
194        }
195        let v = self.as_object_mut().unwrap();
196        if let Some(i) = v.iter().position(|(name, _)| name == k) {
197            &mut v[i].1
198        } else {
199            v.push((k.into(), Self::Null));
200            &mut v.last_mut().unwrap().1
201        }
202    }
203}
204impl IndexMut<usize> for Value {
205    fn index_mut(&mut self, i: usize) -> &mut Self::Output {
206        &mut self.as_array_mut().expect("JSON value is not an array")[i]
207    }
208}
209
210fn write_string(out: &mut Vec<u8>, value: &str) {
211    out.push(b'"');
212    for ch in value.chars() {
213        match ch {
214            '"' => out.extend_from_slice(b"\\\""),
215            '\\' => out.extend_from_slice(b"\\\\"),
216            '\n' => out.extend_from_slice(b"\\n"),
217            '\r' => out.extend_from_slice(b"\\r"),
218            '\t' => out.extend_from_slice(b"\\t"),
219            c if c < ' ' => {
220                out.extend_from_slice(format!("\\u{:04x}", c as u32).as_bytes());
221            }
222            c => {
223                let mut b = [0; 4];
224                out.extend_from_slice(c.encode_utf8(&mut b).as_bytes());
225            }
226        }
227    }
228    out.push(b'"');
229}
230struct Parser<'a> {
231    input: &'a [u8],
232    pos: usize,
233}
234impl<'a> Parser<'a> {
235    fn space(&mut self) {
236        while self
237            .input
238            .get(self.pos)
239            .is_some_and(|c| c.is_ascii_whitespace())
240        {
241            self.pos += 1;
242        }
243    }
244    fn take(&mut self, c: u8) -> bool {
245        self.space();
246        if self.input.get(self.pos) == Some(&c) {
247            self.pos += 1;
248            true
249        } else {
250            false
251        }
252    }
253    fn value(&mut self) -> Result<Value, String> {
254        self.space();
255        match self.input.get(self.pos).copied() {
256            Some(b'{') => self.object(),
257            Some(b'[') => self.array(),
258            Some(b'"') => Ok(Value::String(self.string()?)),
259            Some(b't') => self.literal(b"true", Value::Bool(true)),
260            Some(b'f') => self.literal(b"false", Value::Bool(false)),
261            Some(b'n') => self.literal(b"null", Value::Null),
262            Some(b'-' | b'0'..=b'9') => self.number(),
263            _ => Err("expected JSON value".into()),
264        }
265    }
266    fn literal(&mut self, s: &[u8], v: Value) -> Result<Value, String> {
267        if self.input.get(self.pos..self.pos + s.len()) == Some(s) {
268            self.pos += s.len();
269            Ok(v)
270        } else {
271            Err("invalid JSON literal".into())
272        }
273    }
274    fn object(&mut self) -> Result<Value, String> {
275        self.pos += 1;
276        let mut o = Vec::new();
277        self.space();
278        if self.take(b'}') {
279            return Ok(Value::Object(o));
280        }
281        loop {
282            self.space();
283            if self.input.get(self.pos) != Some(&b'"') {
284                return Err("object key is not a string".into());
285            }
286            let k = self.string()?;
287            if !self.take(b':') {
288                return Err("missing object colon".into());
289            }
290            let v = self.value()?;
291            o.push((k, v));
292            if self.take(b'}') {
293                break;
294            }
295            if !self.take(b',') {
296                return Err("missing object comma".into());
297            }
298        }
299        Ok(Value::Object(o))
300    }
301    fn array(&mut self) -> Result<Value, String> {
302        self.pos += 1;
303        let mut a = Vec::new();
304        if self.take(b']') {
305            return Ok(Value::Array(a));
306        }
307        loop {
308            a.push(self.value()?);
309            if self.take(b']') {
310                break;
311            }
312            if !self.take(b',') {
313                return Err("missing array comma".into());
314            }
315        }
316        Ok(Value::Array(a))
317    }
318    fn string(&mut self) -> Result<String, String> {
319        self.pos += 1;
320        let mut out = String::new();
321        loop {
322            let b = *self.input.get(self.pos).ok_or("unterminated string")?;
323            self.pos += 1;
324            match b {
325                b'"' => return Ok(out),
326                b'\\' => {
327                    let esc = *self.input.get(self.pos).ok_or("bad escape")?;
328                    self.pos += 1;
329                    match esc {
330                        b'"' => out.push('"'),
331                        b'\\' => out.push('\\'),
332                        b'/' => out.push('/'),
333                        b'b' => out.push('\u{8}'),
334                        b'f' => out.push('\u{c}'),
335                        b'n' => out.push('\n'),
336                        b'r' => out.push('\r'),
337                        b't' => out.push('\t'),
338                        b'u' => {
339                            let first = self.unicode_escape()?;
340                            let scalar = match first {
341                                0xd800..=0xdbff => {
342                                    if self.input.get(self.pos..self.pos + 2) != Some(b"\\u") {
343                                        return Err("unpaired high surrogate".into());
344                                    }
345                                    self.pos += 2;
346                                    let second = self.unicode_escape()?;
347                                    if !(0xdc00..=0xdfff).contains(&second) {
348                                        return Err("invalid low surrogate".into());
349                                    }
350                                    0x1_0000
351                                        + (u32::from(first - 0xd800) << 10)
352                                        + u32::from(second - 0xdc00)
353                                }
354                                0xdc00..=0xdfff => return Err("unpaired low surrogate".into()),
355                                value => u32::from(value),
356                            };
357                            out.push(char::from_u32(scalar).ok_or("invalid unicode scalar")?);
358                        }
359                        _ => return Err("invalid escape".into()),
360                    }
361                }
362                0..=0x1f => return Err("control character in string".into()),
363                0x20..=0x7f => out.push(char::from(b)),
364                _ => {
365                    // Decode exactly one scalar from the lead byte. Validating
366                    // the whole remaining input here would make parsing
367                    // quadratic in document size.
368                    let width = match b {
369                        0xc2..=0xdf => 2,
370                        0xe0..=0xef => 3,
371                        0xf0..=0xf4 => 4,
372                        _ => return Err("invalid utf8".into()),
373                    };
374                    let start = self.pos - 1;
375                    let encoded = self.input.get(start..start + width).ok_or("invalid utf8")?;
376                    let ch = std::str::from_utf8(encoded)
377                        .map_err(|_| "invalid utf8")?
378                        .chars()
379                        .next()
380                        .ok_or("invalid utf8")?;
381                    out.push(ch);
382                    self.pos = start + width;
383                }
384            }
385        }
386    }
387    fn number(&mut self) -> Result<Value, String> {
388        let start = self.pos;
389        if self.input.get(self.pos) == Some(&b'-') {
390            self.pos += 1;
391        }
392        match self.input.get(self.pos) {
393            Some(b'0') => self.pos += 1,
394            Some(b'1'..=b'9') => {
395                self.pos += 1;
396                while self
397                    .input
398                    .get(self.pos)
399                    .is_some_and(|byte| byte.is_ascii_digit())
400                {
401                    self.pos += 1;
402                }
403            }
404            _ => return Err("invalid number".into()),
405        }
406        if self.input.get(self.pos) == Some(&b'.') {
407            self.pos += 1;
408            if !self
409                .input
410                .get(self.pos)
411                .is_some_and(|byte| byte.is_ascii_digit())
412            {
413                return Err("invalid number fraction".into());
414            }
415            while self
416                .input
417                .get(self.pos)
418                .is_some_and(|byte| byte.is_ascii_digit())
419            {
420                self.pos += 1;
421            }
422        }
423        if self
424            .input
425            .get(self.pos)
426            .is_some_and(|byte| matches!(byte, b'e' | b'E'))
427        {
428            self.pos += 1;
429            if self
430                .input
431                .get(self.pos)
432                .is_some_and(|byte| matches!(byte, b'+' | b'-'))
433            {
434                self.pos += 1;
435            }
436            if !self
437                .input
438                .get(self.pos)
439                .is_some_and(|byte| byte.is_ascii_digit())
440            {
441                return Err("invalid number exponent".into());
442            }
443            while self
444                .input
445                .get(self.pos)
446                .is_some_and(|byte| byte.is_ascii_digit())
447            {
448                self.pos += 1;
449            }
450        }
451        let text =
452            std::str::from_utf8(&self.input[start..self.pos]).map_err(|_| "invalid number")?;
453        Ok(Value::Number(text.into()))
454    }
455
456    fn unicode_escape(&mut self) -> Result<u16, String> {
457        let hex = self
458            .input
459            .get(self.pos..self.pos + 4)
460            .ok_or("short unicode escape")?;
461        self.pos += 4;
462        let text = std::str::from_utf8(hex).map_err(|_| "invalid unicode escape")?;
463        u16::from_str_radix(text, 16).map_err(|_| "invalid unicode escape".into())
464    }
465}
466
467#[cfg(test)]
468mod tests {
469    use super::Value;
470
471    #[test]
472    fn parses_unicode_surrogate_pairs() {
473        assert_eq!(
474            Value::parse(br#""\ud83d\ude80""#).unwrap(),
475            Value::String("🚀".into())
476        );
477        assert!(Value::parse(br#""\ud83d""#).is_err());
478        assert!(Value::parse(br#""\ude80""#).is_err());
479    }
480
481    #[test]
482    fn parses_raw_multibyte_scalars_and_rejects_malformed_bytes() {
483        let source = "\"aé\u{20ac}\u{1f680}\"";
484        assert_eq!(
485            Value::parse(source.as_bytes()).unwrap(),
486            Value::String("aé\u{20ac}\u{1f680}".into())
487        );
488        for invalid in [
489            b"\"\x80\"".as_slice(),             // bare continuation byte
490            b"\"\xc0\xaf\"".as_slice(),         // overlong encoding
491            b"\"\xed\xa0\x80\"".as_slice(),     // UTF-16 surrogate
492            b"\"\xf5\x80\x80\x80\"".as_slice(), // beyond U+10FFFF
493            b"\"\xe2\x82\"".as_slice(),         // truncated sequence
494        ] {
495            assert!(
496                Value::parse(invalid).is_err(),
497                "{invalid:?} should be invalid"
498            );
499        }
500    }
501
502    #[test]
503    fn enforces_json_number_grammar_without_float_range_limits() {
504        assert!(Value::parse(b"123456789012345678901234567890e999999").is_ok());
505        for invalid in [
506            b"01".as_slice(),
507            b"1.".as_slice(),
508            b"1e".as_slice(),
509            b"-".as_slice(),
510        ] {
511            assert!(
512                Value::parse(invalid).is_err(),
513                "{invalid:?} should be invalid"
514            );
515        }
516    }
517}