1use prism::operation::TermValue;
40use prism::pipeline::{
41 ConstrainedTypeShape, ConstraintRef, IntoBindingValue, PartitionProductFields,
42};
43
44#[cfg(feature = "alloc")]
48pub(crate) const TAG_NULL: u8 = 0x00;
49#[cfg(feature = "alloc")]
50pub(crate) const TAG_FALSE: u8 = 0x01;
51#[cfg(feature = "alloc")]
52pub(crate) const TAG_TRUE: u8 = 0x02;
53#[cfg(feature = "alloc")]
54pub(crate) const TAG_NUMBER: u8 = 0x03;
55#[cfg(feature = "alloc")]
56pub(crate) const TAG_STRING: u8 = 0x04;
57#[cfg(feature = "alloc")]
58pub(crate) const TAG_ARRAY: u8 = 0x05;
59#[cfg(feature = "alloc")]
60pub(crate) const TAG_OBJECT: u8 = 0x06;
61
62#[cfg(feature = "alloc")]
65const INVALID_JSON_VIOLATION: prism::pipeline::ShapeViolation = prism::pipeline::ShapeViolation {
66 shape_iri: "https://uor.foundation/addr/JsonValue",
67 constraint_iri: "https://uor.foundation/addr/JsonValue/validUtf8Json",
68 property_iri: "https://uor.foundation/addr/inputBytes",
69 expected_range: "https://uor.foundation/addr/ValidUtf8Json",
70 min_count: 0,
71 max_count: 1,
72 kind: prism::pipeline::ViolationKind::ValueCheck,
73};
74
75#[cfg(feature = "alloc")]
76const DEPTH_BOUND_VIOLATION: prism::pipeline::ShapeViolation = prism::pipeline::ShapeViolation {
77 shape_iri: "https://uor.foundation/addr/JsonValue",
78 constraint_iri: "https://uor.foundation/addr/JsonValue/depthBound",
79 property_iri: "https://uor.foundation/addr/JsonValue/depth",
80 expected_range: "http://www.w3.org/2001/XMLSchema#nonNegativeInteger",
81 min_count: 0,
82 max_count: crate::json::shapes::bounds::MAX_JSON_DEPTH as u32,
83 kind: prism::pipeline::ViolationKind::CardinalityViolation,
84};
85
86#[derive(Clone, Copy, Debug)]
92pub struct JsonCarrier<'a>(&'a [u8]);
93
94impl<'a> JsonCarrier<'a> {
95 #[must_use]
97 pub fn new(canonical_bytes: &'a [u8]) -> Self {
98 Self(canonical_bytes)
99 }
100
101 #[must_use]
103 pub fn canonical_bytes(&self) -> &'a [u8] {
104 self.0
105 }
106}
107
108impl ConstrainedTypeShape for JsonCarrier<'_> {
109 const IRI: &'static str = "https://uor.foundation/addr/JsonValue";
110 const SITE_COUNT: usize = 1;
111 const CONSTRAINTS: &'static [ConstraintRef] = &[];
112 const CYCLE_SIZE: u64 = u64::MAX;
113}
114
115impl prism::uor_foundation::pipeline::__sdk_seal::Sealed for JsonCarrier<'_> {}
116
117impl<'a> IntoBindingValue<'a> for JsonCarrier<'a> {
118 fn as_binding_value<const INLINE_BYTES: usize>(&self) -> TermValue<'a, INLINE_BYTES> {
119 TermValue::borrowed(self.0)
120 }
121}
122
123impl PartitionProductFields for JsonCarrier<'_> {
124 const FIELDS: &'static [(u32, u32)] = &[];
125 const FIELD_NAMES: &'static [&'static str] = &[];
126}
127
128#[cfg(feature = "alloc")]
133pub use alloc_impl::{canonicalize, ArrayIter, JsonValue, JsonValueRef, ObjectIter};
134
135#[cfg(feature = "alloc")]
136mod alloc_impl {
137 use super::*;
138 use crate::canonical::nfc;
139 use crate::json::shapes::bounds::MAX_JSON_DEPTH;
140 use alloc::collections::BTreeSet;
141 use alloc::vec::Vec;
142 use prism::pipeline::ShapeViolation;
143
144 #[derive(Clone, PartialEq, Eq)]
152 pub struct JsonValue {
153 pub(crate) bytes: Vec<u8>,
154 }
155
156 impl core::fmt::Debug for JsonValue {
157 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
158 f.debug_struct("JsonValue")
159 .field("len", &self.bytes.len())
160 .finish_non_exhaustive()
161 }
162 }
163
164 impl JsonValue {
165 pub fn parse(raw: &[u8]) -> Result<Self, ShapeViolation> {
177 let mut value = Self { bytes: Vec::new() };
178 let mut p = Parser::new(raw);
179 p.skip_ws();
180 parse_value(&mut p, &mut value, 0)?;
181 p.skip_ws();
182 if !p.is_eof() {
183 return Err(INVALID_JSON_VIOLATION);
184 }
185 Ok(value)
186 }
187
188 #[must_use]
192 pub fn tagged_bytes(&self) -> &[u8] {
193 &self.bytes
194 }
195
196 fn push_byte(&mut self, b: u8) {
197 self.bytes.push(b);
198 }
199
200 fn push_u32_be(&mut self, v: u32) {
201 self.bytes.extend_from_slice(&v.to_be_bytes());
202 }
203
204 fn extend(&mut self, data: &[u8]) {
205 self.bytes.extend_from_slice(data);
206 }
207
208 fn patch_u32_be(&mut self, offset: usize, v: u32) {
209 self.bytes[offset..offset + 4].copy_from_slice(&v.to_be_bytes());
210 }
211 }
212
213 pub fn canonicalize(raw: &[u8]) -> Result<Vec<u8>, ShapeViolation> {
219 let value = JsonValue::parse(raw)?;
220 let mut out = Vec::new();
221 let mut pos = 0;
222 emit_value(value.tagged_bytes(), &mut pos, &mut out)?;
223 Ok(out)
224 }
225
226 struct Parser<'a> {
229 input: &'a [u8],
230 pos: usize,
231 }
232
233 impl<'a> Parser<'a> {
234 fn new(input: &'a [u8]) -> Self {
235 Self { input, pos: 0 }
236 }
237 fn is_eof(&self) -> bool {
238 self.pos >= self.input.len()
239 }
240 fn peek(&self) -> Result<u8, ShapeViolation> {
241 if self.is_eof() {
242 return Err(INVALID_JSON_VIOLATION);
243 }
244 Ok(self.input[self.pos])
245 }
246 fn bump(&mut self) -> Result<u8, ShapeViolation> {
247 let b = self.peek()?;
248 self.pos += 1;
249 Ok(b)
250 }
251 fn skip_ws(&mut self) {
252 while self.pos < self.input.len() {
253 match self.input[self.pos] {
254 b' ' | b'\t' | b'\n' | b'\r' => self.pos += 1,
255 _ => break,
256 }
257 }
258 }
259 fn expect(&mut self, byte: u8) -> Result<(), ShapeViolation> {
260 if self.bump()? != byte {
261 return Err(INVALID_JSON_VIOLATION);
262 }
263 Ok(())
264 }
265 fn expect_lit(&mut self, lit: &[u8]) -> Result<(), ShapeViolation> {
266 if self.pos + lit.len() > self.input.len()
267 || &self.input[self.pos..self.pos + lit.len()] != lit
268 {
269 return Err(INVALID_JSON_VIOLATION);
270 }
271 self.pos += lit.len();
272 Ok(())
273 }
274 }
275
276 fn parse_value(
277 p: &mut Parser<'_>,
278 out: &mut JsonValue,
279 depth: usize,
280 ) -> Result<(), ShapeViolation> {
281 if depth > MAX_JSON_DEPTH {
282 return Err(DEPTH_BOUND_VIOLATION);
283 }
284 p.skip_ws();
285 match p.peek()? {
286 b'n' => {
287 p.expect_lit(b"null")?;
288 out.push_byte(TAG_NULL);
289 Ok(())
290 }
291 b't' => {
292 p.expect_lit(b"true")?;
293 out.push_byte(TAG_TRUE);
294 Ok(())
295 }
296 b'f' => {
297 p.expect_lit(b"false")?;
298 out.push_byte(TAG_FALSE);
299 Ok(())
300 }
301 b'"' => parse_string(p, out),
302 b'-' | b'0'..=b'9' => parse_number(p, out),
303 b'[' => parse_array(p, out, depth + 1),
304 b'{' => parse_object(p, out, depth + 1),
305 _ => Err(INVALID_JSON_VIOLATION),
306 }
307 }
308
309 fn parse_array(
310 p: &mut Parser<'_>,
311 out: &mut JsonValue,
312 depth: usize,
313 ) -> Result<(), ShapeViolation> {
314 p.expect(b'[')?;
315 out.push_byte(TAG_ARRAY);
316 let count_pos = out.bytes.len();
317 out.push_u32_be(0);
318 let mut count: u32 = 0;
319 p.skip_ws();
320 if p.peek()? == b']' {
321 p.pos += 1;
322 return Ok(());
323 }
324 loop {
325 parse_value(p, out, depth)?;
326 count += 1;
327 p.skip_ws();
328 match p.bump()? {
329 b',' => {
330 p.skip_ws();
331 continue;
332 }
333 b']' => break,
334 _ => return Err(INVALID_JSON_VIOLATION),
335 }
336 }
337 out.patch_u32_be(count_pos, count);
338 Ok(())
339 }
340
341 fn parse_object(
342 p: &mut Parser<'_>,
343 out: &mut JsonValue,
344 depth: usize,
345 ) -> Result<(), ShapeViolation> {
346 p.expect(b'{')?;
347 out.push_byte(TAG_OBJECT);
348 let count_pos = out.bytes.len();
349 out.push_u32_be(0);
350 let mut count: u32 = 0;
351 let mut seen: BTreeSet<Vec<u8>> = BTreeSet::new();
358 p.skip_ws();
359 if p.peek()? == b'}' {
360 p.pos += 1;
361 return Ok(());
362 }
363 loop {
364 p.skip_ws();
365 if p.peek()? != b'"' {
366 return Err(INVALID_JSON_VIOLATION);
367 }
368 let key = decode_string_into_nfc(p)?;
369 if seen.contains(&key) {
370 return Err(INVALID_JSON_VIOLATION);
371 }
372 out.push_u32_be(key.len() as u32);
373 out.extend(&key);
374 seen.insert(key);
375 p.skip_ws();
376 p.expect(b':')?;
377 p.skip_ws();
378 parse_value(p, out, depth)?;
379 count += 1;
380 p.skip_ws();
381 match p.bump()? {
382 b',' => continue,
383 b'}' => break,
384 _ => return Err(INVALID_JSON_VIOLATION),
385 }
386 }
387 out.patch_u32_be(count_pos, count);
388 Ok(())
389 }
390
391 fn parse_string(p: &mut Parser<'_>, out: &mut JsonValue) -> Result<(), ShapeViolation> {
392 let s = decode_string_into_nfc(p)?;
393 out.push_byte(TAG_STRING);
394 out.push_u32_be(s.len() as u32);
395 out.extend(&s);
396 Ok(())
397 }
398
399 fn decode_string_into_nfc(p: &mut Parser<'_>) -> Result<Vec<u8>, ShapeViolation> {
402 p.expect(b'"')?;
403 let mut stage1 = Vec::new();
404 loop {
405 if p.is_eof() {
406 return Err(INVALID_JSON_VIOLATION);
407 }
408 let b = p.input[p.pos];
409 match b {
410 b'"' => {
411 p.pos += 1;
412 break;
413 }
414 b'\\' => {
415 p.pos += 1;
416 let esc = p.bump()?;
417 match esc {
418 b'"' => stage1.push(b'"'),
419 b'\\' => stage1.push(b'\\'),
420 b'/' => stage1.push(b'/'),
421 b'b' => stage1.push(0x08),
422 b'f' => stage1.push(0x0C),
423 b'n' => stage1.push(0x0A),
424 b'r' => stage1.push(0x0D),
425 b't' => stage1.push(0x09),
426 b'u' => {
427 let cp = decode_u_escape(p)?;
428 let c = char::from_u32(cp).ok_or(INVALID_JSON_VIOLATION)?;
429 let mut tmp = [0u8; 4];
430 stage1.extend_from_slice(c.encode_utf8(&mut tmp).as_bytes());
431 }
432 _ => return Err(INVALID_JSON_VIOLATION),
433 }
434 }
435 0x00..=0x1F => return Err(INVALID_JSON_VIOLATION),
437 _ => {
438 stage1.push(b);
439 p.pos += 1;
440 }
441 }
442 }
443 normalize_nfc(&stage1)
444 }
445
446 fn normalize_nfc(stage1: &[u8]) -> Result<Vec<u8>, ShapeViolation> {
450 let mut cap = stage1.len().saturating_mul(3).max(64);
451 loop {
452 let mut buf = alloc::vec![0u8; cap];
453 match nfc::normalize_into(stage1, &mut buf) {
454 Ok(n) => {
455 buf.truncate(n);
456 return Ok(buf);
457 }
458 Err(nfc::NfcError::OutputOverflow) => {
459 cap = cap.saturating_mul(2);
460 }
461 Err(_) => return Err(INVALID_JSON_VIOLATION),
462 }
463 }
464 }
465
466 fn decode_u_escape(p: &mut Parser<'_>) -> Result<u32, ShapeViolation> {
467 let high = decode_hex4(p)?;
468 if (0xD800..=0xDBFF).contains(&high) {
469 if p.input.get(p.pos..p.pos + 2) != Some(b"\\u") {
470 return Err(INVALID_JSON_VIOLATION);
471 }
472 p.pos += 2;
473 let low = decode_hex4(p)?;
474 if !(0xDC00..=0xDFFF).contains(&low) {
475 return Err(INVALID_JSON_VIOLATION);
476 }
477 Ok(0x10000 + ((high - 0xD800) << 10) + (low - 0xDC00))
478 } else if (0xDC00..=0xDFFF).contains(&high) {
479 Err(INVALID_JSON_VIOLATION)
480 } else {
481 Ok(high)
482 }
483 }
484
485 fn decode_hex4(p: &mut Parser<'_>) -> Result<u32, ShapeViolation> {
486 if p.pos + 4 > p.input.len() {
487 return Err(INVALID_JSON_VIOLATION);
488 }
489 let mut v: u32 = 0;
490 for _ in 0..4 {
491 let d = p.input[p.pos];
492 p.pos += 1;
493 let nibble = match d {
494 b'0'..=b'9' => (d - b'0') as u32,
495 b'a'..=b'f' => 10 + (d - b'a') as u32,
496 b'A'..=b'F' => 10 + (d - b'A') as u32,
497 _ => return Err(INVALID_JSON_VIOLATION),
498 };
499 v = (v << 4) | nibble;
500 }
501 Ok(v)
502 }
503
504 fn parse_number(p: &mut Parser<'_>, out: &mut JsonValue) -> Result<(), ShapeViolation> {
505 let start = p.pos;
506 let mut has_decimal = false;
507 let mut has_exponent = false;
508 if p.peek()? == b'-' {
509 p.pos += 1;
510 }
511 match p.peek()? {
512 b'0' => p.pos += 1,
513 b'1'..=b'9' => {
514 p.pos += 1;
515 while let Ok(b) = p.peek() {
516 if b.is_ascii_digit() {
517 p.pos += 1;
518 } else {
519 break;
520 }
521 }
522 }
523 _ => return Err(INVALID_JSON_VIOLATION),
524 }
525 if p.peek().ok() == Some(b'.') {
526 has_decimal = true;
527 p.pos += 1;
528 let frac_start = p.pos;
529 while let Ok(b) = p.peek() {
530 if b.is_ascii_digit() {
531 p.pos += 1;
532 } else {
533 break;
534 }
535 }
536 if p.pos == frac_start {
537 return Err(INVALID_JSON_VIOLATION);
538 }
539 }
540 if let Ok(b) = p.peek() {
541 if b == b'e' || b == b'E' {
542 has_exponent = true;
543 p.pos += 1;
544 if let Ok(s) = p.peek() {
545 if s == b'+' || s == b'-' {
546 p.pos += 1;
547 }
548 }
549 let exp_start = p.pos;
550 while let Ok(d) = p.peek() {
551 if d.is_ascii_digit() {
552 p.pos += 1;
553 } else {
554 break;
555 }
556 }
557 if p.pos == exp_start {
558 return Err(INVALID_JSON_VIOLATION);
559 }
560 }
561 }
562 let raw = &p.input[start..p.pos];
563 let canon = canonicalize_number(raw, has_decimal || has_exponent)?;
564 out.push_byte(TAG_NUMBER);
565 out.push_u32_be(canon.len() as u32);
566 out.extend(&canon);
567 Ok(())
568 }
569
570 fn canonicalize_number(raw: &[u8], is_float_syntax: bool) -> Result<Vec<u8>, ShapeViolation> {
576 let is_negative_zero = raw == b"-0";
577 if is_float_syntax || is_negative_zero {
578 let s = core::str::from_utf8(raw).map_err(|_| INVALID_JSON_VIOLATION)?;
579 let v: f64 = s.parse().map_err(|_| INVALID_JSON_VIOLATION)?;
580 let mut ryu_buf = ryu::Buffer::new();
581 Ok(ryu_buf.format(v).as_bytes().to_vec())
582 } else {
583 Ok(raw.to_vec())
584 }
585 }
586
587 fn read_byte(tagged: &[u8], pos: &mut usize) -> Result<u8, ShapeViolation> {
590 if *pos >= tagged.len() {
591 return Err(INVALID_JSON_VIOLATION);
592 }
593 let b = tagged[*pos];
594 *pos += 1;
595 Ok(b)
596 }
597
598 fn read_u32_be(tagged: &[u8], pos: &mut usize) -> Result<u32, ShapeViolation> {
599 if *pos + 4 > tagged.len() {
600 return Err(INVALID_JSON_VIOLATION);
601 }
602 let v = u32::from_be_bytes([
603 tagged[*pos],
604 tagged[*pos + 1],
605 tagged[*pos + 2],
606 tagged[*pos + 3],
607 ]);
608 *pos += 4;
609 Ok(v)
610 }
611
612 fn read_slice<'a>(
613 tagged: &'a [u8],
614 pos: &mut usize,
615 len: usize,
616 ) -> Result<&'a [u8], ShapeViolation> {
617 if *pos + len > tagged.len() {
618 return Err(INVALID_JSON_VIOLATION);
619 }
620 let s = &tagged[*pos..*pos + len];
621 *pos += len;
622 Ok(s)
623 }
624
625 fn emit_value(tagged: &[u8], pos: &mut usize, out: &mut Vec<u8>) -> Result<(), ShapeViolation> {
626 let tag = read_byte(tagged, pos)?;
627 match tag {
628 TAG_NULL => {
629 out.extend_from_slice(b"null");
630 Ok(())
631 }
632 TAG_FALSE => {
633 out.extend_from_slice(b"false");
634 Ok(())
635 }
636 TAG_TRUE => {
637 out.extend_from_slice(b"true");
638 Ok(())
639 }
640 TAG_NUMBER => {
641 let len = read_u32_be(tagged, pos)? as usize;
642 let bytes = read_slice(tagged, pos, len)?;
643 out.extend_from_slice(bytes);
644 Ok(())
645 }
646 TAG_STRING => {
647 let len = read_u32_be(tagged, pos)? as usize;
648 let bytes = read_slice(tagged, pos, len)?;
649 emit_json_string(bytes, out);
650 Ok(())
651 }
652 TAG_ARRAY => {
653 let count = read_u32_be(tagged, pos)? as usize;
654 out.push(b'[');
655 for i in 0..count {
656 if i > 0 {
657 out.push(b',');
658 }
659 emit_value(tagged, pos, out)?;
660 }
661 out.push(b']');
662 Ok(())
663 }
664 TAG_OBJECT => emit_object(tagged, pos, out),
665 _ => Err(INVALID_JSON_VIOLATION),
666 }
667 }
668
669 fn emit_object(
670 tagged: &[u8],
671 pos: &mut usize,
672 out: &mut Vec<u8>,
673 ) -> Result<(), ShapeViolation> {
674 let count = read_u32_be(tagged, pos)? as usize;
675 let mut entries: Vec<usize> = Vec::with_capacity(count);
679 for _ in 0..count {
680 entries.push(*pos);
681 let key_len = read_u32_be(tagged, pos)? as usize;
682 *pos += key_len;
683 if *pos > tagged.len() {
684 return Err(INVALID_JSON_VIOLATION);
685 }
686 skip_value(tagged, pos)?;
687 }
688 entries.sort_by(|&a, &b| entry_key(a, tagged).cmp(entry_key(b, tagged)));
689 out.push(b'{');
690 for (i, &entry_off) in entries.iter().enumerate() {
691 if i > 0 {
692 out.push(b',');
693 }
694 let mut p = entry_off;
695 let key_len = read_u32_be(tagged, &mut p)? as usize;
696 let key_bytes = read_slice(tagged, &mut p, key_len)?;
697 emit_json_string(key_bytes, out);
698 out.push(b':');
699 emit_value(tagged, &mut p, out)?;
700 }
701 out.push(b'}');
702 Ok(())
703 }
704
705 fn entry_key(off: usize, tagged: &[u8]) -> &[u8] {
706 if off + 4 > tagged.len() {
707 return &[];
708 }
709 let key_len = u32::from_be_bytes([
710 tagged[off],
711 tagged[off + 1],
712 tagged[off + 2],
713 tagged[off + 3],
714 ]) as usize;
715 let start = off + 4;
716 if start + key_len > tagged.len() {
717 return &[];
718 }
719 &tagged[start..start + key_len]
720 }
721
722 fn skip_value(tagged: &[u8], pos: &mut usize) -> Result<(), ShapeViolation> {
723 let tag = read_byte(tagged, pos)?;
724 match tag {
725 TAG_NULL | TAG_FALSE | TAG_TRUE => Ok(()),
726 TAG_NUMBER | TAG_STRING => {
727 let len = read_u32_be(tagged, pos)? as usize;
728 *pos += len;
729 if *pos > tagged.len() {
730 Err(INVALID_JSON_VIOLATION)
731 } else {
732 Ok(())
733 }
734 }
735 TAG_ARRAY => {
736 let count = read_u32_be(tagged, pos)? as usize;
737 for _ in 0..count {
738 skip_value(tagged, pos)?;
739 }
740 Ok(())
741 }
742 TAG_OBJECT => {
743 let count = read_u32_be(tagged, pos)? as usize;
744 for _ in 0..count {
745 let key_len = read_u32_be(tagged, pos)? as usize;
746 *pos += key_len;
747 if *pos > tagged.len() {
748 return Err(INVALID_JSON_VIOLATION);
749 }
750 skip_value(tagged, pos)?;
751 }
752 Ok(())
753 }
754 _ => Err(INVALID_JSON_VIOLATION),
755 }
756 }
757
758 fn emit_json_string(bytes: &[u8], out: &mut Vec<u8>) {
760 out.push(b'"');
761 for &b in bytes {
762 match b {
763 b'"' => out.extend_from_slice(b"\\\""),
764 b'\\' => out.extend_from_slice(b"\\\\"),
765 0x08 => out.extend_from_slice(b"\\b"),
766 0x09 => out.extend_from_slice(b"\\t"),
767 0x0A => out.extend_from_slice(b"\\n"),
768 0x0C => out.extend_from_slice(b"\\f"),
769 0x0D => out.extend_from_slice(b"\\r"),
770 0x00..=0x1F => {
771 out.extend_from_slice(b"\\u00");
772 out.push(nibble_hex(b >> 4));
773 out.push(nibble_hex(b & 0x0f));
774 }
775 _ => out.push(b),
776 }
777 }
778 out.push(b'"');
779 }
780
781 fn nibble_hex(n: u8) -> u8 {
782 match n {
783 0..=9 => b'0' + n,
784 10..=15 => b'a' + (n - 10),
785 _ => b'0',
786 }
787 }
788
789 #[derive(Clone, Copy)]
796 pub struct JsonValueRef<'a> {
797 tagged: &'a [u8],
798 offset: usize,
799 }
800
801 impl<'a> JsonValueRef<'a> {
802 pub fn root(value: &'a JsonValue) -> Self {
804 Self {
805 tagged: value.tagged_bytes(),
806 offset: 0,
807 }
808 }
809
810 pub fn tag(&self) -> u8 {
812 self.tagged[self.offset]
813 }
814 pub fn is_null(&self) -> bool {
815 self.tag() == TAG_NULL
816 }
817 pub fn is_bool(&self) -> bool {
818 matches!(self.tag(), TAG_FALSE | TAG_TRUE)
819 }
820 pub fn is_number(&self) -> bool {
821 self.tag() == TAG_NUMBER
822 }
823 pub fn is_string(&self) -> bool {
824 self.tag() == TAG_STRING
825 }
826 pub fn is_array(&self) -> bool {
827 self.tag() == TAG_ARRAY
828 }
829 pub fn is_object(&self) -> bool {
830 self.tag() == TAG_OBJECT
831 }
832
833 pub fn as_bool(&self) -> Option<bool> {
834 match self.tag() {
835 TAG_FALSE => Some(false),
836 TAG_TRUE => Some(true),
837 _ => None,
838 }
839 }
840
841 pub fn as_str(&self) -> Option<&'a [u8]> {
843 if !self.is_string() {
844 return None;
845 }
846 let mut p = self.offset + 1;
847 let len = read_u32_be(self.tagged, &mut p).ok()? as usize;
848 Some(&self.tagged[p..p + len])
849 }
850
851 pub fn as_number_str(&self) -> Option<&'a [u8]> {
853 if !self.is_number() {
854 return None;
855 }
856 let mut p = self.offset + 1;
857 let len = read_u32_be(self.tagged, &mut p).ok()? as usize;
858 Some(&self.tagged[p..p + len])
859 }
860
861 pub fn get(&self, key: &[u8]) -> Option<JsonValueRef<'a>> {
863 let mut iter = self.iter_object()?;
864 iter.find_map(|(k, v)| if k == key { Some(v) } else { None })
865 }
866
867 pub fn iter_object(&self) -> Option<ObjectIter<'a>> {
870 if !self.is_object() {
871 return None;
872 }
873 let mut p = self.offset + 1;
874 let count = read_u32_be(self.tagged, &mut p).ok()? as usize;
875 Some(ObjectIter {
876 tagged: self.tagged,
877 pos: p,
878 remaining: count,
879 })
880 }
881
882 pub fn iter_array(&self) -> Option<ArrayIter<'a>> {
884 if !self.is_array() {
885 return None;
886 }
887 let mut p = self.offset + 1;
888 let count = read_u32_be(self.tagged, &mut p).ok()? as usize;
889 Some(ArrayIter {
890 tagged: self.tagged,
891 pos: p,
892 remaining: count,
893 })
894 }
895 }
896
897 pub struct ObjectIter<'a> {
899 tagged: &'a [u8],
900 pos: usize,
901 remaining: usize,
902 }
903
904 impl<'a> Iterator for ObjectIter<'a> {
905 type Item = (&'a [u8], JsonValueRef<'a>);
906 fn next(&mut self) -> Option<Self::Item> {
907 if self.remaining == 0 {
908 return None;
909 }
910 let key_len = read_u32_be(self.tagged, &mut self.pos).ok()? as usize;
911 let key_end = self.pos + key_len;
912 let key = &self.tagged[self.pos..key_end];
913 self.pos = key_end;
914 let value_offset = self.pos;
915 self.pos = skip_to_end(self.tagged, self.pos).ok()?;
916 self.remaining -= 1;
917 Some((
918 key,
919 JsonValueRef {
920 tagged: self.tagged,
921 offset: value_offset,
922 },
923 ))
924 }
925 }
926
927 pub struct ArrayIter<'a> {
929 tagged: &'a [u8],
930 pos: usize,
931 remaining: usize,
932 }
933
934 impl<'a> Iterator for ArrayIter<'a> {
935 type Item = JsonValueRef<'a>;
936 fn next(&mut self) -> Option<Self::Item> {
937 if self.remaining == 0 {
938 return None;
939 }
940 let value_offset = self.pos;
941 self.pos = skip_to_end(self.tagged, self.pos).ok()?;
942 self.remaining -= 1;
943 Some(JsonValueRef {
944 tagged: self.tagged,
945 offset: value_offset,
946 })
947 }
948 }
949
950 fn skip_to_end(tagged: &[u8], pos: usize) -> Result<usize, ShapeViolation> {
951 let mut p = pos;
952 let tag = read_byte(tagged, &mut p)?;
953 match tag {
954 TAG_NULL | TAG_FALSE | TAG_TRUE => Ok(p),
955 TAG_NUMBER | TAG_STRING => {
956 let len = read_u32_be(tagged, &mut p)? as usize;
957 Ok(p + len)
958 }
959 TAG_ARRAY => {
960 let count = read_u32_be(tagged, &mut p)? as usize;
961 for _ in 0..count {
962 p = skip_to_end(tagged, p)?;
963 }
964 Ok(p)
965 }
966 TAG_OBJECT => {
967 let count = read_u32_be(tagged, &mut p)? as usize;
968 for _ in 0..count {
969 let key_len = read_u32_be(tagged, &mut p)? as usize;
970 p += key_len;
971 p = skip_to_end(tagged, p)?;
972 }
973 Ok(p)
974 }
975 _ => Err(INVALID_JSON_VIOLATION),
976 }
977 }
978
979 #[cfg(test)]
980 mod tests {
981 use super::*;
982
983 #[test]
984 fn parses_simple_object() {
985 let v = JsonValue::parse(br#"{"foo":"bar"}"#).expect("valid");
986 assert_eq!(v.bytes[0], TAG_OBJECT);
987 }
988
989 #[test]
990 fn rejects_invalid_json() {
991 let err = JsonValue::parse(b"not json").expect_err("must reject");
992 assert_eq!(err.shape_iri, INVALID_JSON_VIOLATION.shape_iri);
993 }
994
995 #[test]
996 fn rejects_overdeep_recursion() {
997 use alloc::string::String;
998 let mut s = String::new();
999 for _ in 0..(MAX_JSON_DEPTH + 2) {
1000 s.push('[');
1001 }
1002 for _ in 0..(MAX_JSON_DEPTH + 2) {
1003 s.push(']');
1004 }
1005 let err = JsonValue::parse(s.as_bytes()).expect_err("must reject");
1006 assert_eq!(err.constraint_iri, DEPTH_BOUND_VIOLATION.constraint_iri);
1007 }
1008
1009 #[test]
1010 fn accepts_unbounded_string_width() {
1011 use alloc::format;
1012 use alloc::string::String;
1013 let big: String = "a".repeat(200_000);
1014 let raw = format!("\"{big}\"");
1015 let canon = canonicalize(raw.as_bytes()).expect("unbounded string admitted");
1016 assert_eq!(canon.len(), big.len() + 2);
1017 }
1018
1019 const CANONICAL_FIXTURES: &[(&[u8], &[u8])] = &[
1020 (br#"{"foo":"bar"}"#, br#"{"foo":"bar"}"#),
1021 (br#"{"b": 1, "a": 2}"#, br#"{"a":2,"b":1}"#),
1022 (
1023 br#"{"nested": {"deep": {"value": "found"}}}"#,
1024 br#"{"nested":{"deep":{"value":"found"}}}"#,
1025 ),
1026 (
1027 br#"{"int": 42, "bool": true, "null_val": null}"#,
1028 br#"{"bool":true,"int":42,"null_val":null}"#,
1029 ),
1030 (b"[1, 2, 3]", b"[1,2,3]"),
1031 (br#"["a", "b", "c"]"#, br#"["a","b","c"]"#),
1032 ];
1033
1034 #[test]
1035 fn canonicalizer_matches_reference_for_inline_fixtures() {
1036 for (raw, expected) in CANONICAL_FIXTURES {
1037 let canon = canonicalize(raw).expect("valid");
1038 assert_eq!(canon, *expected, "raw={raw:?}");
1039 }
1040 }
1041
1042 #[test]
1043 fn canonicalizer_collapses_unicode_decomposed_to_composed() {
1044 let decomposed = "{\"name\": \"cafe\u{0301}\"}".as_bytes();
1045 let composed = "{\"name\":\"caf\u{00E9}\"}".as_bytes();
1046 assert_eq!(
1047 canonicalize(decomposed).expect("valid"),
1048 canonicalize(composed).expect("valid")
1049 );
1050 }
1051
1052 #[test]
1053 fn canonicalize_is_idempotent_on_its_own_output() {
1054 for (raw, _expected) in CANONICAL_FIXTURES {
1055 let once = canonicalize(raw).expect("valid");
1056 let twice = canonicalize(&once).expect("re-canonicalises");
1057 assert_eq!(once, twice, "idempotence broken for {raw:?}");
1058 }
1059 }
1060 }
1061}