jsonata/value/
serialize.rs

1// Acknowledgement:
2//
3// This is based on the JSON dumping code from [Maciej Hirsz's](https://github.com/maciejhirsz)
4// excellent [json crate](https://github.com/maciejhirsz/json-rust), and modified to work on
5// our custom internal value.
6//
7// The original code is licensed in the same way as this crate.
8
9use std::io::Write;
10
11use super::Value;
12use crate::Result;
13
14const QU: u8 = b'"';
15const BS: u8 = b'\\';
16const BB: u8 = b'b';
17const TT: u8 = b't';
18const NN: u8 = b'n';
19const FF: u8 = b'f';
20const RR: u8 = b'r';
21const UU: u8 = b'u';
22const __: u8 = 0;
23
24// Look up table for characters that need escaping in a product string
25static ESCAPED: [u8; 256] = [
26    // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
27    UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, // 0
28    UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, // 1
29    __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2
30    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3
31    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4
32    __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, // 5
33    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6
34    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7
35    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8
36    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9
37    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A
38    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B
39    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C
40    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D
41    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E
42    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F
43];
44
45pub trait Formatter {
46    fn write_min(&self, output: &mut Vec<u8>, slice: &[u8], min: u8);
47    fn new_line(&self, output: &mut Vec<u8>);
48    fn indent(&mut self);
49    fn dedent(&mut self);
50}
51
52pub struct DumpFormatter;
53
54impl Formatter for DumpFormatter {
55    #[inline(always)]
56    fn write_min(&self, output: &mut Vec<u8>, _: &[u8], min: u8) {
57        output.push(min);
58    }
59
60    #[inline(always)]
61    fn new_line(&self, _output: &mut Vec<u8>) {}
62
63    #[inline(always)]
64    fn indent(&mut self) {}
65
66    #[inline(always)]
67    fn dedent(&mut self) {}
68}
69
70pub struct PrettyFormatter {
71    dent: u16,
72    spaces: u16,
73}
74
75impl Default for PrettyFormatter {
76    fn default() -> Self {
77        Self { dent: 0, spaces: 2 }
78    }
79}
80
81impl Formatter for PrettyFormatter {
82    #[inline(always)]
83    fn write_min(&self, output: &mut Vec<u8>, slice: &[u8], _: u8) {
84        output.extend_from_slice(slice);
85    }
86
87    fn new_line(&self, output: &mut Vec<u8>) {
88        output.push(b'\n');
89        for _ in 0..(self.dent * self.spaces) {
90            output.push(b' ');
91        }
92    }
93
94    fn indent(&mut self) {
95        self.dent += 1;
96    }
97
98    fn dedent(&mut self) {
99        self.dent -= 1;
100    }
101}
102
103pub struct Serializer<T: Formatter> {
104    output: Vec<u8>,
105    formatter: T,
106    fail_on_invalid_numbers: bool,
107}
108
109impl<T: Formatter> Serializer<T> {
110    pub fn new(formatter: T, fail_on_invalid_numbers: bool) -> Self {
111        Serializer {
112            output: Vec::with_capacity(1024),
113            formatter,
114            fail_on_invalid_numbers,
115        }
116    }
117
118    pub fn serialize<'a>(mut self, value: &'a Value<'a>) -> Result<String> {
119        self.write_json(value)?;
120
121        // SAFETY: Original strings were unicode, numbers are all ASCII,
122        // therefore this is safe.
123        Ok(unsafe { String::from_utf8_unchecked(self.output) })
124    }
125
126    #[inline(always)]
127    fn write(&mut self, slice: &[u8]) {
128        self.output.extend_from_slice(slice);
129    }
130
131    #[inline(always)]
132    fn write_char(&mut self, ch: u8) {
133        self.output.push(ch);
134    }
135
136    #[inline(never)]
137    fn write_string_complex(&mut self, string: &str, mut start: usize) {
138        self.write(&string.as_bytes()[..start]);
139
140        for (index, ch) in string.bytes().enumerate().skip(start) {
141            let escape = ESCAPED[ch as usize];
142            if escape > 0 {
143                self.write(&string.as_bytes()[start..index]);
144                self.write(&[b'\\', escape]);
145                start = index + 1;
146            }
147            if escape == b'u' {
148                write!(self.output, "{:04x}", ch).unwrap();
149            }
150        }
151        self.write(&string.as_bytes()[start..]);
152
153        self.write_char(b'"');
154    }
155
156    #[inline(always)]
157    fn write_string(&mut self, string: &str) {
158        self.write_char(b'"');
159
160        for (index, ch) in string.bytes().enumerate() {
161            if ESCAPED[ch as usize] > 0 {
162                self.write_string_complex(string, index);
163                return;
164            }
165        }
166
167        self.write(string.as_bytes());
168        self.write_char(b'"');
169    }
170
171    #[inline(always)]
172    fn write_number(&mut self, number: f64) {
173        const MAX_SIGNIFICANT_DIGITS: usize = 15;
174
175        if number.is_finite() {
176            let mut buffer = dtoa::Buffer::new();
177            let formatted = buffer.format_finite(number).as_bytes();
178
179            // JSONata uses JSON.stringify with Number.toPrecision(15) to format numbers.
180            //
181            // dtoa gets us close to the behaviour of JSON.stringify, in particular for
182            // switching to scientific notation (which Rust format! doesn't do), but dtoa
183            // doesn't support specifying a number of significant digits.
184            //
185            // This craziness limits the number of significant digits and trims off trailing
186            // zeroes in the fraction by doing string manipulation.
187            //
188            // It's not pretty, and I'm sure there's a better way to do this.
189            let mut split_iter = formatted.split(|b| *b == b'.');
190            let whole = split_iter.next();
191            let fraction = split_iter.next();
192            if let Some(whole) = whole {
193                self.write(whole);
194                if whole.len() < MAX_SIGNIFICANT_DIGITS {
195                    if let Some(fraction) = fraction {
196                        let fraction_length =
197                            usize::min(MAX_SIGNIFICANT_DIGITS - whole.len(), fraction.len());
198                        if fraction_length > 0 {
199                            let fraction = unsafe {
200                                std::str::from_utf8_unchecked(&fraction[0..fraction_length])
201                                    .trim_end_matches('0')
202                            };
203                            if !fraction.is_empty() {
204                                self.write_char(b'.');
205                                self.write(fraction.as_bytes());
206                            }
207                        }
208                    }
209                }
210            } else {
211                self.write(formatted);
212            }
213        } else {
214            self.write(b"null");
215        }
216    }
217
218    #[inline(always)]
219    fn write_object<'a>(&mut self, object: &'a Value<'a>) -> Result<()> {
220        self.write_char(b'{');
221        let mut iter = object.entries();
222
223        if let Some((key, value)) = iter.next() {
224            self.formatter.indent();
225            self.formatter.new_line(&mut self.output);
226            self.write_string(key);
227            self.formatter.write_min(&mut self.output, b": ", b':');
228            self.write_json(value)?;
229        } else {
230            self.write_char(b'}');
231            return Ok(());
232        }
233
234        for (key, value) in iter {
235            self.write_char(b',');
236            self.formatter.new_line(&mut self.output);
237            self.write_string(key);
238            self.formatter.write_min(&mut self.output, b": ", b':');
239            self.write_json(value)?;
240        }
241
242        self.formatter.dedent();
243        self.formatter.new_line(&mut self.output);
244        self.write_char(b'}');
245
246        Ok(())
247    }
248
249    #[inline(always)]
250    fn write_array<'a>(&mut self, array: &'a Value<'a>) -> Result<()> {
251        self.write_char(b'[');
252        let mut iter = array.members();
253
254        if let Some(item) = iter.next() {
255            self.formatter.indent();
256            self.formatter.new_line(&mut self.output);
257            self.write_json(item)?;
258        } else {
259            self.write_char(b']');
260            return Ok(());
261        }
262
263        for item in iter {
264            self.write_char(b',');
265            self.formatter.new_line(&mut self.output);
266            self.write_json(item)?;
267        }
268
269        self.formatter.dedent();
270        self.formatter.new_line(&mut self.output);
271        self.write_char(b']');
272
273        Ok(())
274    }
275
276    fn write_json<'a>(&mut self, value: &'a Value<'a>) -> Result<()> {
277        match value {
278            Value::Undefined => {}
279            Value::Null => self.write(b"null"),
280            Value::String(ref string) => self.write_string(string),
281            Value::Number(n) => {
282                if self.fail_on_invalid_numbers {
283                    value.is_valid_number()?;
284                }
285                self.write_number(*n);
286            }
287            Value::Bool(true) => self.write(b"true"),
288            Value::Bool(false) => self.write(b"false"),
289            Value::Array(..) | Value::Range(..) => self.write_array(value)?,
290            Value::Object(..) => self.write_object(value)?,
291            Value::Lambda { .. } | Value::NativeFn { .. } => self.write(b"\"\""),
292        };
293
294        Ok(())
295    }
296}