1// Copyright (c) 2023 Huawei Device Co., Ltd.
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6//     http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14use crate::{consts::*, Array, Error, JsonValue, Number, Object};
15#[cfg(feature = "c_adapter")]
16use std::ffi::CString;
17use std::io::Write;
18
19// todo: Considers extracting Encoder traits.
20
21/// JSON encoder with additional formats, used to output JsonValue instances in JSON format to the specified location.
22///
23/// This encoder will add additional formatting control whitespace characters during encoding.
24pub(crate) struct FormattedEncoder<'a, W: Write> {
25    output: &'a mut W,
26    /// The current number of nested layers
27    tab: usize,
28}
29
30impl<'a, W: Write> FormattedEncoder<'a, W> {
31    /// Creates
32    pub(crate) fn new(output: &'a mut W) -> Self {
33        Self { output, tab: 0 }
34    }
35
36    /// Encodes
37    pub(crate) fn encode(&mut self, value: &JsonValue) -> Result<(), Error> {
38        self.encode_value(value)?;
39        self.output.write_all(LINE_FEED_STR)?;
40        Ok(())
41    }
42
43    /// Encodes JsonValue
44    fn encode_value(&mut self, value: &JsonValue) -> Result<(), Error> {
45        match value {
46            JsonValue::Null => self.encode_null(),
47            JsonValue::Boolean(boolean) => self.encode_boolean(boolean),
48            JsonValue::Number(number) => self.encode_number(number),
49            JsonValue::String(string) => self.encode_string(string),
50            JsonValue::Array(array) => self.encode_array(array),
51            JsonValue::Object(object) => self.encode_object(object),
52        }
53    }
54
55    /// Add tabs to improve readability.
56    fn add_tab(&mut self) -> Result<(), Error> {
57        for _ in 0..self.tab {
58            self.output.write_all(FOUR_SPACES_STR)?;
59        }
60        Ok(())
61    }
62
63    /// Encodes Null
64    fn encode_null(&mut self) -> Result<(), Error> {
65        encode_null(self.output)
66    }
67
68    /// Encodes Boolean
69    fn encode_boolean(&mut self, boolean: &bool) -> Result<(), Error> {
70        encode_boolean(self.output, *boolean)
71    }
72
73    /// Encodes Number
74    fn encode_number(&mut self, number: &Number) -> Result<(), Error> {
75        encode_number(self.output, number)
76    }
77
78    /// Encodes Key
79    fn encode_key(&mut self, key: &str) -> Result<(), Error> {
80        encode_string(self.output, key)
81    }
82
83    /// Encodes String
84    #[cfg(feature = "c_adapter")]
85    fn encode_string(&mut self, string: &CString) -> Result<(), Error> {
86        encode_string(self.output, unsafe {
87            core::str::from_utf8_unchecked(string.as_bytes())
88        })
89    }
90
91    /// Encodes String
92    #[cfg(not(feature = "c_adapter"))]
93    fn encode_string(&mut self, string: &str) -> Result<(), Error> {
94        encode_string(self.output, string)
95    }
96
97    /// Encodes Array
98    fn encode_array(&mut self, array: &Array) -> Result<(), Error> {
99        // Check whether multiple lines are required. If array or object
100        // exists in the array value, multiple lines are required.
101        let mut multiple_line = false;
102        for v in array.iter() {
103            if v.is_array() | v.is_object() {
104                multiple_line = true;
105                break;
106            }
107        }
108
109        self.output.write_all(LEFT_SQUARE_BRACKET_STR)?;
110        if multiple_line {
111            self.output.write_all(LINE_FEED_STR)?;
112            self.tab += 1;
113            self.add_tab()?;
114            for (n, v) in array.iter().enumerate() {
115                if n != 0 {
116                    self.output.write_all(COMMA_STR)?;
117                    self.output.write_all(LINE_FEED_STR)?;
118                    self.add_tab()?;
119                }
120                self.encode_value(v)?;
121            }
122            self.output.write_all(LINE_FEED_STR)?;
123            self.tab -= 1;
124            self.add_tab()?;
125        } else {
126            for (n, v) in array.iter().enumerate() {
127                if n != 0 {
128                    self.output.write_all(COMMA_STR)?;
129                    self.output.write_all(SPACE_STR)?;
130                }
131                self.encode_value(v)?;
132            }
133        }
134        self.output.write_all(RIGHT_SQUARE_BRACKET_STR)?;
135        Ok(())
136    }
137
138    /// Encodes Object
139    fn encode_object(&mut self, object: &Object) -> Result<(), Error> {
140        self.output.write_all(LEFT_CURLY_BRACKET_STR)?;
141        self.tab += 1;
142        for (u, (k, v)) in object.iter().enumerate() {
143            if u != 0 {
144                self.output.write_all(COMMA_STR)?;
145            }
146            self.output.write_all(LINE_FEED_STR)?;
147            self.add_tab()?;
148            self.encode_key(k)?;
149            self.output.write_all(COLON_STR)?;
150            self.output.write_all(SPACE_STR)?;
151            self.encode_value(v)?;
152        }
153        self.tab -= 1;
154        // Non-empty objects require additional newlines and tabs.
155        if !object.is_empty() {
156            self.output.write_all(LINE_FEED_STR)?;
157            self.add_tab()?;
158        }
159        self.output.write_all(RIGHT_CURLY_BRACKET_STR)?;
160        Ok(())
161    }
162}
163
164/// JSON encoder that outputs no extra whitespace characters ,
165/// used to output a JsonValue instance in JSON format to a specified location.
166pub(crate) struct CompactEncoder<'a, W: Write> {
167    output: &'a mut W,
168}
169
170impl<'a, W: Write> CompactEncoder<'a, W> {
171    /// Creates
172    pub(crate) fn new(output: &'a mut W) -> Self {
173        Self { output }
174    }
175
176    /// Encodes
177    pub(crate) fn encode(&mut self, value: &JsonValue) -> Result<(), Error> {
178        self.encode_value(value)
179    }
180
181    /// Encodes JsonValue
182    fn encode_value(&mut self, value: &JsonValue) -> Result<(), Error> {
183        match value {
184            JsonValue::Null => self.encode_null(),
185            JsonValue::Boolean(boolean) => self.encode_boolean(boolean),
186            JsonValue::Number(number) => self.encode_number(number),
187            JsonValue::String(string) => self.encode_string(string),
188            JsonValue::Array(array) => self.encode_array(array),
189            JsonValue::Object(object) => self.encode_object(object),
190        }
191    }
192
193    /// Encodes Null
194    fn encode_null(&mut self) -> Result<(), Error> {
195        encode_null(self.output)
196    }
197
198    /// Encodes Boolean
199    fn encode_boolean(&mut self, boolean: &bool) -> Result<(), Error> {
200        encode_boolean(self.output, *boolean)
201    }
202
203    /// Encodes Number
204    fn encode_number(&mut self, number: &Number) -> Result<(), Error> {
205        encode_number(self.output, number)
206    }
207
208    /// Encodes Key
209    fn encode_key(&mut self, key: &str) -> Result<(), Error> {
210        encode_string(self.output, key)
211    }
212
213    /// Encodes String
214    #[cfg(feature = "c_adapter")]
215    fn encode_string(&mut self, string: &CString) -> Result<(), Error> {
216        encode_string(self.output, unsafe {
217            std::str::from_utf8_unchecked(string.as_bytes())
218        })
219    }
220
221    /// Encodes String
222    #[cfg(not(feature = "c_adapter"))]
223    fn encode_string(&mut self, string: &str) -> Result<(), Error> {
224        encode_string(self.output, string)
225    }
226
227    /// Encodes Array
228    fn encode_array(&mut self, array: &Array) -> Result<(), Error> {
229        self.output.write_all(LEFT_SQUARE_BRACKET_STR)?;
230        for (n, v) in array.iter().enumerate() {
231            if n != 0 {
232                self.output.write_all(COMMA_STR)?;
233            }
234            self.encode_value(v)?;
235        }
236        self.output.write_all(RIGHT_SQUARE_BRACKET_STR)?;
237        Ok(())
238    }
239
240    /// Encodes Object
241    fn encode_object(&mut self, object: &Object) -> Result<(), Error> {
242        self.output.write_all(LEFT_CURLY_BRACKET_STR)?;
243        for (u, (k, v)) in object.iter().enumerate() {
244            if u != 0 {
245                self.output.write_all(COMMA_STR)?;
246            }
247            self.encode_key(k)?;
248            self.output.write_all(COLON_STR)?;
249            self.encode_value(v)?;
250        }
251        self.output.write_all(RIGHT_CURLY_BRACKET_STR)?;
252        Ok(())
253    }
254}
255
256#[inline]
257fn encode_null(writer: &mut dyn Write) -> Result<(), Error> {
258    writer.write_all(NULL_STR)?;
259    Ok(())
260}
261
262#[inline]
263fn encode_boolean(writer: &mut dyn Write, boolean: bool) -> Result<(), Error> {
264    if boolean {
265        writer.write_all(TRUE_STR)?;
266    } else {
267        writer.write_all(FALSE_STR)?;
268    }
269    Ok(())
270}
271
272#[inline]
273pub(crate) fn encode_number(writer: &mut dyn Write, number: &Number) -> Result<(), Error> {
274    write!(writer, "{number}")?;
275    Ok(())
276}
277
278#[inline]
279fn encode_string(writer: &mut dyn Write, string: &str) -> Result<(), Error> {
280    writer.write_all(QUOTATION_MARK_STR)?;
281    encode_string_inner(writer, string)?;
282    writer.write_all(QUOTATION_MARK_STR)?;
283    Ok(())
284}
285
286#[cfg(feature = "ascii_only")]
287pub(crate) fn encode_string_inner(writer: &mut dyn Write, string: &str) -> Result<(), Error> {
288    let bytes = string.as_bytes();
289    let len = bytes.len();
290    let mut start = 0usize;
291
292    for i in 0..len {
293        let ch = &bytes[i];
294        if ESCAPE[(*ch) as usize] {
295            writer.write_all(&bytes[start..i])?;
296            start = i + 1;
297
298            match *ch {
299                REVERSE_SOLIDUS => writer.write_all(JSON_REVERSE_SOLIDUS)?,
300                QUOTATION_MARK => writer.write_all(JSON_QUOTATION_MARK)?,
301                BS_UNICODE_U8 => writer.write_all(JSON_BS)?,
302                FF_UNICODE_U8 => writer.write_all(JSON_FF)?,
303                LF_UNICODE_U8 => writer.write_all(JSON_LF)?,
304                CR_UNICODE_U8 => writer.write_all(JSON_CR)?,
305                HT_UNICODE_U8 => writer.write_all(JSON_HT)?,
306                x => write!(writer, "\\u{number:0>width$x}", number = x, width = 4)?,
307            }
308        }
309    }
310    if start != len {
311        writer.write_all(&bytes[start..len])?;
312    }
313
314    Ok(())
315}
316
317#[cfg(not(feature = "ascii_only"))]
318pub(crate) fn encode_string_inner(writer: &mut dyn Write, string: &str) -> Result<(), Error> {
319    fn split_pattern(
320        writer: &mut dyn Write,
321        pattern: &mut &str,
322        split_pos: &mut usize,
323        ch: char,
324    ) -> Result<(), Error> {
325        let (l, r) = (*pattern).split_at(*split_pos);
326        writer.write_all(l.as_bytes())?;
327        *pattern = r;
328
329        let (_, r) = (*pattern).split_at(ch.len_utf8());
330        *pattern = r;
331        *split_pos = 0;
332        Ok(())
333    }
334
335    let mut pattern = string;
336    let mut split_pos = 0usize;
337    for ch in string.chars() {
338        if ch.is_ascii() {
339            match PRINT_MAP[ch as usize] {
340                PrintMapItem::Other => {
341                    split_pos += 1;
342                    continue;
343                }
344                PrintMapItem::Special(x) => {
345                    split_pattern(writer, &mut pattern, &mut split_pos, ch)?;
346                    writer.write_all(x)?;
347                }
348                PrintMapItem::Control => {
349                    split_pattern(writer, &mut pattern, &mut split_pos, ch)?;
350                    let bytes = ch as u32;
351                    write!(writer, "\\u{number:0>width$x}", number = bytes, width = 4)?;
352                }
353            }
354            continue;
355        }
356        split_pattern(writer, &mut pattern, &mut split_pos, ch)?;
357        let bytes = ch as u32;
358        write!(writer, "\\u{number:0>width$x}", number = bytes, width = 4)?;
359    }
360    if split_pos != 0 {
361        writer.write_all(pattern.as_bytes())?;
362    }
363    Ok(())
364}
365
366#[cfg(test)]
367mod ut_encoder {
368    use crate::{CompactEncoder, FormattedEncoder, JsonValue};
369    use std::io::Write;
370
371    struct StringWriter {
372        string: String,
373    }
374
375    impl StringWriter {
376        fn new() -> Self {
377            Self {
378                string: String::new(),
379            }
380        }
381    }
382
383    impl Write for StringWriter {
384        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
385            self.string
386                .push_str(unsafe { std::str::from_utf8_unchecked(buf) });
387            self.flush()?;
388            Ok(buf.len())
389        }
390
391        fn flush(&mut self) -> std::io::Result<()> {
392            Ok(())
393        }
394    }
395
396    macro_rules! encoder_test_case {
397        ($encoder: ident, $input: expr, $output: expr $(,)?) => {
398            let value = JsonValue::from_text($input).unwrap();
399            let mut writer = StringWriter::new();
400            let mut encoder = $encoder::new(&mut writer);
401            assert!(encoder.encode(&value).is_ok());
402            assert_eq!(writer.string, $output);
403        };
404    }
405
406    /// UT test for `FormattedEncoder`.
407    ///
408    /// # Title
409    /// ut_formatted_encoder
410    ///
411    /// # Brief
412    /// 1. Creates a `JsonValue` called `json_value`.
413    /// 2. Creates a `FormattedEncoder` called `encoder`.
414    /// 3. Uses `encoder` to encode `json_value`.
415    /// 4. Checks if the results are correct.
416    #[test]
417    fn ut_formatted_encoder() {
418        encoder_test_case!(
419            FormattedEncoder,
420            "{\"null\":null}",
421            "{\n    \"null\": null\n}\n",
422        );
423
424        encoder_test_case!(
425            FormattedEncoder,
426            "{\"true\":true}",
427            "{\n    \"true\": true\n}\n",
428        );
429
430        encoder_test_case!(
431            FormattedEncoder,
432            "{\"false\":false}",
433            "{\n    \"false\": false\n}\n",
434        );
435
436        encoder_test_case!(
437            FormattedEncoder,
438            "{\"number\":3.14}",
439            "{\n    \"number\": 3.14\n}\n",
440        );
441
442        encoder_test_case!(
443            FormattedEncoder,
444            "{\"string\":\"HelloWorld\"}",
445            "{\n    \"string\": \"HelloWorld\"\n}\n",
446        );
447
448        encoder_test_case!(
449            FormattedEncoder,
450            "{\"array\":[1, 2, 3]}",
451            "{\n    \"array\": [1, 2, 3]\n}\n",
452        );
453
454        encoder_test_case!(
455            FormattedEncoder,
456            "{\"object\":{\"key1\":1}}",
457            "{\n    \"object\": {\n        \"key1\": 1\n    }\n}\n",
458        );
459    }
460
461    /// UT test for `CompactEncoder`.
462    ///
463    /// # Title
464    /// ut_compact_encoder
465    ///
466    /// # Brief
467    /// 1. Creates a `JsonValue` called `json_value`.
468    /// 2. Creates a `Compact` called `encoder`.
469    /// 3. Uses `encoder` to encode `json_value`.
470    /// 4. Checks if the results are correct.
471    #[test]
472    fn ut_compact_encoder() {
473        encoder_test_case!(CompactEncoder, "{\"null\":null}", "{\"null\":null}",);
474
475        encoder_test_case!(CompactEncoder, "{\"true\":true}", "{\"true\":true}",);
476
477        encoder_test_case!(CompactEncoder, "{\"false\":false}", "{\"false\":false}",);
478
479        encoder_test_case!(CompactEncoder, "{\"number\":3.14}", "{\"number\":3.14}",);
480
481        encoder_test_case!(
482            CompactEncoder,
483            "{\"string\":\"HelloWorld\"}",
484            "{\"string\":\"HelloWorld\"}",
485        );
486
487        #[cfg(not(feature = "ascii_only"))]
488        encoder_test_case!(
489            CompactEncoder,
490            "{\"string\":\"\\b\\t\\f\\n\\u0000\\u2764\"}",
491            "{\"string\":\"\\b\\t\\f\\n\\u0000\\u2764\"}",
492        );
493
494        #[cfg(feature = "ascii_only")]
495        encoder_test_case!(
496            CompactEncoder,
497            "{\"string\":\"\\b\\t\\f\\n\\u0000\\u2764\"}",
498            "{\"string\":\"\\b\\t\\f\\n\\u0000\u{2764}\"}",
499        );
500
501        encoder_test_case!(CompactEncoder, "{\"array\":[1,2,3]}", "{\"array\":[1,2,3]}",);
502
503        encoder_test_case!(
504            CompactEncoder,
505            "{\"object\":{\"key1\":1,\"key2\":2}}",
506            "{\"object\":{\"key1\":1,\"key2\":2}}",
507        );
508    }
509}
510