Skip to main content

seam_core/
json.rs

1//! JSON read in place.
2//!
3//! The boundary rules apply while the bytes are read: an integer never passes
4//! through an `f64`, which is the corruption a host parser may already have
5//! committed before Seam is called.
6//!
7//! Parsing records where each value sits rather than copying it out, so a
8//! string is borrowed from the caller's buffer and a rejected document is never
9//! materialised. [`Ref`] implements [`Input`], so the validator walks the bytes.
10//!
11//! This parses in order to validate. There is no encoder.
12
13use std::borrow::Cow;
14
15use crate::error::{Code, Issue, Path, ValidationError};
16use crate::input::{Input, Kind};
17use crate::limits::Limits;
18use crate::value::{Int, Slot};
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct JsonError {
22    pub line: usize,
23    pub column: usize,
24    pub message: String,
25    /// Set when the document was refused for exceeding a limit rather than for
26    /// being malformed.
27    ///
28    /// The limits exist to stop a hostile document from being built at all, so
29    /// they are checked here rather than after parsing — which means there is
30    /// no path to report, only a position. The code is what a caller acts on,
31    /// and it is the same code the validator would have produced had the same
32    /// payload arrived as host objects.
33    pub code: Option<Code>,
34}
35
36impl JsonError {
37    pub fn as_validation(&self) -> Option<ValidationError> {
38        self.code.map(|code| ValidationError {
39            issues: vec![Issue { path: Path(Vec::new()), code, message: self.message.clone() }],
40        })
41    }
42}
43
44impl std::fmt::Display for JsonError {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        write!(f, "{}:{}: {}", self.line, self.column, self.message)
47    }
48}
49
50impl std::error::Error for JsonError {}
51
52#[derive(Debug, Clone, Copy)]
53struct Span {
54    start: u32,
55    len: u32,
56    escaped: bool,
57}
58
59#[derive(Debug, Clone, Copy)]
60struct Member {
61    key: Span,
62    node: u32,
63}
64
65#[derive(Debug, Clone, Copy)]
66enum Node {
67    Null,
68    Bool(bool),
69    Int(Int),
70    IntTooWide,
71    Float(f64),
72    Str(Span),
73    Array { first: u32, len: u32 },
74    Object { first: u32, len: u32 },
75}
76
77/// A parsed document: where every value lives, not a copy of it.
78#[derive(Debug)]
79pub struct Document<'a> {
80    src: &'a [u8],
81    nodes: Vec<Node>,
82    items: Vec<u32>,
83    members: Vec<Member>,
84    root: u32,
85}
86
87impl<'a> Document<'a> {
88    /// Parses one document, rejecting anything after it.
89    pub fn parse(input: &'a [u8], limits: Limits) -> Result<Self, JsonError> {
90        // See `Limits::MAX_DEPTH`: this parser recurses, and a stack overflow
91        // kills the process rather than raising anything a caller can catch.
92        let limits = limits.clamped();
93        if u32::try_from(input.len()).is_err() {
94            return Err(JsonError {
95                line: 1,
96                column: 1,
97                message: "document larger than 4 GiB".to_string(),
98                // A limit like any other, and the one a caller is most likely
99                // to want to tell apart from malformed input.
100                code: Some(Code::SizeExceeded),
101            });
102        }
103        if std::str::from_utf8(input).is_err() {
104            return Err(JsonError {
105                line: 1,
106                column: 1,
107                message: "input is not valid UTF-8".to_string(),
108                // Not a limit: this is not JSON at all.
109                code: None,
110            });
111        }
112
113        let mut p = Parser {
114            src: input,
115            pos: 0,
116            limits,
117            nodes: Vec::new(),
118            items: Vec::new(),
119            members: Vec::new(),
120        };
121        p.skip_ws();
122        let root = p.value(0)?;
123        p.skip_ws();
124        if p.pos < p.src.len() {
125            return Err(p.error("trailing characters after the document"));
126        }
127        Ok(Document {
128            src: input,
129            nodes: p.nodes,
130            items: p.items,
131            members: p.members,
132            root,
133        })
134    }
135
136    /// The whole document, ready to validate.
137    #[must_use]
138    pub fn root(&self) -> Ref<'a, '_> {
139        Ref { doc: self, node: self.root }
140    }
141
142    fn bytes(&self, span: Span) -> &'a [u8] {
143        let end = (span.start + span.len) as usize;
144        self.src.get(span.start as usize..end).unwrap_or(&[])
145    }
146
147    fn text(&self, span: Span) -> Cow<'a, str> {
148        // Checked once, for the whole input, before parsing began.
149        let raw = std::str::from_utf8(self.bytes(span)).unwrap_or("");
150        if span.escaped {
151            Cow::Owned(unescape(raw))
152        } else {
153            Cow::Borrowed(raw)
154        }
155    }
156}
157
158/// One value inside a [`Document`].
159#[derive(Clone, Copy)]
160pub struct Ref<'a, 'd> {
161    doc: &'d Document<'a>,
162    node: u32,
163}
164
165impl<'a, 'd> Ref<'a, 'd> {
166    fn node(&self) -> Node {
167        self.doc
168            .nodes
169            .get(self.node as usize)
170            .copied()
171            .unwrap_or(Node::Null)
172    }
173
174    fn at(&self, node: u32) -> Ref<'a, 'd> {
175        Ref { doc: self.doc, node }
176    }
177
178    fn members(&self) -> &'d [Member] {
179        match self.node() {
180            Node::Object { first, len } => self
181                .doc
182                .members
183                .get(first as usize..(first + len) as usize)
184                .unwrap_or(&[]),
185            _ => &[],
186        }
187    }
188
189    /// The text of a string value, borrowed unless it carried escapes.
190    #[must_use]
191    pub fn text(&self) -> Option<Cow<'a, str>> {
192        match self.node() {
193            Node::Str(span) => Some(self.doc.text(span)),
194            _ => None,
195        }
196    }
197
198    /// Members of an object, in the order they appeared.
199    pub fn entries(&self) -> impl Iterator<Item = (Cow<'a, str>, Ref<'a, 'd>)> + '_ {
200        self.members()
201            .iter()
202            .map(move |m| (self.doc.text(m.key), self.at(m.node)))
203    }
204
205    /// Elements of an array, in order.
206    pub fn elements(&self) -> impl Iterator<Item = Ref<'a, 'd>> + '_ {
207        let items = match self.node() {
208            Node::Array { first, len } => self
209                .doc
210                .items
211                .get(first as usize..(first + len) as usize)
212                .unwrap_or(&[]),
213            _ => &[],
214        };
215        items.iter().map(move |&n| self.at(n))
216    }
217}
218
219impl Input for Ref<'_, '_> {
220    type Child<'x>
221        = Self
222    where
223        Self: 'x;
224
225    fn kind(&self) -> Kind {
226        match self.node() {
227            Node::Null => Kind::Null,
228            Node::Bool(_) => Kind::Bool,
229            Node::Int(_) => Kind::Int,
230            Node::IntTooWide => Kind::IntegerTooWide,
231            Node::Float(_) => Kind::Float,
232            Node::Str(_) => Kind::String,
233            Node::Array { .. } => Kind::Array,
234            Node::Object { .. } => Kind::Object,
235        }
236    }
237
238    fn as_bool(&self) -> Option<bool> {
239        match self.node() {
240            Node::Bool(b) => Some(b),
241            _ => None,
242        }
243    }
244
245    fn as_int(&self) -> Option<Int> {
246        match self.node() {
247            Node::Int(n) => Some(n),
248            _ => None,
249        }
250    }
251
252    fn as_f64(&self) -> Option<f64> {
253        match self.node() {
254            Node::Float(f) => Some(f),
255            Node::Int(n) => Some(n.as_i128() as f64),
256            _ => None,
257        }
258    }
259
260    fn as_str(&self) -> Option<Cow<'_, str>> {
261        self.text()
262    }
263
264    fn len(&self) -> usize {
265        match self.node() {
266            Node::Array { len, .. } | Node::Object { len, .. } => len as usize,
267            _ => 0,
268        }
269    }
270
271    fn item(&self, index: usize) -> Option<Self::Child<'_>> {
272        let Node::Array { first, len } = self.node() else {
273            return None;
274        };
275        if index >= len as usize {
276            return None;
277        }
278        self.doc
279            .items
280            .get(first as usize + index)
281            .map(|&n| self.at(n))
282    }
283
284    fn slot(&self, key: &str) -> Slot<Self::Child<'_>> {
285        // Linear over the members: objects at a boundary have a handful of
286        // keys, and comparing byte slices beats hashing at that size.
287        for m in self.members() {
288            let matches = if m.key.escaped {
289                self.doc.text(m.key) == key
290            } else {
291                self.doc.bytes(m.key) == key.as_bytes()
292            };
293            if matches {
294                let child = self.at(m.node);
295                return match child.node() {
296                    Node::Null => Slot::Null,
297                    _ => Slot::Present(child),
298                };
299            }
300        }
301        Slot::Absent
302    }
303
304    fn each_key(&self, f: &mut dyn FnMut(&str)) {
305        for m in self.members() {
306            f(&self.doc.text(m.key));
307        }
308    }
309}
310
311fn unescape(raw: &str) -> String {
312    let mut out = String::with_capacity(raw.len());
313    let mut chars = raw.chars();
314    while let Some(c) = chars.next() {
315        if c != '\\' {
316            out.push(c);
317            continue;
318        }
319        match chars.next() {
320            Some('"') => out.push('"'),
321            Some('\\') => out.push('\\'),
322            Some('/') => out.push('/'),
323            Some('b') => out.push('\u{0008}'),
324            Some('f') => out.push('\u{000C}'),
325            Some('n') => out.push('\n'),
326            Some('r') => out.push('\r'),
327            Some('t') => out.push('\t'),
328            Some('u') => {
329                let first = hex4(&mut chars);
330                let scalar = if (0xD800..=0xDBFF).contains(&first) {
331                    // The parser accepted this, so the pair is well formed.
332                    chars.next();
333                    chars.next();
334                    let second = hex4(&mut chars);
335                    0x10000 + ((first - 0xD800) << 10) + (second - 0xDC00)
336                } else {
337                    first
338                };
339                out.push(char::from_u32(scalar).unwrap_or('\u{FFFD}'));
340            }
341            _ => {}
342        }
343    }
344    out
345}
346
347fn hex4(chars: &mut std::str::Chars<'_>) -> u32 {
348    let mut n = 0;
349    for _ in 0..4 {
350        n = n * 16 + chars.next().and_then(|c| c.to_digit(16)).unwrap_or(0);
351    }
352    n
353}
354
355struct Parser<'a> {
356    src: &'a [u8],
357    pos: usize,
358    limits: Limits,
359    nodes: Vec<Node>,
360    items: Vec<u32>,
361    members: Vec<Member>,
362}
363
364impl Parser<'_> {
365    fn error(&self, message: impl Into<String>) -> JsonError {
366        let mut line = 1;
367        let mut column = 1;
368        for &b in self.src.iter().take(self.pos) {
369            if b == b'\n' {
370                line += 1;
371                column = 1;
372            } else {
373                column += 1;
374            }
375        }
376        JsonError { line, column, message: message.into(), code: None }
377    }
378
379    /// The same, for a limit rather than a syntax error.
380    fn exceeded(&self, code: Code, message: impl Into<String>) -> JsonError {
381        JsonError { code: Some(code), ..self.error(message) }
382    }
383
384    fn push(&mut self, node: Node) -> u32 {
385        self.nodes.push(node);
386        (self.nodes.len() - 1) as u32
387    }
388
389    fn peek(&self) -> Option<u8> {
390        self.src.get(self.pos).copied()
391    }
392
393    fn bump(&mut self) -> Option<u8> {
394        let b = self.peek()?;
395        self.pos += 1;
396        Some(b)
397    }
398
399    fn skip_ws(&mut self) {
400        while matches!(self.peek(), Some(b' ' | b'\t' | b'\n' | b'\r')) {
401            self.pos += 1;
402        }
403    }
404
405    fn expect(&mut self, byte: u8) -> Result<(), JsonError> {
406        if self.peek() == Some(byte) {
407            self.pos += 1;
408            Ok(())
409        } else {
410            Err(self.error(format!("expected `{}`", byte as char)))
411        }
412    }
413
414    fn value(&mut self, depth: usize) -> Result<u32, JsonError> {
415        if depth > self.limits.max_depth {
416            return Err(self.exceeded(
417                Code::DepthExceeded,
418                format!("nesting deeper than the limit of {}", self.limits.max_depth),
419            ));
420        }
421        match self.peek() {
422            Some(b'{') => self.object(depth),
423            Some(b'[') => self.array(depth),
424            Some(b'"') => {
425                let span = self.string()?;
426                Ok(self.push(Node::Str(span)))
427            }
428            Some(b't') => self.literal("true", Node::Bool(true)),
429            Some(b'f') => self.literal("false", Node::Bool(false)),
430            Some(b'n') => self.literal("null", Node::Null),
431            Some(b'-' | b'0'..=b'9') => self.number(),
432            Some(b) => Err(self.error(format!("unexpected `{}`", b as char))),
433            None => Err(self.error("unexpected end of input")),
434        }
435    }
436
437    fn literal(&mut self, word: &str, node: Node) -> Result<u32, JsonError> {
438        if self.src[self.pos..].starts_with(word.as_bytes()) {
439            self.pos += word.len();
440            Ok(self.push(node))
441        } else {
442            Err(self.error(format!("expected `{word}`")))
443        }
444    }
445
446    fn object(&mut self, depth: usize) -> Result<u32, JsonError> {
447        self.expect(b'{')?;
448        self.skip_ws();
449        if self.peek() == Some(b'}') {
450            self.pos += 1;
451            return Ok(self.push(Node::Object { first: 0, len: 0 }));
452        }
453        let mut found: Vec<Member> = Vec::new();
454        loop {
455            self.skip_ws();
456            let key = self.string()?;
457            self.skip_ws();
458            self.expect(b':')?;
459            self.skip_ws();
460            let node = self.value(depth + 1)?;
461
462            // Last one wins, as every mainstream parser does.
463            let existing = found.iter().position(|m| {
464                let a = self
465                    .src
466                    .get(m.key.start as usize..(m.key.start + m.key.len) as usize);
467                let b = self
468                    .src
469                    .get(key.start as usize..(key.start + key.len) as usize);
470                a == b
471            });
472            match existing {
473                Some(i) => found[i].node = node,
474                None => found.push(Member { key, node }),
475            }
476            if found.len() > self.limits.max_object_keys {
477                return Err(self.exceeded(
478                    Code::SizeExceeded,
479                    format!(
480                        "more than {} keys in one object",
481                        self.limits.max_object_keys
482                    ),
483                ));
484            }
485
486            self.skip_ws();
487            match self.bump() {
488                Some(b',') => {}
489                Some(b'}') => {
490                    let first = self.members.len() as u32;
491                    let len = found.len() as u32;
492                    self.members.extend_from_slice(&found);
493                    return Ok(self.push(Node::Object { first, len }));
494                }
495                _ => return Err(self.error("expected `,` or `}`")),
496            }
497        }
498    }
499
500    fn array(&mut self, depth: usize) -> Result<u32, JsonError> {
501        self.expect(b'[')?;
502        self.skip_ws();
503        if self.peek() == Some(b']') {
504            self.pos += 1;
505            return Ok(self.push(Node::Array { first: 0, len: 0 }));
506        }
507        let mut found: Vec<u32> = Vec::new();
508        loop {
509            self.skip_ws();
510            found.push(self.value(depth + 1)?);
511            if found.len() > self.limits.max_items {
512                return Err(self.exceeded(
513                    Code::SizeExceeded,
514                    format!("more than {} items in one array", self.limits.max_items),
515                ));
516            }
517            self.skip_ws();
518            match self.bump() {
519                Some(b',') => {}
520                Some(b']') => {
521                    let first = self.items.len() as u32;
522                    let len = found.len() as u32;
523                    self.items.extend_from_slice(&found);
524                    return Ok(self.push(Node::Array { first, len }));
525                }
526                _ => return Err(self.error("expected `,` or `]`")),
527            }
528        }
529    }
530
531    /// Records where the string is instead of copying it out.
532    fn string(&mut self) -> Result<Span, JsonError> {
533        self.expect(b'"')?;
534        let start = self.pos;
535        let mut escaped = false;
536        loop {
537            match self.bump() {
538                Some(b'"') => {
539                    let len = (self.pos - 1 - start) as u32;
540                    if len as usize > self.limits.max_string_bytes {
541                        return Err(self.exceeded(
542                            Code::SizeExceeded,
543                            format!("string longer than {} bytes", self.limits.max_string_bytes),
544                        ));
545                    }
546                    return Ok(Span { start: start as u32, len, escaped });
547                }
548                Some(b'\\') => {
549                    escaped = true;
550                    self.check_escape()?;
551                }
552                Some(_) => {}
553                None => return Err(self.error("unterminated string")),
554            }
555        }
556    }
557
558    fn check_escape(&mut self) -> Result<(), JsonError> {
559        match self.bump() {
560            Some(b'"' | b'\\' | b'/' | b'b' | b'f' | b'n' | b'r' | b't') => Ok(()),
561            Some(b'u') => match self.hex4()? {
562                0xD800..=0xDBFF => {
563                    if self.bump() != Some(b'\\') || self.bump() != Some(b'u') {
564                        return Err(self.error("lone high surrogate"));
565                    }
566                    if !(0xDC00..=0xDFFF).contains(&self.hex4()?) {
567                        return Err(self.error("high surrogate not followed by a low one"));
568                    }
569                    Ok(())
570                }
571                0xDC00..=0xDFFF => Err(self.error("lone low surrogate")),
572                _ => Ok(()),
573            },
574            _ => Err(self.error("unknown escape")),
575        }
576    }
577
578    fn hex4(&mut self) -> Result<u32, JsonError> {
579        let mut n = 0;
580        for _ in 0..4 {
581            let d = match self.bump() {
582                Some(b @ b'0'..=b'9') => u32::from(b - b'0'),
583                Some(b @ b'a'..=b'f') => u32::from(b - b'a') + 10,
584                Some(b @ b'A'..=b'F') => u32::from(b - b'A') + 10,
585                _ => return Err(self.error("expected four hex digits")),
586            };
587            n = n * 16 + d;
588        }
589        Ok(n)
590    }
591
592    fn number(&mut self) -> Result<u32, JsonError> {
593        let start = self.pos;
594        if self.peek() == Some(b'-') {
595            self.pos += 1;
596        }
597        match self.peek() {
598            // A leading zero may not be followed by another digit.
599            Some(b'0') => self.pos += 1,
600            Some(b'1'..=b'9') => {
601                while matches!(self.peek(), Some(b'0'..=b'9')) {
602                    self.pos += 1;
603                }
604            }
605            _ => return Err(self.error("expected a digit")),
606        }
607
608        let mut fractional = false;
609        if self.peek() == Some(b'.') {
610            fractional = true;
611            self.pos += 1;
612            if !matches!(self.peek(), Some(b'0'..=b'9')) {
613                return Err(self.error("expected a digit after `.`"));
614            }
615            while matches!(self.peek(), Some(b'0'..=b'9')) {
616                self.pos += 1;
617            }
618        }
619        if matches!(self.peek(), Some(b'e' | b'E')) {
620            fractional = true;
621            self.pos += 1;
622            if matches!(self.peek(), Some(b'+' | b'-')) {
623                self.pos += 1;
624            }
625            if !matches!(self.peek(), Some(b'0'..=b'9')) {
626                return Err(self.error("expected a digit in the exponent"));
627            }
628            while matches!(self.peek(), Some(b'0'..=b'9')) {
629                self.pos += 1;
630            }
631        }
632
633        let text = std::str::from_utf8(&self.src[start..self.pos]).unwrap_or("");
634        if fractional {
635            return match text.parse::<f64>() {
636                Ok(f) => Ok(self.push(Node::Float(f))),
637                Err(_) => Err(self.error("number does not fit an f64")),
638            };
639        }
640        // The integer never touches an f64. This is the line the project is
641        // about: a host parser that widens here has already lost the value.
642        if let Ok(n) = text.parse::<i64>() {
643            return Ok(self.push(Node::Int(Int::Signed(n))));
644        }
645        if let Ok(n) = text.parse::<u64>() {
646            return Ok(self.push(Node::Int(Int::Unsigned(n))));
647        }
648        Ok(self.push(Node::IntTooWide))
649    }
650}
651
652#[cfg(test)]
653mod tests {
654    use super::*;
655
656    fn doc(src: &str) -> Document<'_> {
657        Document::parse(src.as_bytes(), Limits::PERMISSIVE).expect("should parse")
658    }
659
660    fn err(src: &str) -> JsonError {
661        Document::parse(src.as_bytes(), Limits::PERMISSIVE).expect_err("should not parse")
662    }
663
664    fn present<'a, 'd>(r: &Ref<'a, 'd>, key: &str) -> Ref<'a, 'd> {
665        match r.slot(key) {
666            Slot::Present(v) => v,
667            _ => panic!("expected a value at `{key}`"),
668        }
669    }
670
671    #[test]
672    fn the_boundary_integer_survives() {
673        // The value JavaScript's JSON.parse corrupts to ...992.
674        assert_eq!(
675            doc("9007199254740993").root().as_int(),
676            Some(Int::Signed(9_007_199_254_740_993))
677        );
678        assert_eq!(
679            doc("18446744073709551615").root().as_int(),
680            Some(Int::Unsigned(u64::MAX))
681        );
682        assert_eq!(
683            doc("-9223372036854775808").root().as_int(),
684            Some(Int::Signed(i64::MIN))
685        );
686    }
687
688    #[test]
689    fn an_integer_past_64_bits_is_a_kind_not_a_number() {
690        assert_eq!(
691            doc("18446744073709551616").root().kind(),
692            Kind::IntegerTooWide
693        );
694        assert_eq!(
695            doc("-9223372036854775809").root().kind(),
696            Kind::IntegerTooWide
697        );
698    }
699
700    #[test]
701    fn a_plain_string_is_borrowed_not_copied() {
702        let d = doc(r#""hello""#);
703        assert!(matches!(d.root().text(), Some(Cow::Borrowed("hello"))));
704    }
705
706    #[test]
707    fn an_escaped_string_is_decoded_on_demand() {
708        assert_eq!(doc(r#""a\"b""#).root().text().as_deref(), Some("a\"b"));
709        assert_eq!(doc(r#""a\tb""#).root().text().as_deref(), Some("a\tb"));
710        assert_eq!(doc(r#""ñ""#).root().text().as_deref(), Some("ñ"));
711        assert_eq!(doc(r#""😀""#).root().text().as_deref(), Some("😀"));
712    }
713
714    #[test]
715    fn utf8_passes_through_untouched() {
716        assert_eq!(doc(r#""ñ😀""#).root().text().as_deref(), Some("ñ😀"));
717    }
718
719    #[test]
720    fn objects_and_arrays_are_walkable() {
721        let d = doc(r#"{"a": [1, null], "b": true}"#);
722        let root = d.root();
723        assert_eq!(root.kind(), Kind::Object);
724        assert_eq!(root.len(), 2);
725
726        let a = present(&root, "a");
727        assert_eq!(a.kind(), Kind::Array);
728        assert_eq!(a.len(), 2);
729        assert_eq!(a.item(0).and_then(|v| v.as_int()), Some(Int::Signed(1)));
730        assert_eq!(a.item(1).map(|v| v.kind()), Some(Kind::Null));
731        assert!(a.item(2).is_none());
732
733        assert_eq!(present(&root, "b").as_bool(), Some(true));
734        assert!(matches!(root.slot("missing"), Slot::Absent));
735    }
736
737    #[test]
738    fn a_null_member_is_null_and_a_missing_one_is_absent() {
739        let d = doc(r#"{"n": null}"#);
740        assert!(matches!(d.root().slot("n"), Slot::Null));
741        assert!(matches!(d.root().slot("gone"), Slot::Absent));
742    }
743
744    #[test]
745    fn keys_are_visited_in_order() {
746        let d = doc(r#"{"b": 1, "a": 2, "c": 3}"#);
747        let mut seen = Vec::new();
748        d.root().each_key(&mut |k| seen.push(k.to_string()));
749        assert_eq!(seen, ["b", "a", "c"]);
750    }
751
752    #[test]
753    fn a_duplicated_key_keeps_the_last() {
754        let d = doc(r#"{"a": 1, "a": 2}"#);
755        assert_eq!(d.root().len(), 1);
756        assert_eq!(present(&d.root(), "a").as_int(), Some(Int::Signed(2)));
757    }
758
759    #[test]
760    fn an_escaped_key_still_matches() {
761        let d = doc(r#"{"abc": 1}"#);
762        assert_eq!(present(&d.root(), "abc").as_int(), Some(Int::Signed(1)));
763    }
764
765    #[test]
766    fn nesting_is_walkable() {
767        let d = doc(r#"{"a": {"b": {"c": 7}}}"#);
768        let mut here = d.root();
769        for key in ["a", "b", "c"] {
770            here = present(&here, key);
771        }
772        assert_eq!(here.as_int(), Some(Int::Signed(7)));
773    }
774
775    #[test]
776    fn floats_stay_floats() {
777        assert_eq!(doc("1.5").root().as_f64(), Some(1.5));
778        assert_eq!(doc("1e3").root().as_f64(), Some(1000.0));
779        assert_eq!(doc("1").root().as_f64(), Some(1.0));
780    }
781
782    #[test]
783    fn malformed_input_is_rejected() {
784        assert!(err("01").message.contains("trailing"));
785        assert!(err("+1").message.contains("unexpected"));
786        assert!(err(".5").message.contains("unexpected"));
787        assert!(err("5.").message.contains("after `.`"));
788        assert!(err("1e").message.contains("exponent"));
789        assert!(err(r#"{"a":1,}"#).message.contains("expected"));
790        assert!(err("[1,]").message.contains("unexpected"));
791        assert!(err(r#""unterminated"#).message.contains("unterminated"));
792        assert!(err(r#""\ud800""#).message.contains("surrogate"));
793        assert!(err(r#""\q""#).message.contains("escape"));
794        assert!(err("{} {}").message.contains("trailing"));
795        assert!(err("").message.contains("end of input"));
796    }
797
798    #[test]
799    fn invalid_utf8_is_rejected() {
800        let e = Document::parse(&[b'"', 0xFF, b'"'], Limits::PERMISSIVE).expect_err("should fail");
801        assert!(e.message.contains("UTF-8"));
802    }
803
804    #[test]
805    fn errors_carry_a_position() {
806        let e = err("{\n  \"a\": xyz\n}");
807        assert_eq!((e.line, e.column), (2, 8));
808    }
809
810    #[test]
811    fn limits_are_enforced_while_reading() {
812        let deep = "[".repeat(40) + &"]".repeat(40);
813        let limits = Limits { max_depth: 8, ..Limits::DEFAULT };
814        assert!(Document::parse(deep.as_bytes(), limits)
815            .expect_err("should fail")
816            .message
817            .contains("nesting"));
818
819        let limits = Limits { max_items: 2, ..Limits::DEFAULT };
820        assert!(Document::parse(b"[1,2,3]", limits)
821            .expect_err("should fail")
822            .message
823            .contains("items"));
824
825        let limits = Limits { max_string_bytes: 3, ..Limits::DEFAULT };
826        assert!(Document::parse(br#""abcd""#, limits)
827            .expect_err("should fail")
828            .message
829            .contains("longer than"));
830    }
831
832    #[test]
833    fn a_hostile_document_does_not_exhaust_the_stack() {
834        let deep = "[".repeat(100_000);
835        assert!(Document::parse(deep.as_bytes(), Limits::DEFAULT).is_err());
836    }
837}