Skip to main content

datumesh_core/
json.rs

1//! Strict JSON (SPEC 6.1, I-JSON) and the RFC 8785 JSON Canonicalization Scheme (SPEC 6.2).
2//!
3//! [`parse`] accepts exactly the texts SPEC 6.1 accepts and reports failures in the order it
4//! defines. [`Value::to_jcs`] writes the RFC 8785 serialisation, including ECMAScript number
5//! formatting, so it is a complete JCS even though Datumesh objects never contain JSON numbers.
6
7use crate::error::{err, ErrorCode, Result};
8use core::cmp::Ordering;
9
10/// The deepest nesting of arrays and objects accepted (SPEC 6.1).
11pub const MAX_DEPTH: usize = 64;
12
13/// A JSON number: the text it was written as, and its IEEE 754 double value.
14#[derive(Clone, Debug, PartialEq)]
15pub struct Number {
16    text: String,
17    value: f64,
18}
19
20impl Number {
21    /// The number exactly as written in the parsed text.
22    pub fn text(&self) -> &str {
23        &self.text
24    }
25
26    /// The nearest double. Always finite.
27    pub fn value(&self) -> f64 {
28        self.value
29    }
30
31    /// A number from a finite double; `None` for NaN and infinities.
32    pub fn from_f64(value: f64) -> Option<Number> {
33        if value.is_finite() {
34            Some(Number { text: format_number(value), value })
35        } else {
36            None
37        }
38    }
39}
40
41/// A parsed JSON value. Object members keep their text order; names are unique.
42#[derive(Clone, Debug, PartialEq)]
43pub enum Value {
44    /// `null`
45    Null,
46    /// `true` or `false`
47    Bool(bool),
48    /// A number.
49    Number(Number),
50    /// A string.
51    String(String),
52    /// An array.
53    Array(Vec<Value>),
54    /// An object, members in text order.
55    Object(Vec<(String, Value)>),
56}
57
58impl Value {
59    /// The member named `name` of an object.
60    pub fn get(&self, name: &str) -> Option<&Value> {
61        match self {
62            Value::Object(m) => m.iter().find(|(k, _)| k == name).map(|(_, v)| v),
63            _ => None,
64        }
65    }
66
67    /// The string, if this is one.
68    pub fn as_str(&self) -> Option<&str> {
69        match self {
70            Value::String(s) => Some(s),
71            _ => None,
72        }
73    }
74
75    /// The members, if this is an object.
76    pub fn as_object(&self) -> Option<&[(String, Value)]> {
77        match self {
78            Value::Object(m) => Some(m),
79            _ => None,
80        }
81    }
82
83    /// The JSON type name, for messages.
84    pub(crate) fn kind(&self) -> &'static str {
85        match self {
86            Value::Null => "null",
87            Value::Bool(_) => "a boolean",
88            Value::Number(_) => "a number",
89            Value::String(_) => "a string",
90            Value::Array(_) => "an array",
91            Value::Object(_) => "an object",
92        }
93    }
94
95    /// The RFC 8785 canonical serialisation as UTF-8 bytes.
96    pub fn to_jcs(&self) -> Vec<u8> {
97        let mut out = String::new();
98        write_jcs(self, &mut out);
99        out.into_bytes()
100    }
101
102    /// The RFC 8785 canonical serialisation as a string.
103    pub fn to_jcs_string(&self) -> String {
104        let mut out = String::new();
105        write_jcs(self, &mut out);
106        out
107    }
108}
109
110/// An object's members sorted as JCS sorts them (UTF-16 code units of the names).
111pub(crate) fn jcs_sorted(members: &[(String, Value)]) -> Vec<&(String, Value)> {
112    let mut v: Vec<&(String, Value)> = members.iter().collect();
113    v.sort_by(|a, b| utf16_cmp(&a.0, &b.0));
114    v
115}
116
117/// Compares two strings by their UTF-16 code units (RFC 8785 3.2.3).
118pub(crate) fn utf16_cmp(a: &str, b: &str) -> Ordering {
119    a.encode_utf16().cmp(b.encode_utf16())
120}
121
122/// Parses `bytes` as strict JSON (SPEC 6.1).
123///
124/// Failures, in the order SPEC 6.1 reports them: a byte order mark, invalid UTF-8, nesting
125/// deeper than [`MAX_DEPTH`], then the first in text order of a syntax error, a number that
126/// overflows a double, or a duplicate member (detected when its object closes), and last a
127/// lone surrogate. All are `E_PARSE_JSON` except duplicates (`E_PARSE_DUPLICATE_KEY`).
128pub fn parse(bytes: &[u8]) -> Result<Value> {
129    if bytes.starts_with(&[0xEF, 0xBB, 0xBF]) {
130        return err(ErrorCode::ParseJson, "$", "byte order mark");
131    }
132    let text = match core::str::from_utf8(bytes) {
133        Ok(t) => t,
134        Err(e) => return err(ErrorCode::ParseJson, "$", format!("invalid UTF-8 at byte {}", e.valid_up_to())),
135    };
136    check_depth(text.as_bytes())?;
137    let mut p = Parser { b: text.as_bytes(), i: 0, lone_surrogate: false };
138    p.ws();
139    let v = p.value()?;
140    p.ws();
141    if p.i != p.b.len() {
142        return p.syntax("extra data after the value");
143    }
144    if p.lone_surrogate {
145        return err(ErrorCode::ParseJson, "$", "lone surrogate");
146    }
147    Ok(v)
148}
149
150/// Parses `bytes` and returns their JCS serialisation.
151pub fn canonicalize_json(bytes: &[u8]) -> Result<Vec<u8>> {
152    Ok(parse(bytes)?.to_jcs())
153}
154
155fn check_depth(b: &[u8]) -> Result<()> {
156    let mut depth: usize = 0;
157    let mut in_str = false;
158    let mut esc = false;
159    for &c in b {
160        if in_str {
161            if esc {
162                esc = false;
163            } else if c == b'\\' {
164                esc = true;
165            } else if c == b'"' {
166                in_str = false;
167            }
168            continue;
169        }
170        match c {
171            b'"' => in_str = true,
172            b'[' | b'{' => {
173                depth += 1;
174                if depth > MAX_DEPTH {
175                    return err(ErrorCode::ParseJson, "$", format!("nested deeper than {MAX_DEPTH} levels"));
176                }
177            }
178            b']' | b'}' => depth = depth.saturating_sub(1),
179            _ => {}
180        }
181    }
182    Ok(())
183}
184
185struct Parser<'a> {
186    b: &'a [u8],
187    i: usize,
188    lone_surrogate: bool,
189}
190
191impl Parser<'_> {
192    fn syntax<T>(&self, what: &str) -> Result<T> {
193        err(ErrorCode::ParseJson, "$", format!("{what} at byte {}", self.i))
194    }
195
196    fn peek(&self) -> Option<u8> {
197        self.b.get(self.i).copied()
198    }
199
200    fn ws(&mut self) {
201        while let Some(b' ' | b'\t' | b'\n' | b'\r') = self.peek() {
202            self.i += 1;
203        }
204    }
205
206    fn lit(&mut self, word: &[u8], v: Value) -> Result<Value> {
207        if self.b.get(self.i..self.i + word.len()) == Some(word) {
208            self.i += word.len();
209            Ok(v)
210        } else {
211            self.syntax("invalid literal")
212        }
213    }
214
215    // Depth is bounded by `check_depth`, so recursion is at most MAX_DEPTH deep.
216    fn value(&mut self) -> Result<Value> {
217        match self.peek() {
218            Some(b'{') => self.object(),
219            Some(b'[') => self.array(),
220            Some(b'"') => Ok(Value::String(self.string()?.0)),
221            Some(b't') => self.lit(b"true", Value::Bool(true)),
222            Some(b'f') => self.lit(b"false", Value::Bool(false)),
223            Some(b'n') => self.lit(b"null", Value::Null),
224            Some(b'-' | b'0'..=b'9') => self.number(),
225            Some(_) => self.syntax("unexpected character"),
226            None => self.syntax("unexpected end of text"),
227        }
228    }
229
230    fn array(&mut self) -> Result<Value> {
231        self.i += 1;
232        let mut items = Vec::new();
233        self.ws();
234        if self.peek() == Some(b']') {
235            self.i += 1;
236            return Ok(Value::Array(items));
237        }
238        loop {
239            self.ws();
240            items.push(self.value()?);
241            self.ws();
242            match self.peek() {
243                Some(b',') => self.i += 1,
244                Some(b']') => {
245                    self.i += 1;
246                    return Ok(Value::Array(items));
247                }
248                _ => return self.syntax("expected ',' or ']'"),
249            }
250        }
251    }
252
253    fn object(&mut self) -> Result<Value> {
254        self.i += 1;
255        let mut members: Vec<(String, Value)> = Vec::new();
256        let mut units: Vec<Vec<u16>> = Vec::new();
257        self.ws();
258        if self.peek() == Some(b'}') {
259            self.i += 1;
260            return Ok(Value::Object(members));
261        }
262        loop {
263            self.ws();
264            if self.peek() != Some(b'"') {
265                return self.syntax("expected a member name");
266            }
267            let (name, name_units) = self.string()?;
268            self.ws();
269            if self.peek() != Some(b':') {
270                return self.syntax("expected ':'");
271            }
272            self.i += 1;
273            self.ws();
274            let v = self.value()?;
275            members.push((name, v));
276            units.push(name_units);
277            self.ws();
278            match self.peek() {
279                Some(b',') => self.i += 1,
280                Some(b'}') => {
281                    self.i += 1;
282                    break;
283                }
284                _ => return self.syntax("expected ',' or '}'"),
285            }
286        }
287        // Duplicates are detected when the object closes (SPEC 6.1), comparing unescaped names.
288        let mut order: Vec<usize> = (0..units.len()).collect();
289        order.sort_by(|&a, &b| units[a].cmp(&units[b]).then(a.cmp(&b)));
290        let mut dup: Option<usize> = None;
291        for w in order.windows(2) {
292            if units[w[0]] == units[w[1]] {
293                let second = w[0].max(w[1]);
294                dup = Some(dup.map_or(second, |d| d.min(second)));
295            }
296        }
297        if let Some(d) = dup {
298            let name = members.get(d).map(|m| m.0.as_str()).unwrap_or("");
299            return err(ErrorCode::ParseDuplicateKey, "$", format!("duplicate member '{name}'"));
300        }
301        Ok(Value::Object(members))
302    }
303
304    fn hex4(&mut self) -> Result<u16> {
305        let mut v: u16 = 0;
306        for _ in 0..4 {
307            let d = match self.peek() {
308                Some(c @ b'0'..=b'9') => c - b'0',
309                Some(c @ b'a'..=b'f') => c - b'a' + 10,
310                Some(c @ b'A'..=b'F') => c - b'A' + 10,
311                _ => return self.syntax("invalid \\u escape"),
312            };
313            v = (v << 4) | u16::from(d);
314            self.i += 1;
315        }
316        Ok(v)
317    }
318
319    /// Returns the string and its UTF-16 code units (lone surrogates kept in the units,
320    /// replaced by U+FFFD in the string and remembered as an error for later).
321    fn string(&mut self) -> Result<(String, Vec<u16>)> {
322        self.i += 1;
323        let mut units: Vec<u16> = Vec::new();
324        loop {
325            let c = match self.peek() {
326                None => return self.syntax("unterminated string"),
327                Some(c) => c,
328            };
329            match c {
330                b'"' => {
331                    self.i += 1;
332                    break;
333                }
334                b'\\' => {
335                    self.i += 1;
336                    let e = match self.peek() {
337                        Some(e) => e,
338                        None => return self.syntax("unterminated string"),
339                    };
340                    self.i += 1;
341                    let u = match e {
342                        b'"' => 0x22,
343                        b'\\' => 0x5C,
344                        b'/' => 0x2F,
345                        b'b' => 0x08,
346                        b'f' => 0x0C,
347                        b'n' => 0x0A,
348                        b'r' => 0x0D,
349                        b't' => 0x09,
350                        b'u' => self.hex4()?,
351                        _ => {
352                            self.i -= 1;
353                            return self.syntax("invalid escape");
354                        }
355                    };
356                    units.push(u);
357                }
358                0x00..=0x1F => return self.syntax("control character in string"),
359                _ => {
360                    // Copy one UTF-8 encoded character (input is valid UTF-8).
361                    let len = match c {
362                        0x00..=0x7F => 1,
363                        0xC0..=0xDF => 2,
364                        0xE0..=0xEF => 3,
365                        _ => 4,
366                    };
367                    let chunk = self.b.get(self.i..self.i + len).unwrap_or(&[]);
368                    match core::str::from_utf8(chunk).ok().and_then(|s| s.chars().next()) {
369                        Some(ch) => {
370                            let mut buf = [0u16; 2];
371                            units.extend_from_slice(ch.encode_utf16(&mut buf));
372                        }
373                        None => return self.syntax("invalid UTF-8"),
374                    }
375                    self.i += len;
376                }
377            }
378        }
379        let mut s = String::with_capacity(units.len());
380        for r in char::decode_utf16(units.iter().copied()) {
381            match r {
382                Ok(ch) => s.push(ch),
383                Err(_) => {
384                    self.lone_surrogate = true;
385                    s.push('\u{FFFD}');
386                }
387            }
388        }
389        Ok((s, units))
390    }
391
392    fn number(&mut self) -> Result<Value> {
393        let start = self.i;
394        if self.peek() == Some(b'-') {
395            self.i += 1;
396        }
397        match self.peek() {
398            Some(b'0') => self.i += 1,
399            Some(b'1'..=b'9') => {
400                while let Some(b'0'..=b'9') = self.peek() {
401                    self.i += 1;
402                }
403            }
404            _ => return self.syntax("invalid number"),
405        }
406        if self.peek() == Some(b'.') {
407            self.i += 1;
408            if !matches!(self.peek(), Some(b'0'..=b'9')) {
409                return self.syntax("invalid number");
410            }
411            while let Some(b'0'..=b'9') = self.peek() {
412                self.i += 1;
413            }
414        }
415        if let Some(b'e' | b'E') = self.peek() {
416            self.i += 1;
417            if let Some(b'+' | b'-') = self.peek() {
418                self.i += 1;
419            }
420            if !matches!(self.peek(), Some(b'0'..=b'9')) {
421                return self.syntax("invalid number");
422            }
423            while let Some(b'0'..=b'9') = self.peek() {
424                self.i += 1;
425            }
426        }
427        let text = core::str::from_utf8(self.b.get(start..self.i).unwrap_or(&[])).unwrap_or("");
428        let value: f64 = match text.parse() {
429            Ok(v) => v,
430            Err(_) => return self.syntax("invalid number"),
431        };
432        if !value.is_finite() {
433            return err(ErrorCode::ParseJson, "$", format!("number out of IEEE 754 range: {text}"));
434        }
435        Ok(Value::Number(Number { text: text.to_string(), value }))
436    }
437}
438
439enum Piece<'a> {
440    Value(&'a Value),
441    Text(&'static str),
442    Key(&'a str),
443}
444
445// Iterative, so that a deeply nested value built in memory cannot exhaust the stack.
446fn write_jcs(v: &Value, out: &mut String) {
447    let mut stack = vec![Piece::Value(v)];
448    while let Some(p) = stack.pop() {
449        match p {
450            Piece::Text(t) => out.push_str(t),
451            Piece::Key(k) => {
452                write_string(k, out);
453                out.push(':');
454            }
455            Piece::Value(Value::Null) => out.push_str("null"),
456            Piece::Value(Value::Bool(true)) => out.push_str("true"),
457            Piece::Value(Value::Bool(false)) => out.push_str("false"),
458            Piece::Value(Value::Number(n)) => out.push_str(&format_number(n.value)),
459            Piece::Value(Value::String(s)) => write_string(s, out),
460            Piece::Value(Value::Array(items)) => {
461                out.push('[');
462                stack.push(Piece::Text("]"));
463                for (i, item) in items.iter().enumerate().rev() {
464                    stack.push(Piece::Value(item));
465                    if i > 0 {
466                        stack.push(Piece::Text(","));
467                    }
468                }
469            }
470            Piece::Value(Value::Object(members)) => {
471                out.push('{');
472                stack.push(Piece::Text("}"));
473                for (i, (k, val)) in jcs_sorted(members).into_iter().enumerate().rev() {
474                    stack.push(Piece::Value(val));
475                    stack.push(Piece::Key(k));
476                    if i > 0 {
477                        stack.push(Piece::Text(","));
478                    }
479                }
480            }
481        }
482    }
483}
484
485/// Checks that an already-parsed value nests at most [`MAX_DEPTH`] arrays and objects
486/// (SPEC 6.1 applies the parser's limit to values accepted in parsed form). `E_PARSE_JSON`.
487pub fn check_value_depth(v: &Value) -> Result<()> {
488    let mut stack: Vec<(&Value, usize)> = vec![(v, 0)];
489    while let Some((v, d)) = stack.pop() {
490        let children: Box<dyn Iterator<Item = &Value>> = match v {
491            Value::Array(items) => Box::new(items.iter()),
492            Value::Object(members) => Box::new(members.iter().map(|(_, x)| x)),
493            _ => continue,
494        };
495        if d + 1 > MAX_DEPTH {
496            return err(ErrorCode::ParseJson, "$", format!("nested deeper than {MAX_DEPTH} levels"));
497        }
498        stack.extend(children.map(|c| (c, d + 1)));
499    }
500    Ok(())
501}
502
503/// Writes a JSON string as RFC 8785 does: only `"`, `\` and C0 controls are escaped.
504pub(crate) fn write_string(s: &str, out: &mut String) {
505    out.push('"');
506    for c in s.chars() {
507        match c {
508            '"' => out.push_str("\\\""),
509            '\\' => out.push_str("\\\\"),
510            '\u{08}' => out.push_str("\\b"),
511            '\u{09}' => out.push_str("\\t"),
512            '\u{0A}' => out.push_str("\\n"),
513            '\u{0C}' => out.push_str("\\f"),
514            '\u{0D}' => out.push_str("\\r"),
515            c if (c as u32) < 0x20 => {
516                const HEX: &[u8; 16] = b"0123456789abcdef";
517                let n = c as usize;
518                out.push_str("\\u00");
519                out.push(char::from(HEX[(n >> 4) & 0xF]));
520                out.push(char::from(HEX[n & 0xF]));
521            }
522            c => out.push(c),
523        }
524    }
525    out.push('"');
526}
527
528fn split_sci(sci: &str) -> (String, i64) {
529    let (mantissa, exp) = sci.split_once('e').unwrap_or((sci, "0"));
530    (mantissa.chars().filter(char::is_ascii_digit).collect(), exp.parse().unwrap_or(0))
531}
532
533/// The ECMAScript digits of a positive finite double: the fewest digits that round-trip
534/// and, among those, the closest to the value, ties to even (ECMA-262 Number::toString).
535///
536/// `{:e}` gives the fewest digits but breaks ties upwards; `{:.N e}` rounds the exact value
537/// half to even. The latter is used whenever it round-trips at the same length.
538fn shortest_digits(v: f64) -> (String, i64) {
539    let shortest = split_sci(&format!("{v:e}"));
540    let k = shortest.0.len();
541    let exact_text = format!("{:.*e}", k.saturating_sub(1), v);
542    if exact_text.parse::<f64>() == Ok(v) {
543        let exact = split_sci(&exact_text);
544        if exact.0.len() == k {
545            return exact;
546        }
547    }
548    shortest
549}
550
551/// Formats a finite double as ECMAScript `Number.prototype.toString` does (RFC 8785 3.2.2.3).
552///
553/// Rust's `{:e}` formatting yields the shortest digit string that round-trips; this lays
554/// those digits out by the ECMAScript rules.
555pub fn format_number(v: f64) -> String {
556    if v == 0.0 || !v.is_finite() {
557        // -0 serialises as 0; non-finite values never reach here from `parse`.
558        return "0".to_string();
559    }
560    let mut out = String::new();
561    if v < 0.0 {
562        out.push('-');
563    }
564    let (digits, exp) = shortest_digits(v.abs());
565    let k = digits.len() as i64;
566    let n = exp + 1;
567    if k <= n && n <= 21 {
568        out.push_str(&digits);
569        for _ in 0..(n - k) {
570            out.push('0');
571        }
572    } else if 0 < n && n <= 21 {
573        let (a, b) = digits.split_at(n as usize);
574        out.push_str(a);
575        out.push('.');
576        out.push_str(b);
577    } else if -6 < n && n <= 0 {
578        out.push_str("0.");
579        for _ in 0..(-n) {
580            out.push('0');
581        }
582        out.push_str(&digits);
583    } else {
584        let (a, b) = digits.split_at(1);
585        out.push_str(a);
586        if !b.is_empty() {
587            out.push('.');
588            out.push_str(b);
589        }
590        out.push('e');
591        let e = n - 1;
592        out.push(if e < 0 { '-' } else { '+' });
593        out.push_str(&e.abs().to_string());
594    }
595    out
596}