1use std::collections::HashSet;
13use std::fmt;
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct ParseLimits {
19 pub max_bytes: usize,
21 pub max_depth: usize,
23 pub max_string_bytes: usize,
26 pub max_collection_len: usize,
28 pub max_total_values: usize,
31 pub max_number_bytes: usize,
33 pub allow_fractional_numbers: bool,
35}
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub struct ScanStats {
40 pub bytes: usize,
42 pub max_depth_seen: usize,
44 pub values: usize,
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
50pub enum BoundError {
51 TooLarge { limit: usize, actual: usize },
53 TooDeep { limit: usize, offset: usize },
55 StringTooLong { limit: usize, offset: usize },
57 CollectionTooLong { limit: usize, offset: usize },
59 TooManyValues { limit: usize, offset: usize },
61 NumberTooLong { limit: usize, offset: usize },
63 FractionalNumber { offset: usize },
65 DuplicateKey { key: String, offset: usize },
68 Syntax {
70 offset: usize,
71 expected: &'static str,
72 },
73 InvalidUtf8 { offset: usize },
75 TrailingData { offset: usize },
77 Empty,
79}
80
81impl fmt::Display for BoundError {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 use BoundError::*;
84 match self {
85 TooLarge { limit, actual } => {
86 write!(f, "input is {actual} bytes, limit {limit}")
87 }
88 TooDeep { limit, offset } => {
89 write!(f, "nesting exceeds depth {limit} at byte {offset}")
90 }
91 StringTooLong { limit, offset } => {
92 write!(f, "string exceeds {limit} bytes at byte {offset}")
93 }
94 CollectionTooLong { limit, offset } => {
95 write!(f, "collection exceeds {limit} members at byte {offset}")
96 }
97 TooManyValues { limit, offset } => {
98 write!(f, "document exceeds {limit} values at byte {offset}")
99 }
100 NumberTooLong { limit, offset } => {
101 write!(f, "number exceeds {limit} bytes at byte {offset}")
102 }
103 FractionalNumber { offset } => {
104 write!(f, "fractional number not allowed at byte {offset}")
105 }
106 DuplicateKey { key, offset } => {
107 write!(f, "duplicate key {key:?} at byte {offset}")
108 }
109 Syntax { offset, expected } => {
110 write!(f, "expected {expected} at byte {offset}")
111 }
112 InvalidUtf8 { offset } => write!(f, "invalid UTF-8 at byte {offset}"),
113 TrailingData { offset } => write!(f, "trailing data at byte {offset}"),
114 Empty => write!(f, "empty input"),
115 }
116 }
117}
118
119impl std::error::Error for BoundError {}
120
121#[derive(Clone, Debug, PartialEq, Eq)]
123pub enum ParseError {
124 Bound(BoundError),
126 Schema { message: String },
129}
130
131impl fmt::Display for ParseError {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 match self {
134 ParseError::Bound(e) => write!(f, "bounds: {e}"),
135 ParseError::Schema { message } => write!(f, "schema: {message}"),
136 }
137 }
138}
139
140impl std::error::Error for ParseError {
141 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
142 match self {
143 ParseError::Bound(e) => Some(e),
144 ParseError::Schema { .. } => None,
145 }
146 }
147}
148
149impl From<BoundError> for ParseError {
150 fn from(e: BoundError) -> Self {
151 ParseError::Bound(e)
152 }
153}
154
155pub fn parse_bounded<T: serde::de::DeserializeOwned>(
167 bytes: &[u8],
168 limits: &ParseLimits,
169) -> Result<T, ParseError> {
170 scan(bytes, limits)?;
171 serde_json::from_slice(bytes).map_err(|e| ParseError::Schema {
172 message: e.to_string(),
173 })
174}
175
176pub fn scan(bytes: &[u8], limits: &ParseLimits) -> Result<ScanStats, BoundError> {
183 if bytes.len() > limits.max_bytes {
184 return Err(BoundError::TooLarge {
185 limit: limits.max_bytes,
186 actual: bytes.len(),
187 });
188 }
189 Scanner {
190 b: bytes,
191 pos: 0,
192 limits,
193 stack: Vec::new(),
194 values: 0,
195 max_depth_seen: 0,
196 }
197 .run()
198}
199
200enum Frame {
201 Array { len: usize },
202 Object { len: usize, keys: HashSet<String> },
203}
204
205struct Scanner<'a> {
206 b: &'a [u8],
207 pos: usize,
208 limits: &'a ParseLimits,
209 stack: Vec<Frame>,
210 values: usize,
211 max_depth_seen: usize,
212}
213
214enum Step {
215 Value,
216 AfterValue,
217}
218
219fn syntax(offset: usize, expected: &'static str) -> BoundError {
220 BoundError::Syntax { offset, expected }
221}
222
223impl Scanner<'_> {
224 fn peek(&self) -> Option<u8> {
225 self.b.get(self.pos).copied()
226 }
227
228 fn skip_ws(&mut self) {
229 while let Some(b' ' | b'\t' | b'\n' | b'\r') = self.peek() {
230 self.pos += 1;
231 }
232 }
233
234 fn run(mut self) -> Result<ScanStats, BoundError> {
235 self.skip_ws();
236 if self.pos == self.b.len() {
237 return Err(BoundError::Empty);
238 }
239 let mut step = Step::Value;
240 loop {
241 step = match step {
242 Step::Value => self.value()?,
243 Step::AfterValue => {
244 self.skip_ws();
245 match self.stack.last() {
246 None => {
247 if self.pos != self.b.len() {
248 return Err(BoundError::TrailingData { offset: self.pos });
249 }
250 return Ok(ScanStats {
251 bytes: self.b.len(),
252 max_depth_seen: self.max_depth_seen,
253 values: self.values,
254 });
255 }
256 Some(Frame::Array { .. }) => match self.peek() {
257 Some(b',') => {
258 self.pos += 1;
259 self.skip_ws();
260 self.begin_member()?;
261 Step::Value
262 }
263 Some(b']') => {
264 self.pos += 1;
265 self.stack.pop();
266 Step::AfterValue
267 }
268 _ => return Err(syntax(self.pos, "',' or ']'")),
269 },
270 Some(Frame::Object { .. }) => match self.peek() {
271 Some(b',') => {
272 self.pos += 1;
273 self.skip_ws();
274 self.key()?;
275 Step::Value
276 }
277 Some(b'}') => {
278 self.pos += 1;
279 self.stack.pop();
280 Step::AfterValue
281 }
282 _ => return Err(syntax(self.pos, "',' or '}'")),
283 },
284 }
285 }
286 };
287 }
288 }
289
290 fn begin_member(&mut self) -> Result<(), BoundError> {
292 let limit = self.limits.max_collection_len;
293 let offset = self.pos;
294 let len = match self.stack.last_mut() {
295 Some(Frame::Array { len }) | Some(Frame::Object { len, .. }) => len,
296 None => return Ok(()),
297 };
298 *len += 1;
299 if *len > limit {
300 return Err(BoundError::CollectionTooLong { limit, offset });
301 }
302 Ok(())
303 }
304
305 fn count_value(&mut self) -> Result<(), BoundError> {
306 self.values += 1;
307 if self.values > self.limits.max_total_values {
308 return Err(BoundError::TooManyValues {
309 limit: self.limits.max_total_values,
310 offset: self.pos,
311 });
312 }
313 Ok(())
314 }
315
316 fn push(&mut self, frame: Frame) -> Result<(), BoundError> {
317 if self.stack.len() >= self.limits.max_depth {
318 return Err(BoundError::TooDeep {
319 limit: self.limits.max_depth,
320 offset: self.pos,
321 });
322 }
323 self.stack.push(frame);
324 self.max_depth_seen = self.max_depth_seen.max(self.stack.len());
325 Ok(())
326 }
327
328 fn key(&mut self) -> Result<(), BoundError> {
330 if self.peek() != Some(b'"') {
331 return Err(syntax(self.pos, "object key"));
332 }
333 self.begin_member()?;
334 let offset = self.pos;
335 let key = self.string(true)?.unwrap_or_default();
336 if let Some(Frame::Object { keys, .. }) = self.stack.last_mut() {
337 if keys.contains(&key) {
338 return Err(BoundError::DuplicateKey {
339 key: key.chars().take(64).collect(),
340 offset,
341 });
342 }
343 keys.insert(key);
344 }
345 self.skip_ws();
346 if self.peek() != Some(b':') {
347 return Err(syntax(self.pos, "':'"));
348 }
349 self.pos += 1;
350 self.skip_ws();
351 Ok(())
352 }
353
354 fn value(&mut self) -> Result<Step, BoundError> {
356 let Some(c) = self.peek() else {
357 return Err(syntax(self.pos, "value"));
358 };
359 match c {
360 b'{' => {
361 self.count_value()?;
362 self.push(Frame::Object {
363 len: 0,
364 keys: HashSet::new(),
365 })?;
366 self.pos += 1;
367 self.skip_ws();
368 if self.peek() == Some(b'}') {
369 self.pos += 1;
370 self.stack.pop();
371 return Ok(Step::AfterValue);
372 }
373 self.key()?;
374 Ok(Step::Value)
375 }
376 b'[' => {
377 self.count_value()?;
378 self.push(Frame::Array { len: 0 })?;
379 self.pos += 1;
380 self.skip_ws();
381 if self.peek() == Some(b']') {
382 self.pos += 1;
383 self.stack.pop();
384 return Ok(Step::AfterValue);
385 }
386 self.begin_member()?;
387 Ok(Step::Value)
388 }
389 b'"' => {
390 self.count_value()?;
391 self.string(false)?;
392 Ok(Step::AfterValue)
393 }
394 b'-' | b'0'..=b'9' => {
395 self.count_value()?;
396 self.number()?;
397 Ok(Step::AfterValue)
398 }
399 b't' => self.literal(b"true"),
400 b'f' => self.literal(b"false"),
401 b'n' => self.literal(b"null"),
402 _ => Err(syntax(self.pos, "value")),
403 }
404 }
405
406 fn literal(&mut self, word: &'static [u8]) -> Result<Step, BoundError> {
407 self.count_value()?;
408 if self.b[self.pos..].starts_with(word) {
409 self.pos += word.len();
410 Ok(Step::AfterValue)
411 } else {
412 Err(syntax(self.pos, "literal true, false or null"))
413 }
414 }
415
416 fn digits(&mut self, start: usize) -> Result<usize, BoundError> {
417 let mut n = 0;
418 while let Some(b'0'..=b'9') = self.peek() {
419 self.pos += 1;
420 n += 1;
421 self.check_number_len(start)?;
422 }
423 Ok(n)
424 }
425
426 fn check_number_len(&self, start: usize) -> Result<(), BoundError> {
427 if self.pos - start > self.limits.max_number_bytes {
428 return Err(BoundError::NumberTooLong {
429 limit: self.limits.max_number_bytes,
430 offset: start,
431 });
432 }
433 Ok(())
434 }
435
436 fn number(&mut self) -> Result<(), BoundError> {
437 let start = self.pos;
438 if self.peek() == Some(b'-') {
439 self.pos += 1;
440 self.check_number_len(start)?;
441 }
442 match self.peek() {
443 Some(b'0') => {
444 self.pos += 1;
445 self.check_number_len(start)?;
446 }
447 Some(b'1'..=b'9') => {
448 self.digits(start)?;
449 }
450 _ => return Err(syntax(self.pos, "digit")),
451 }
452 if self.peek() == Some(b'.') {
453 if !self.limits.allow_fractional_numbers {
454 return Err(BoundError::FractionalNumber { offset: self.pos });
455 }
456 self.pos += 1;
457 self.check_number_len(start)?;
458 if self.digits(start)? == 0 {
459 return Err(syntax(self.pos, "digit"));
460 }
461 }
462 if let Some(b'e' | b'E') = self.peek() {
463 if !self.limits.allow_fractional_numbers {
464 return Err(BoundError::FractionalNumber { offset: self.pos });
465 }
466 self.pos += 1;
467 self.check_number_len(start)?;
468 if let Some(b'+' | b'-') = self.peek() {
469 self.pos += 1;
470 self.check_number_len(start)?;
471 }
472 if self.digits(start)? == 0 {
473 return Err(syntax(self.pos, "digit"));
474 }
475 }
476 Ok(())
477 }
478
479 fn hex4(&mut self) -> Result<u32, BoundError> {
480 let mut v = 0u32;
481 for _ in 0..4 {
482 let d = match self.peek() {
483 Some(c @ b'0'..=b'9') => c - b'0',
484 Some(c @ b'a'..=b'f') => c - b'a' + 10,
485 Some(c @ b'A'..=b'F') => c - b'A' + 10,
486 _ => return Err(syntax(self.pos, "hex digit")),
487 };
488 v = v * 16 + u32::from(d);
489 self.pos += 1;
490 }
491 Ok(v)
492 }
493
494 fn string(&mut self, decode: bool) -> Result<Option<String>, BoundError> {
497 let open = self.pos;
498 self.pos += 1;
499 let start = self.pos;
500 let limit = self.limits.max_string_bytes;
501 let mut out = decode.then(String::new);
502 loop {
503 if self.pos - start > limit {
504 return Err(BoundError::StringTooLong {
505 limit,
506 offset: open,
507 });
508 }
509 let Some(c) = self.peek() else {
510 return Err(syntax(self.pos, "closing '\"'"));
511 };
512 match c {
513 b'"' => {
514 self.pos += 1;
515 return Ok(out);
516 }
517 b'\\' => {
518 self.pos += 1;
519 let esc = self.peek();
520 let ch = match esc {
521 Some(b'"') => '"',
522 Some(b'\\') => '\\',
523 Some(b'/') => '/',
524 Some(b'b') => '\u{8}',
525 Some(b'f') => '\u{c}',
526 Some(b'n') => '\n',
527 Some(b'r') => '\r',
528 Some(b't') => '\t',
529 Some(b'u') => {
530 let at = self.pos - 1;
531 self.pos += 1;
532 let hi = self.hex4()?;
533 let cp = match hi {
534 0xD800..=0xDBFF => {
535 if !self.b[self.pos..].starts_with(b"\\u") {
536 return Err(syntax(at, "valid surrogate pair"));
537 }
538 self.pos += 2;
539 let lo = self.hex4()?;
540 if !(0xDC00..=0xDFFF).contains(&lo) {
541 return Err(syntax(at, "valid surrogate pair"));
542 }
543 0x10000 + ((hi - 0xD800) << 10) + (lo - 0xDC00)
544 }
545 0xDC00..=0xDFFF => {
546 return Err(syntax(at, "valid surrogate pair"));
547 }
548 v => v,
549 };
550 if let Some(s) = out.as_mut() {
551 s.push(char::from_u32(cp).unwrap_or('\u{fffd}'));
553 }
554 continue;
555 }
556 _ => return Err(syntax(self.pos, "escape character")),
557 };
558 self.pos += 1;
559 if let Some(s) = out.as_mut() {
560 s.push(ch);
561 }
562 }
563 0x00..=0x1F => {
564 return Err(syntax(self.pos, "escaped control character"));
565 }
566 0x20..=0x7F => {
567 self.pos += 1;
568 if let Some(s) = out.as_mut() {
569 s.push(char::from(c));
570 }
571 }
572 _ => {
573 let width = match c {
574 0xC2..=0xDF => 2,
575 0xE0..=0xEF => 3,
576 0xF0..=0xF4 => 4,
577 _ => return Err(BoundError::InvalidUtf8 { offset: self.pos }),
578 };
579 let end = self.pos + width;
580 let Some(span) = self.b.get(self.pos..end) else {
581 return Err(BoundError::InvalidUtf8 { offset: self.pos });
582 };
583 let Ok(text) = std::str::from_utf8(span) else {
584 return Err(BoundError::InvalidUtf8 { offset: self.pos });
585 };
586 if let Some(s) = out.as_mut() {
587 s.push_str(text);
588 }
589 self.pos = end;
590 }
591 }
592 }
593 }
594}
595
596#[cfg(test)]
597mod tests {
598 use super::*;
599
600 const WIDE: ParseLimits = ParseLimits {
601 max_bytes: 1 << 20,
602 max_depth: 64,
603 max_string_bytes: 1 << 16,
604 max_collection_len: 1 << 16,
605 max_total_values: 1 << 20,
606 max_number_bytes: 64,
607 allow_fractional_numbers: true,
608 };
609
610 fn ok(s: &str, l: &ParseLimits) -> ScanStats {
611 scan(s.as_bytes(), l).unwrap_or_else(|e| panic!("{s:?}: {e}"))
612 }
613
614 fn err(s: &str, l: &ParseLimits) -> BoundError {
615 scan(s.as_bytes(), l).expect_err(s)
616 }
617
618 #[test]
619 fn accepts_valid_documents() {
620 for s in [
621 "0",
622 "-0",
623 "1.5e-3",
624 "-12E+4",
625 "true",
626 "false",
627 "null",
628 "\"\"",
629 "\"a\\n\\u00e9\\ud83d\\ude00\"",
630 "\"é😀\"",
631 " [ ] ",
632 "{}",
633 "{\"a\":[1,{\"b\":null}],\"c\":\"d\"}",
634 "\r\n\t[1 , 2]\n",
635 ] {
636 ok(s, &WIDE);
637 }
638 }
639
640 #[test]
641 fn stats() {
642 let s = ok("{\"a\":[1,2,{}],\"b\":\"x\"}", &WIDE);
643 assert_eq!(s.max_depth_seen, 3);
644 assert_eq!(s.values, 6);
646 assert_eq!(ok("7", &WIDE).max_depth_seen, 0);
647 }
648
649 #[test]
650 fn syntax_errors() {
651 for s in [
652 "[1,]",
653 "[1 2]",
654 "{\"a\" 1}",
655 "{\"a\":}",
656 "{1:2}",
657 "{\"a\":1,}",
658 "tru",
659 "nul",
660 "01x",
661 "[01]",
662 "-",
663 "1.",
664 "1e",
665 "1e+",
666 ".5",
667 "+1",
668 "\"abc",
669 "\"\\x\"",
670 "\"\\u12G4\"",
671 "[",
672 "{",
673 "'a'",
674 "\"a\tb\"",
675 "\"\u{1}\"",
676 ] {
677 let e = err(s, &WIDE);
678 assert!(
679 matches!(
680 e,
681 BoundError::Syntax { .. } | BoundError::TrailingData { .. }
682 ),
683 "{s:?}: {e:?}"
684 );
685 }
686 }
687
688 #[test]
689 fn empty_and_trailing() {
690 assert_eq!(err("", &WIDE), BoundError::Empty);
691 assert_eq!(err(" \n\t ", &WIDE), BoundError::Empty);
692 assert_eq!(err("1 2", &WIDE), BoundError::TrailingData { offset: 2 });
693 assert_eq!(err("{} {}", &WIDE), BoundError::TrailingData { offset: 3 });
694 assert_eq!(err("01", &WIDE), BoundError::TrailingData { offset: 1 });
695 }
696
697 #[test]
698 fn too_large_checked_first() {
699 let l = ParseLimits {
700 max_bytes: 4,
701 ..WIDE
702 };
703 ok("[12]", &l);
704 assert_eq!(
706 err("#####", &l),
707 BoundError::TooLarge {
708 limit: 4,
709 actual: 5
710 }
711 );
712 let zero = ParseLimits {
713 max_bytes: 0,
714 ..WIDE
715 };
716 assert_eq!(err("", &zero), BoundError::Empty);
717 }
718
719 #[test]
720 fn depth_limit() {
721 let l = ParseLimits {
722 max_depth: 3,
723 ..WIDE
724 };
725 assert_eq!(ok("[[[1]]]", &l).max_depth_seen, 3);
726 assert_eq!(ok("{\"a\":{\"b\":[]}}", &l).max_depth_seen, 3);
727 assert_eq!(
728 err("[[[[1]]]]", &l),
729 BoundError::TooDeep {
730 limit: 3,
731 offset: 3
732 }
733 );
734 let flat = ParseLimits {
735 max_depth: 0,
736 ..WIDE
737 };
738 ok("\"scalar\"", &flat);
739 assert!(matches!(err("[]", &flat), BoundError::TooDeep { .. }));
740 }
741
742 #[test]
743 fn deep_nesting_is_refused_without_recursion() {
744 let n = 10_000;
745 let s = "[".repeat(n) + &"]".repeat(n);
746 assert!(matches!(
747 err(&s, &WIDE),
748 BoundError::TooDeep { limit: 64, .. }
749 ));
750 let deep = ParseLimits {
751 max_depth: n,
752 ..WIDE
753 };
754 assert_eq!(ok(&s, &deep).max_depth_seen, n);
755 let obj = "{\"a\":".repeat(n) + "1" + &"}".repeat(n);
756 assert_eq!(ok(&obj, &deep).max_depth_seen, n);
757 }
758
759 #[test]
760 fn string_limit() {
761 let l = ParseLimits {
762 max_string_bytes: 3,
763 ..WIDE
764 };
765 ok("\"abc\"", &l);
766 ok("{\"abc\":\"xyz\"}", &l);
767 ok("\"a\\n\"", &l);
769 assert_eq!(
770 err("\"abcd\"", &l),
771 BoundError::StringTooLong {
772 limit: 3,
773 offset: 0
774 }
775 );
776 assert!(matches!(
777 err("{\"abcd\":1}", &l),
778 BoundError::StringTooLong { .. }
779 ));
780 assert!(matches!(
781 err("\"\\u0041\"", &l),
782 BoundError::StringTooLong { .. }
783 ));
784 ok("\"é\"", &l);
786 assert!(matches!(
787 err("\"éé\"", &l),
788 BoundError::StringTooLong { .. }
789 ));
790 }
791
792 #[test]
793 fn string_limit_fires_before_end_of_input() {
794 let l = ParseLimits {
795 max_string_bytes: 3,
796 ..WIDE
797 };
798 assert!(matches!(
800 err("\"abcdefgh", &l),
801 BoundError::StringTooLong { .. }
802 ));
803 }
804
805 #[test]
806 fn collection_limit() {
807 let l = ParseLimits {
808 max_collection_len: 2,
809 ..WIDE
810 };
811 ok("[1,2]", &l);
812 ok("{\"a\":1,\"b\":2}", &l);
813 assert_eq!(
814 err("[1,2,3]", &l),
815 BoundError::CollectionTooLong {
816 limit: 2,
817 offset: 5
818 }
819 );
820 assert!(matches!(
821 err("{\"a\":1,\"b\":2,\"c\":3}", &l),
822 BoundError::CollectionTooLong { .. }
823 ));
824 assert!(matches!(
826 err("[1,2,3,#", &l),
827 BoundError::CollectionTooLong { .. }
828 ));
829 }
830
831 #[test]
832 fn total_value_limit() {
833 let l = ParseLimits {
834 max_total_values: 3,
835 ..WIDE
836 };
837 ok("[1,2]", &l);
838 ok("{\"a\":1,\"b\":2}", &l);
839 assert_eq!(
840 err("[1,2,3]", &l),
841 BoundError::TooManyValues {
842 limit: 3,
843 offset: 5
844 }
845 );
846 assert!(matches!(
847 err("[[],[],[]]", &l),
848 BoundError::TooManyValues { .. }
849 ));
850 }
851
852 #[test]
853 fn number_limit() {
854 let l = ParseLimits {
855 max_number_bytes: 4,
856 ..WIDE
857 };
858 ok("1234", &l);
859 ok("-123", &l);
860 ok("1.25", &l);
861 assert_eq!(
862 err("12345", &l),
863 BoundError::NumberTooLong {
864 limit: 4,
865 offset: 0
866 }
867 );
868 assert!(matches!(
869 err("[-1234]", &l),
870 BoundError::NumberTooLong { offset: 1, .. }
871 ));
872 assert!(matches!(err("1.234", &l), BoundError::NumberTooLong { .. }));
873 assert!(matches!(err("1e+10", &l), BoundError::NumberTooLong { .. }));
874 }
875
876 #[test]
877 fn fractional_numbers() {
878 let l = ParseLimits {
879 allow_fractional_numbers: false,
880 ..WIDE
881 };
882 ok("-42", &l);
883 ok("[0,1,2]", &l);
884 assert_eq!(err("1.5", &l), BoundError::FractionalNumber { offset: 1 });
885 assert_eq!(err("[1e3]", &l), BoundError::FractionalNumber { offset: 2 });
886 assert_eq!(err("2E-1", &l), BoundError::FractionalNumber { offset: 1 });
887 ok("1.5", &WIDE);
888 ok("1e3", &WIDE);
889 }
890
891 #[test]
892 fn duplicate_keys() {
893 assert_eq!(
894 err("{\"a\":1,\"a\":2}", &WIDE),
895 BoundError::DuplicateKey {
896 key: "a".into(),
897 offset: 7
898 }
899 );
900 assert!(matches!(
902 err("{\"a\":1,\"\\u0061\":2}", &WIDE),
903 BoundError::DuplicateKey { .. }
904 ));
905 assert!(matches!(
906 err("{\"\\/\":1,\"/\":2}", &WIDE),
907 BoundError::DuplicateKey { .. }
908 ));
909 ok("{\"a\":{\"a\":1},\"b\":[{\"a\":1},{\"a\":2}]}", &WIDE);
911 assert!(matches!(
913 err("{\"x\":{\"a\":1,\"a\":2}}", &WIDE),
914 BoundError::DuplicateKey { .. }
915 ));
916 let long = "k".repeat(100);
918 let s = format!("{{\"{long}\":1,\"{long}\":2}}");
919 match err(&s, &WIDE) {
920 BoundError::DuplicateKey { key, .. } => assert_eq!(key.chars().count(), 64),
921 e => panic!("{e:?}"),
922 }
923 }
924
925 #[test]
926 fn surrogates() {
927 ok("\"\\ud83d\\ude00\"", &WIDE);
928 ok("\"\\uD83D\\uDE00\"", &WIDE);
929 for s in [
930 "\"\\ud83d\"",
931 "\"\\ud83dx\"",
932 "\"\\ud83d\\u0041\"",
933 "\"\\ude00\"",
934 "\"\\ude00\\ud83d\"",
935 "\"\\ud83d\\n\"",
936 ] {
937 assert!(matches!(err(s, &WIDE), BoundError::Syntax { .. }), "{s}");
938 }
939 assert!(matches!(
941 err("{\"😀\":1,\"\\ud83d\\ude00\":2}", &WIDE),
942 BoundError::DuplicateKey { .. }
943 ));
944 }
945
946 #[test]
947 fn invalid_utf8() {
948 let cases: [&[u8]; 6] = [
949 b"\"\xff\"",
950 b"\"\xc0\xaf\"",
951 b"\"\xe0\x80\xaf\"",
952 b"\"\xed\xa0\x80\"",
953 b"\"\xf5\x80\x80\x80\"",
954 b"\"\xc3",
955 ];
956 for c in cases {
957 assert_eq!(
958 scan(c, &WIDE),
959 Err(BoundError::InvalidUtf8 { offset: 1 }),
960 "{c:?}"
961 );
962 }
963 }
964
965 #[derive(serde::Deserialize, Debug, PartialEq)]
966 #[serde(deny_unknown_fields)]
967 struct Doc {
968 name: String,
969 n: u32,
970 }
971
972 #[test]
973 fn parse_bounded_typed() {
974 let d: Doc = parse_bounded(br#"{"name":"x","n":3}"#, &WIDE).unwrap();
975 assert_eq!(
976 d,
977 Doc {
978 name: "x".into(),
979 n: 3
980 }
981 );
982 let e = parse_bounded::<Doc>(br#"{"name":"x","n":3,"extra":1}"#, &WIDE).unwrap_err();
983 assert!(matches!(e, ParseError::Schema { ref message } if message.contains("extra")));
984 let e = parse_bounded::<Doc>(br#"{"name":"x","n":3,"n":4}"#, &WIDE).unwrap_err();
986 assert!(matches!(
987 e,
988 ParseError::Bound(BoundError::DuplicateKey { .. })
989 ));
990 let l = ParseLimits {
992 max_string_bytes: 2,
993 ..WIDE
994 };
995 let e = parse_bounded::<Doc>(br#"{"name":"xyz","n":3}"#, &l).unwrap_err();
996 assert!(matches!(
997 e,
998 ParseError::Bound(BoundError::StringTooLong { .. })
999 ));
1000 let e = parse_bounded::<Doc>(
1001 br#"{"name":"x","n":3.5}"#,
1002 &ParseLimits {
1003 allow_fractional_numbers: false,
1004 ..WIDE
1005 },
1006 )
1007 .unwrap_err();
1008 assert!(matches!(
1009 e,
1010 ParseError::Bound(BoundError::FractionalNumber { .. })
1011 ));
1012 assert!(e.to_string().starts_with("bounds:"));
1013 }
1014
1015 #[test]
1016 fn display_is_informative() {
1017 let e = BoundError::TooLarge {
1018 limit: 1,
1019 actual: 2,
1020 };
1021 assert_eq!(e.to_string(), "input is 2 bytes, limit 1");
1022 }
1023}