1use core::fmt;
16
17use crate::number::NumberValue;
18use crate::owned::OwnedDataValue;
19use crate::value::DataValue;
20
21pub(crate) trait JsonSink {
31 type Error;
32 fn write_bytes(&mut self, b: &[u8]) -> Result<(), Self::Error>;
33 fn write_byte(&mut self, b: u8) -> Result<(), Self::Error>;
34}
35
36impl JsonSink for Vec<u8> {
37 type Error = core::convert::Infallible;
38 #[inline]
39 fn write_bytes(&mut self, b: &[u8]) -> Result<(), Self::Error> {
40 self.extend_from_slice(b);
41 Ok(())
42 }
43 #[inline]
44 fn write_byte(&mut self, b: u8) -> Result<(), Self::Error> {
45 self.push(b);
46 Ok(())
47 }
48}
49
50impl JsonSink for bumpalo::collections::Vec<'_, u8> {
51 type Error = core::convert::Infallible;
52 #[inline]
53 fn write_bytes(&mut self, b: &[u8]) -> Result<(), Self::Error> {
54 self.reserve(b.len());
59 let len = self.len();
60 unsafe {
64 core::ptr::copy_nonoverlapping(b.as_ptr(), self.as_mut_ptr().add(len), b.len());
65 self.set_len(len + b.len());
66 }
67 Ok(())
68 }
69 #[inline]
70 fn write_byte(&mut self, b: u8) -> Result<(), Self::Error> {
71 self.push(b);
72 Ok(())
73 }
74}
75
76#[inline]
83fn into_arena_str<'b>(mut out: bumpalo::collections::Vec<'b, u8>) -> &'b str {
84 out.shrink_to_fit();
89 unsafe { core::str::from_utf8_unchecked(out.into_bump_slice()) }
90}
91
92const FMT_STAGING: usize = 128;
96
97struct FormatterSink<'a, 'b> {
98 f: &'a mut fmt::Formatter<'b>,
99 buf: [u8; FMT_STAGING],
100 len: usize,
101}
102
103impl<'a, 'b> FormatterSink<'a, 'b> {
104 fn new(f: &'a mut fmt::Formatter<'b>) -> Self {
105 FormatterSink {
106 f,
107 buf: [0; FMT_STAGING],
108 len: 0,
109 }
110 }
111
112 fn flush(&mut self) -> fmt::Result {
113 if self.len > 0 {
114 let s = unsafe { core::str::from_utf8_unchecked(&self.buf[..self.len]) };
118 self.f.write_str(s)?;
119 self.len = 0;
120 }
121 Ok(())
122 }
123}
124
125impl<'a, 'b> JsonSink for FormatterSink<'a, 'b> {
126 type Error = fmt::Error;
127 #[inline]
128 fn write_bytes(&mut self, b: &[u8]) -> Result<(), Self::Error> {
129 if self.len + b.len() > FMT_STAGING {
130 self.flush()?;
131 if b.len() >= FMT_STAGING {
132 let s = unsafe { core::str::from_utf8_unchecked(b) };
134 return self.f.write_str(s);
135 }
136 }
137 self.buf[self.len..self.len + b.len()].copy_from_slice(b);
138 self.len += b.len();
139 Ok(())
140 }
141 #[inline]
142 fn write_byte(&mut self, b: u8) -> Result<(), Self::Error> {
143 debug_assert!(b.is_ascii());
144 if self.len == FMT_STAGING {
145 self.flush()?;
146 }
147 self.buf[self.len] = b;
148 self.len += 1;
149 Ok(())
150 }
151}
152
153#[inline]
154fn write_escaped_str<S: JsonSink>(out: &mut S, s: &str) -> Result<(), S::Error> {
155 out.write_byte(b'"')?;
156 let bytes = s.as_bytes();
157 let mut run_start = 0;
158
159 while let Some(off) = crate::simd::find_string_terminator(&bytes[run_start..]) {
164 let hit = run_start + off;
165 if hit > run_start {
166 out.write_bytes(&bytes[run_start..hit])?;
167 }
168 write_escape_byte(out, bytes[hit])?;
169 run_start = hit + 1;
170 }
171 if run_start < bytes.len() {
172 out.write_bytes(&bytes[run_start..])?;
173 }
174 out.write_byte(b'"')
175}
176
177#[cfg(feature = "datetime")]
182fn write_datetime<S: JsonSink>(
183 out: &mut S,
184 d: &crate::datetime::DataDateTime,
185) -> Result<(), S::Error> {
186 match d.iso_secs_buf() {
187 Some(buf) => {
188 out.write_byte(b'"')?;
189 out.write_bytes(&buf)?;
190 out.write_byte(b'"')
191 }
192 None => write_escaped_str(out, &d.to_iso_string()),
193 }
194}
195
196#[cfg(feature = "datetime")]
199fn write_duration<S: JsonSink>(
200 out: &mut S,
201 d: &crate::datetime::DataDuration,
202) -> Result<(), S::Error> {
203 let (days, hours, minutes, seconds) = d.dhms();
204 let mut b = itoa::Buffer::new();
205 out.write_byte(b'"')?;
206 out.write_bytes(b.format(days).as_bytes())?;
207 out.write_bytes(b"d:")?;
208 out.write_bytes(b.format(hours).as_bytes())?;
209 out.write_bytes(b"h:")?;
210 out.write_bytes(b.format(minutes).as_bytes())?;
211 out.write_bytes(b"m:")?;
212 out.write_bytes(b.format(seconds).as_bytes())?;
213 out.write_bytes(b"s\"")
214}
215
216#[cfg(feature = "tensor")]
221fn write_tensor<S: JsonSink>(
222 out: &mut S,
223 t: crate::tensor::DataTensor<'_>,
224) -> Result<(), S::Error> {
225 out.write_bytes(b"{\"")?;
226 out.write_bytes(crate::tensor::DataTensor::JSON_TAG.as_bytes())?;
227 out.write_bytes(b"\":{\"dtype\":\"")?;
228 out.write_bytes(t.dtype().name().as_bytes())?;
229 out.write_bytes(b"\",\"shape\":[")?;
230 let mut buf = itoa::Buffer::new();
231 for (i, d) in t.shape().iter().enumerate() {
232 if i > 0 {
233 out.write_byte(b',')?;
234 }
235 out.write_bytes(buf.format(*d).as_bytes())?;
236 }
237 out.write_bytes(b"],\"data\":\"")?;
238 crate::base64::encode_into(out, t.data())?;
239 out.write_bytes(b"\"}}")
240}
241
242#[cfg(feature = "tensor")]
245fn write_tensor_pretty<S: JsonSink>(
246 out: &mut S,
247 t: crate::tensor::DataTensor<'_>,
248 depth: usize,
249) -> Result<(), S::Error> {
250 out.write_bytes(b"{\n")?;
251 write_indent(out, depth + 1)?;
252 out.write_byte(b'"')?;
253 out.write_bytes(crate::tensor::DataTensor::JSON_TAG.as_bytes())?;
254 out.write_bytes(b"\": {\n")?;
255 write_indent(out, depth + 2)?;
256 out.write_bytes(b"\"dtype\": \"")?;
257 out.write_bytes(t.dtype().name().as_bytes())?;
258 out.write_bytes(b"\",\n")?;
259 write_indent(out, depth + 2)?;
260 out.write_bytes(b"\"shape\": [")?;
261 if !t.shape().is_empty() {
262 let mut buf = itoa::Buffer::new();
263 for (i, d) in t.shape().iter().enumerate() {
264 if i > 0 {
265 out.write_byte(b',')?;
266 }
267 out.write_byte(b'\n')?;
268 write_indent(out, depth + 3)?;
269 out.write_bytes(buf.format(*d).as_bytes())?;
270 }
271 out.write_byte(b'\n')?;
272 write_indent(out, depth + 2)?;
273 }
274 out.write_bytes(b"],\n")?;
275 write_indent(out, depth + 2)?;
276 out.write_bytes(b"\"data\": \"")?;
277 crate::base64::encode_into(out, t.data())?;
278 out.write_bytes(b"\"\n")?;
279 write_indent(out, depth + 1)?;
280 out.write_bytes(b"}\n")?;
281 write_indent(out, depth)?;
282 out.write_byte(b'}')
283}
284
285#[inline]
286fn write_escape_byte<S: JsonSink>(out: &mut S, b: u8) -> Result<(), S::Error> {
287 match b {
288 b'"' => out.write_bytes(b"\\\""),
289 b'\\' => out.write_bytes(b"\\\\"),
290 b'\n' => out.write_bytes(b"\\n"),
291 b'\r' => out.write_bytes(b"\\r"),
292 b'\t' => out.write_bytes(b"\\t"),
293 0x08 => out.write_bytes(b"\\b"),
294 0x0C => out.write_bytes(b"\\f"),
295 c => {
296 const HEX: &[u8; 16] = b"0123456789abcdef";
299 out.write_bytes(b"\\u00")?;
300 out.write_byte(HEX[((c >> 4) & 0x0F) as usize])?;
301 out.write_byte(HEX[(c & 0x0F) as usize])
302 }
303 }
304}
305
306#[inline]
307fn write_number<S: JsonSink>(out: &mut S, n: NumberValue) -> Result<(), S::Error> {
308 match n {
309 NumberValue::Integer(i) => {
310 let mut buf = itoa::Buffer::new();
311 out.write_bytes(buf.format(i).as_bytes())
312 }
313 NumberValue::Float(f) => {
314 if !f.is_finite() {
315 return out.write_bytes(b"null");
318 }
319 let mut buf = ryu::Buffer::new();
320 out.write_bytes(buf.format_finite(f).as_bytes())
321 }
322 }
323}
324
325fn write_data_value<S: JsonSink>(out: &mut S, v: &DataValue<'_>) -> Result<(), S::Error> {
328 match *v {
329 DataValue::Null => out.write_bytes(b"null"),
330 DataValue::Bool(true) => out.write_bytes(b"true"),
331 DataValue::Bool(false) => out.write_bytes(b"false"),
332 DataValue::Number(n) => write_number(out, n),
333 DataValue::String(s) => write_escaped_str(out, s),
334 DataValue::Array(items) => {
335 out.write_byte(b'[')?;
336 let mut first = true;
337 for item in items {
338 if !first {
339 out.write_byte(b',')?;
340 }
341 first = false;
342 write_data_value(out, item)?;
343 }
344 out.write_byte(b']')
345 }
346 DataValue::Object(pairs) => {
347 out.write_byte(b'{')?;
348 let mut first = true;
349 for (k, v) in pairs {
350 if !first {
351 out.write_byte(b',')?;
352 }
353 first = false;
354 write_escaped_str(out, k)?;
355 out.write_byte(b':')?;
356 write_data_value(out, v)?;
357 }
358 out.write_byte(b'}')
359 }
360 #[cfg(feature = "datetime")]
361 DataValue::DateTime(d) => write_datetime(out, &d),
362 #[cfg(feature = "datetime")]
363 DataValue::Duration(d) => write_duration(out, &d),
364 #[cfg(feature = "tensor")]
365 DataValue::Tensor(t) => write_tensor(out, *t),
366 }
367}
368
369fn write_owned_value<S: JsonSink>(out: &mut S, v: &OwnedDataValue) -> Result<(), S::Error> {
370 match v {
371 OwnedDataValue::Null => out.write_bytes(b"null"),
372 OwnedDataValue::Bool(true) => out.write_bytes(b"true"),
373 OwnedDataValue::Bool(false) => out.write_bytes(b"false"),
374 OwnedDataValue::Number(n) => write_number(out, *n),
375 OwnedDataValue::String(s) => write_escaped_str(out, s),
376 OwnedDataValue::Array(items) => {
377 out.write_byte(b'[')?;
378 let mut first = true;
379 for item in items {
380 if !first {
381 out.write_byte(b',')?;
382 }
383 first = false;
384 write_owned_value(out, item)?;
385 }
386 out.write_byte(b']')
387 }
388 OwnedDataValue::Object(pairs) => {
389 out.write_byte(b'{')?;
390 let mut first = true;
391 for (k, v) in pairs {
392 if !first {
393 out.write_byte(b',')?;
394 }
395 first = false;
396 write_escaped_str(out, k)?;
397 out.write_byte(b':')?;
398 write_owned_value(out, v)?;
399 }
400 out.write_byte(b'}')
401 }
402 #[cfg(feature = "datetime")]
403 OwnedDataValue::DateTime(d) => write_datetime(out, d),
404 #[cfg(feature = "datetime")]
405 OwnedDataValue::Duration(d) => write_duration(out, d),
406 #[cfg(feature = "tensor")]
407 OwnedDataValue::Tensor(t) => write_tensor(out, t.view()),
408 }
409}
410
411#[inline]
414fn write_indent<S: JsonSink>(out: &mut S, depth: usize) -> Result<(), S::Error> {
415 const SPACES: &[u8; 64] = b" ";
418 let mut remaining = depth * 2;
419 while remaining > 0 {
420 let chunk = remaining.min(SPACES.len());
421 out.write_bytes(&SPACES[..chunk])?;
422 remaining -= chunk;
423 }
424 Ok(())
425}
426
427fn write_data_value_pretty<S: JsonSink>(
428 out: &mut S,
429 v: &DataValue<'_>,
430 depth: usize,
431) -> Result<(), S::Error> {
432 match *v {
433 DataValue::Null => out.write_bytes(b"null"),
434 DataValue::Bool(true) => out.write_bytes(b"true"),
435 DataValue::Bool(false) => out.write_bytes(b"false"),
436 DataValue::Number(n) => write_number(out, n),
437 DataValue::String(s) => write_escaped_str(out, s),
438 DataValue::Array(items) => {
439 if items.is_empty() {
440 return out.write_bytes(b"[]");
441 }
442 out.write_byte(b'[')?;
443 for (i, item) in items.iter().enumerate() {
444 if i > 0 {
445 out.write_byte(b',')?;
446 }
447 out.write_byte(b'\n')?;
448 write_indent(out, depth + 1)?;
449 write_data_value_pretty(out, item, depth + 1)?;
450 }
451 out.write_byte(b'\n')?;
452 write_indent(out, depth)?;
453 out.write_byte(b']')
454 }
455 DataValue::Object(pairs) => {
456 if pairs.is_empty() {
457 return out.write_bytes(b"{}");
458 }
459 out.write_byte(b'{')?;
460 for (i, (k, v)) in pairs.iter().enumerate() {
461 if i > 0 {
462 out.write_byte(b',')?;
463 }
464 out.write_byte(b'\n')?;
465 write_indent(out, depth + 1)?;
466 write_escaped_str(out, k)?;
467 out.write_bytes(b": ")?;
468 write_data_value_pretty(out, v, depth + 1)?;
469 }
470 out.write_byte(b'\n')?;
471 write_indent(out, depth)?;
472 out.write_byte(b'}')
473 }
474 #[cfg(feature = "datetime")]
475 DataValue::DateTime(d) => write_datetime(out, &d),
476 #[cfg(feature = "datetime")]
477 DataValue::Duration(d) => write_duration(out, &d),
478 #[cfg(feature = "tensor")]
479 DataValue::Tensor(t) => write_tensor_pretty(out, *t, depth),
480 }
481}
482
483fn write_owned_value_pretty<S: JsonSink>(
484 out: &mut S,
485 v: &OwnedDataValue,
486 depth: usize,
487) -> Result<(), S::Error> {
488 match v {
489 OwnedDataValue::Null => out.write_bytes(b"null"),
490 OwnedDataValue::Bool(true) => out.write_bytes(b"true"),
491 OwnedDataValue::Bool(false) => out.write_bytes(b"false"),
492 OwnedDataValue::Number(n) => write_number(out, *n),
493 OwnedDataValue::String(s) => write_escaped_str(out, s),
494 OwnedDataValue::Array(items) => {
495 if items.is_empty() {
496 return out.write_bytes(b"[]");
497 }
498 out.write_byte(b'[')?;
499 for (i, item) in items.iter().enumerate() {
500 if i > 0 {
501 out.write_byte(b',')?;
502 }
503 out.write_byte(b'\n')?;
504 write_indent(out, depth + 1)?;
505 write_owned_value_pretty(out, item, depth + 1)?;
506 }
507 out.write_byte(b'\n')?;
508 write_indent(out, depth)?;
509 out.write_byte(b']')
510 }
511 OwnedDataValue::Object(pairs) => {
512 if pairs.is_empty() {
513 return out.write_bytes(b"{}");
514 }
515 out.write_byte(b'{')?;
516 for (i, (k, v)) in pairs.iter().enumerate() {
517 if i > 0 {
518 out.write_byte(b',')?;
519 }
520 out.write_byte(b'\n')?;
521 write_indent(out, depth + 1)?;
522 write_escaped_str(out, k)?;
523 out.write_bytes(b": ")?;
524 write_owned_value_pretty(out, v, depth + 1)?;
525 }
526 out.write_byte(b'\n')?;
527 write_indent(out, depth)?;
528 out.write_byte(b'}')
529 }
530 #[cfg(feature = "datetime")]
531 OwnedDataValue::DateTime(d) => write_datetime(out, d),
532 #[cfg(feature = "datetime")]
533 OwnedDataValue::Duration(d) => write_duration(out, d),
534 #[cfg(feature = "tensor")]
535 OwnedDataValue::Tensor(t) => write_tensor_pretty(out, t.view(), depth),
536 }
537}
538
539impl DataValue<'_> {
542 pub fn write_json_into(&self, out: &mut Vec<u8>) {
547 let _ = write_data_value(out, self);
548 }
549
550 pub fn pretty(&self) -> Pretty<'_, DataValue<'_>> {
562 Pretty(self)
563 }
564
565 pub fn write_json_pretty_into(&self, out: &mut Vec<u8>) {
567 let _ = write_data_value_pretty(out, self, 0);
568 }
569
570 pub fn to_json_str_in<'b>(&self, arena: &'b bumpalo::Bump) -> &'b str {
585 let mut out = bumpalo::collections::Vec::new_in(arena);
586 let _ = write_data_value(&mut out, self);
587 into_arena_str(out)
588 }
589
590 pub fn to_json_pretty_str_in<'b>(&self, arena: &'b bumpalo::Bump) -> &'b str {
592 let mut out = bumpalo::collections::Vec::new_in(arena);
593 let _ = write_data_value_pretty(&mut out, self, 0);
594 into_arena_str(out)
595 }
596}
597
598impl OwnedDataValue {
599 pub fn write_json_into(&self, out: &mut Vec<u8>) {
602 let _ = write_owned_value(out, self);
603 }
604
605 pub fn pretty(&self) -> Pretty<'_, OwnedDataValue> {
614 Pretty(self)
615 }
616
617 pub fn write_json_pretty_into(&self, out: &mut Vec<u8>) {
619 let _ = write_owned_value_pretty(out, self, 0);
620 }
621
622 pub fn to_json_str_in<'b>(&self, arena: &'b bumpalo::Bump) -> &'b str {
625 let mut out = bumpalo::collections::Vec::new_in(arena);
626 let _ = write_owned_value(&mut out, self);
627 into_arena_str(out)
628 }
629
630 pub fn to_json_pretty_str_in<'b>(&self, arena: &'b bumpalo::Bump) -> &'b str {
632 let mut out = bumpalo::collections::Vec::new_in(arena);
633 let _ = write_owned_value_pretty(&mut out, self, 0);
634 into_arena_str(out)
635 }
636}
637
638pub struct Pretty<'b, T: ?Sized>(&'b T);
643
644impl fmt::Display for DataValue<'_> {
645 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
656 let mut sink = FormatterSink::new(f);
657 write_data_value(&mut sink, self)?;
658 sink.flush()
659 }
660}
661
662impl fmt::Display for OwnedDataValue {
663 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
672 let mut sink = FormatterSink::new(f);
673 write_owned_value(&mut sink, self)?;
674 sink.flush()
675 }
676}
677
678impl fmt::Display for Pretty<'_, DataValue<'_>> {
679 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
680 let mut sink = FormatterSink::new(f);
681 write_data_value_pretty(&mut sink, self.0, 0)?;
682 sink.flush()
683 }
684}
685
686impl fmt::Display for Pretty<'_, OwnedDataValue> {
687 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
688 let mut sink = FormatterSink::new(f);
689 write_owned_value_pretty(&mut sink, self.0, 0)?;
690 sink.flush()
691 }
692}
693
694#[cfg(test)]
695mod tests {
696 use super::*;
697 use bumpalo::Bump;
698
699 fn round_trip(s: &str) -> String {
700 let arena = Bump::new();
701 let v = DataValue::from_str(s, &arena).unwrap();
702 v.to_string()
703 }
704
705 #[test]
706 fn primitives() {
707 assert_eq!(round_trip("null"), "null");
708 assert_eq!(round_trip("true"), "true");
709 assert_eq!(round_trip("false"), "false");
710 assert_eq!(round_trip("42"), "42");
711 assert_eq!(round_trip("-7"), "-7");
712 assert_eq!(round_trip("3.5"), "3.5");
713 }
714
715 #[test]
716 fn strings_with_escapes() {
717 assert_eq!(round_trip(r#""hello""#), r#""hello""#);
718 assert_eq!(round_trip(r#""a\nb""#), r#""a\nb""#);
719 assert_eq!(round_trip(r#""a\\b""#), r#""a\\b""#);
720 assert_eq!(round_trip(r#""a\"b""#), r#""a\"b""#);
721 assert_eq!(round_trip(r#""café""#), r#""café""#);
723 }
724
725 #[test]
726 fn control_bytes_render_as_unicode_escapes() {
727 let arena = Bump::new();
728 let v = DataValue::from_str("\"\\u0001\"", &arena).unwrap();
729 assert_eq!(v.to_string(), "\"\\u0001\"");
730 }
731
732 #[test]
733 fn nested_round_trip_matches_serde_json() {
734 let input = r#"{"a":[1,2,{"b":"hi\n","c":null,"d":true}],"e":-3.5,"f":[],"g":{}}"#;
735 let arena = Bump::new();
736 let v = DataValue::from_str(input, &arena).unwrap();
737 let ours = v.to_string();
738 let serde: serde_json::Value = serde_json::from_str(input).unwrap();
739 let theirs = serde_json::to_string(&serde).unwrap();
740 assert_eq!(ours, theirs);
741 }
742
743 #[test]
744 fn long_string_swar_path() {
745 let arena = Bump::new();
746 let s = format!("\"{}\"", "x".repeat(200));
747 let v = DataValue::from_str(&s, &arena).unwrap();
748 assert_eq!(v.to_string(), s);
749 }
750
751 fn assert_display_matches_vec(input: &str) {
757 let arena = Bump::new();
758 let v = DataValue::from_str(input, &arena).unwrap();
759 let mut buf = Vec::new();
760 v.write_json_into(&mut buf);
761 assert_eq!(v.to_string().into_bytes(), buf, "compact mismatch");
762
763 let mut pretty_buf = Vec::new();
764 v.write_json_pretty_into(&mut pretty_buf);
765 assert_eq!(
766 v.pretty().to_string().into_bytes(),
767 pretty_buf,
768 "pretty mismatch"
769 );
770 }
771
772 #[cfg(feature = "datetime")]
773 #[test]
774 fn datetime_emit_matches_display_wire_format() {
775 use crate::datetime::DataDateTime;
776
777 let dt = DataDateTime::parse("2024-01-15T12:30:45Z").unwrap();
778 let later = DataDateTime::parse("2024-01-18T16:35:51Z").unwrap();
779 let du = later.diff(&dt);
780
781 for v in [DataValue::DateTime(dt), DataValue::Duration(du)] {
782 let display = v.to_string();
783 let mut buf = Vec::new();
784 v.write_json_into(&mut buf);
785 assert_eq!(display.into_bytes(), buf);
786
787 let owned = v.to_owned();
788 assert_eq!(owned.to_string(), v.to_string());
789 }
790 assert_eq!(
791 DataValue::DateTime(dt).to_string(),
792 "\"2024-01-15T12:30:45Z\""
793 );
794 assert_eq!(DataValue::Duration(du).to_string(), "\"3d:4h:5m:6s\"");
795 }
796
797 #[test]
798 fn to_json_str_in_matches_to_string() {
799 let inputs = [
800 "null",
801 "[]",
802 r#"{"a":[1,2.5,"hi\n",null,true],"b":{"c":"é€"},"e":-0.125}"#,
803 ];
804 let arena = Bump::new();
805 let out_arena = Bump::new();
807 for input in inputs {
808 let v = DataValue::from_str(input, &arena).unwrap();
809 assert_eq!(v.to_json_str_in(&out_arena), v.to_string());
810 assert_eq!(v.to_json_pretty_str_in(&out_arena), v.pretty().to_string());
811
812 let owned = v.to_owned();
813 assert_eq!(owned.to_json_str_in(&out_arena), owned.to_string());
814 assert_eq!(
815 owned.to_json_pretty_str_in(&out_arena),
816 owned.pretty().to_string()
817 );
818 }
819 let long = format!("\"{}\"", "x".repeat(5000));
821 let v = DataValue::from_str(&long, &arena).unwrap();
822 assert_eq!(v.to_json_str_in(&out_arena), long);
823 }
824
825 #[test]
826 fn display_matches_vec_across_staging_boundaries() {
827 for n in [1, 7, 126, 127, 128, 129, 200, 255, 256, 257, 1000] {
830 assert_display_matches_vec(&format!("\"{}\"", "x".repeat(n)));
831 }
832 for n in [60, 63, 64, 65, 100] {
835 assert_display_matches_vec(&format!("\"{}\"", "é".repeat(n)));
836 assert_display_matches_vec(&format!("\"{}\"", "€".repeat(n)));
837 }
838 assert_display_matches_vec(&format!("\"{}\"", r#"a\n"#.repeat(100)));
840 assert_display_matches_vec(
842 r#"{"a":[1,2.5,"hi\n",null,true],"b":{"c":"é€","d":[[],{}]},"e":-0.125}"#,
843 );
844 }
845
846 #[test]
847 fn non_finite_floats_render_as_null() {
848 let v = DataValue::from_f64(f64::NAN);
849 assert_eq!(v.to_string(), "null");
850 let v = DataValue::from_f64(f64::INFINITY);
851 assert_eq!(v.to_string(), "null");
852 }
853
854 #[test]
855 fn owned_round_trip() {
856 let v: OwnedDataValue = r#"{"name":"alice","age":30}"#.parse().unwrap();
857 let serde: serde_json::Value = serde_json::from_str(&v.to_string()).unwrap();
858 assert_eq!(serde["name"], "alice");
859 assert_eq!(serde["age"], 30);
860 }
861
862 #[test]
863 fn write_json_into_buffer() {
864 let arena = Bump::new();
865 let v = DataValue::from_str(r#"[1,2,3]"#, &arena).unwrap();
866 let mut buf = Vec::new();
867 v.write_json_into(&mut buf);
868 assert_eq!(buf, b"[1,2,3]");
869 }
870
871 #[test]
872 fn pretty_matches_serde_json_pretty() {
873 let input = r#"{"a":[1,2,{"b":"hi","c":null}],"e":-3.5,"f":[],"g":{}}"#;
874 let arena = Bump::new();
875 let v = DataValue::from_str(input, &arena).unwrap();
876 let ours = v.pretty().to_string();
877 let serde: serde_json::Value = serde_json::from_str(input).unwrap();
878 let theirs = serde_json::to_string_pretty(&serde).unwrap();
879 assert_eq!(ours, theirs);
880 }
881
882 #[test]
883 fn pretty_owned_matches_serde_json_pretty() {
884 let input = r#"{"a":[1,2,{"b":"hi","c":null}],"e":-3.5,"f":[],"g":{}}"#;
885 let v: OwnedDataValue = input.parse().unwrap();
886 let serde: serde_json::Value = serde_json::from_str(input).unwrap();
887 assert_eq!(
888 v.pretty().to_string(),
889 serde_json::to_string_pretty(&serde).unwrap()
890 );
891 }
892
893 #[test]
894 fn pretty_empty_collections_inline() {
895 let arena = Bump::new();
896 let v = DataValue::from_str(r#"{"a":[],"b":{}}"#, &arena).unwrap();
897 assert_eq!(v.pretty().to_string(), "{\n \"a\": [],\n \"b\": {}\n}");
898 }
899
900 #[test]
901 fn pretty_deep_indent_beyond_64_spaces() {
902 let arena = Bump::new();
905 let mut s = String::new();
906 for _ in 0..35 {
907 s.push('[');
908 }
909 s.push('1');
910 for _ in 0..35 {
911 s.push(']');
912 }
913 let v = DataValue::from_str(&s, &arena).unwrap();
914 let ours = v.pretty().to_string();
915 let serde: serde_json::Value = serde_json::from_str(&s).unwrap();
916 assert_eq!(ours, serde_json::to_string_pretty(&serde).unwrap());
917 }
918
919 #[cfg(feature = "tensor")]
920 #[test]
921 fn tensor_compact_wire_format() {
922 use crate::tensor::{DType, DataTensor, OwnedDataTensor};
923 let arena = Bump::new();
924 let t =
925 DataTensor::from_slice_in(&[2, 3], &[1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0], &arena).unwrap();
926 let v = DataValue::tensor_in(t, &arena);
927 let expected =
928 r#"{"tensor":{"dtype":"f32","shape":[2,3],"data":"AACAPwAAAEAAAEBAAACAQAAAoEAAAMBA"}}"#;
929 assert_eq!(v.to_string(), expected);
930 assert_eq!(v.to_json_str_in(&arena), expected);
931 let mut buf = Vec::new();
932 v.write_json_into(&mut buf);
933 assert_eq!(buf, expected.as_bytes());
934 assert_eq!(v.to_owned().to_string(), expected);
935
936 let doc = arena.alloc_slice_copy(&[("t", v), ("n", DataValue::from_i64(1))]);
938 assert_eq!(
939 DataValue::Object(doc).to_string(),
940 format!(r#"{{"t":{expected},"n":1}}"#)
941 );
942 let scalar = OwnedDataTensor::from_slice([], &[7u8]).unwrap();
943 assert_eq!(
944 OwnedDataValue::tensor(scalar).to_string(),
945 r#"{"tensor":{"dtype":"u8","shape":[],"data":"Bw=="}}"#
946 );
947 let empty = OwnedDataTensor::from_bytes(DType::I16, [0, 3], &[]).unwrap();
948 assert_eq!(
949 OwnedDataValue::tensor(empty).to_string(),
950 r#"{"tensor":{"dtype":"i16","shape":[0,3],"data":""}}"#
951 );
952 }
953
954 #[cfg(feature = "tensor")]
955 #[test]
956 fn tensor_pretty_wire_format() {
957 use crate::tensor::{DType, DataTensor, OwnedDataTensor};
958 let arena = Bump::new();
959 let t =
960 DataTensor::from_slice_in(&[2, 3], &[1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0], &arena).unwrap();
961 let v = DataValue::tensor_in(t, &arena);
962 let expected = "{\n \"tensor\": {\n \"dtype\": \"f32\",\n \"shape\": [\n 2,\n 3\n ],\n \"data\": \"AACAPwAAAEAAAEBAAACAQAAAoEAAAMBA\"\n }\n}";
963 assert_eq!(v.pretty().to_string(), expected);
964 assert_eq!(v.to_json_pretty_str_in(&arena), expected);
965 assert_eq!(v.to_owned().pretty().to_string(), expected);
966
967 let doc = arena.alloc_slice_copy(&[("t", v)]);
968 let nested = DataValue::Object(doc).pretty().to_string();
969 assert!(nested.starts_with("{\n \"t\": {\n \"tensor\": {\n \"dtype\": \"f32\","));
970 assert!(nested.ends_with(" }\n }\n}"));
971
972 let scalar = OwnedDataTensor::from_bytes(DType::U8, [], &[7]).unwrap();
973 assert_eq!(
974 OwnedDataValue::tensor(scalar).pretty().to_string(),
975 "{\n \"tensor\": {\n \"dtype\": \"u8\",\n \"shape\": [],\n \"data\": \"Bw==\"\n }\n}"
976 );
977 }
978
979 #[cfg(all(feature = "tensor", feature = "serde_json"))]
980 #[test]
981 fn tensor_pretty_matches_serde_json_pretty() {
982 use crate::tensor::DataTensor;
983 let arena = Bump::new();
984 for shape in [&[2usize, 3][..], &[6], &[], &[0, 6], &[1, 6, 1]] {
985 let elems: Vec<i32> = (0..shape.iter().product::<usize>() as i32).collect();
986 let t = DataTensor::from_slice_in(shape, &elems, &arena).unwrap();
987 let doc = arena.alloc_slice_copy(&[("t", DataValue::tensor_in(t, &arena))]);
988 let v = DataValue::Object(doc);
989 assert_eq!(
990 v.pretty().to_string(),
991 serde_json::to_string_pretty(&v).unwrap()
992 );
993 assert_eq!(v.to_string(), serde_json::to_string(&v).unwrap());
994 }
995 }
996
997 #[cfg(feature = "tensor")]
998 #[test]
999 fn tensor_display_matches_vec_across_chunk_boundaries() {
1000 use crate::tensor::DataTensor;
1001 let arena = Bump::new();
1002 let bytes: Vec<u8> = (0..5000).map(|i| (i % 251) as u8).collect();
1005 let t =
1006 DataTensor::from_bytes_in(crate::tensor::DType::U8, &[5000], &bytes, &arena).unwrap();
1007 let v = DataValue::tensor_in(t, &arena);
1008 let mut buf = Vec::new();
1009 v.write_json_into(&mut buf);
1010 assert_eq!(v.to_string().into_bytes(), buf);
1011 let mut pretty_buf = Vec::new();
1012 v.write_json_pretty_into(&mut pretty_buf);
1013 assert_eq!(v.pretty().to_string().into_bytes(), pretty_buf);
1014 }
1015}