|
| 1 | +// Allow `assert_eq!(true, ...)` because it is used to check a bool value and not |
| 2 | +// a 'flag' / 'state', and `assert_eq!` makes that more explicit |
| 3 | +#![allow(clippy::bool_assert_comparison)] |
| 4 | + |
| 5 | +use std::error::Error; |
| 6 | + |
| 7 | +use assert_no_alloc::permit_alloc; |
| 8 | +// Only use import when creating debug builds, see also configuration below |
| 9 | +#[cfg(debug_assertions)] |
| 10 | +use assert_no_alloc::AllocDisabler; |
| 11 | +use struson::{ |
| 12 | + json_path, |
| 13 | + reader::{JsonReader, JsonStreamReader, ReaderSettings}, |
| 14 | + writer::{JsonStreamWriter, JsonWriter}, |
| 15 | +}; |
| 16 | + |
| 17 | +// Only enable when creating debug builds |
| 18 | +#[cfg(debug_assertions)] |
| 19 | +#[global_allocator] |
| 20 | +static A: AllocDisabler = AllocDisabler; |
| 21 | + |
| 22 | +fn assert_no_alloc<F: FnOnce() -> Result<(), Box<dyn Error>>>(func: F) { |
| 23 | + assert_no_alloc::assert_no_alloc(func).unwrap() |
| 24 | +} |
| 25 | + |
| 26 | +fn new_reader(json: &str) -> JsonStreamReader<&[u8]> { |
| 27 | + JsonStreamReader::new_custom( |
| 28 | + json.as_bytes(), |
| 29 | + ReaderSettings { |
| 30 | + // Disable path tracking because that causes allocations |
| 31 | + track_path: false, |
| 32 | + ..Default::default() |
| 33 | + }, |
| 34 | + ) |
| 35 | +} |
| 36 | + |
| 37 | +#[test] |
| 38 | +fn skip() { |
| 39 | + let json = |
| 40 | + r#"{"a": [{"b": 1, "c": [[], [2, {"d": 3, "e": "some string value"}]]}], "f": true}"#; |
| 41 | + let mut json_reader = new_reader(json); |
| 42 | + |
| 43 | + assert_no_alloc(|| { |
| 44 | + json_reader.skip_value()?; |
| 45 | + |
| 46 | + // Use permit_alloc because assert_no_alloc currently also forbids dealloc |
| 47 | + permit_alloc(|| json_reader.consume_trailing_whitespace())?; |
| 48 | + Ok(()) |
| 49 | + }); |
| 50 | +} |
| 51 | + |
| 52 | +#[test] |
| 53 | +fn read_values() { |
| 54 | + let json = "{\"a\": [\"string\", \"\u{1234}\u{10FFFF}\", 1234.5e+6, true, false, null]}"; |
| 55 | + let mut json_reader = new_reader(json); |
| 56 | + |
| 57 | + assert_no_alloc(|| { |
| 58 | + json_reader.begin_object()?; |
| 59 | + assert_eq!("a", json_reader.next_name()?); |
| 60 | + json_reader.begin_array()?; |
| 61 | + |
| 62 | + assert_eq!("string", json_reader.next_str()?); |
| 63 | + assert_eq!("\u{1234}\u{10FFFF}", json_reader.next_str()?); |
| 64 | + assert_eq!("1234.5e+6", json_reader.next_number_as_str()?); |
| 65 | + assert_eq!(true, json_reader.next_bool()?); |
| 66 | + assert_eq!(false, json_reader.next_bool()?); |
| 67 | + json_reader.next_null()?; |
| 68 | + |
| 69 | + json_reader.end_array()?; |
| 70 | + json_reader.end_object()?; |
| 71 | + // Use permit_alloc because assert_no_alloc currently also forbids dealloc |
| 72 | + permit_alloc(|| json_reader.consume_trailing_whitespace())?; |
| 73 | + Ok(()) |
| 74 | + }); |
| 75 | +} |
| 76 | + |
| 77 | +#[test] |
| 78 | +fn read_string_escape_sequences() { |
| 79 | + let json = r#"["\n", "\t a", "a \u1234", "a \uDBFF\uDFFF b"]"#; |
| 80 | + let mut json_reader = new_reader(json); |
| 81 | + |
| 82 | + assert_no_alloc(|| { |
| 83 | + json_reader.begin_array()?; |
| 84 | + // These don't cause allocation because the internal value buffer has already |
| 85 | + // been allocated when the JSON reader was created, and is then reused |
| 86 | + assert_eq!("\n", json_reader.next_str()?); |
| 87 | + assert_eq!("\t a", json_reader.next_str()?); |
| 88 | + assert_eq!("a \u{1234}", json_reader.next_str()?); |
| 89 | + assert_eq!("a \u{10FFFF} b", json_reader.next_str()?); |
| 90 | + |
| 91 | + json_reader.end_array()?; |
| 92 | + // Use permit_alloc because assert_no_alloc currently also forbids dealloc |
| 93 | + permit_alloc(|| json_reader.consume_trailing_whitespace())?; |
| 94 | + Ok(()) |
| 95 | + }); |
| 96 | +} |
| 97 | + |
| 98 | +#[test] |
| 99 | +fn string_value_reader() -> Result<(), Box<dyn Error>> { |
| 100 | + let repetition_count = 100; |
| 101 | + let json_string_value = "\\n \\t a \\u1234 \\uDBFF\\uDFFF \u{10FFFF}".repeat(repetition_count); |
| 102 | + let expected_string_value = "\n \t a \u{1234} \u{10FFFF} \u{10FFFF}".repeat(repetition_count); |
| 103 | + let json = format!("\"{json_string_value}\""); |
| 104 | + let mut json_reader = new_reader(&json); |
| 105 | + |
| 106 | + // Pre-allocate with expected size to avoid allocations during test execution |
| 107 | + let mut string_output = String::with_capacity(expected_string_value.len()); |
| 108 | + |
| 109 | + // Obtain this here because return value is `Box<dyn ...>` and therefore performs allocation |
| 110 | + let mut string_value_reader = json_reader.next_string_reader()?; |
| 111 | + |
| 112 | + assert_no_alloc(|| { |
| 113 | + string_value_reader.read_to_string(&mut string_output)?; |
| 114 | + Ok(()) |
| 115 | + }); |
| 116 | + drop(string_value_reader); |
| 117 | + // TODO: Ideally would call this inside assert_no_alloc, but is not possible because `string_value_reader` |
| 118 | + // is currently created outside of it and prevents moving `json_reader` into closure |
| 119 | + json_reader.consume_trailing_whitespace()?; |
| 120 | + |
| 121 | + assert_eq!(expected_string_value, string_output); |
| 122 | + Ok(()) |
| 123 | +} |
| 124 | + |
| 125 | +#[test] |
| 126 | +#[ignore = "transfer_to calls JsonWriter.string_value_writer and that returns a `Box<dyn ...>`"] |
| 127 | +fn transfer_to() -> Result<(), Box<dyn Error>> { |
| 128 | + let inner_json = r#"{"a":[{"b":1,"c":[[],[2,{"d":3,"e":"some string value"}]]}],"f":true}"#; |
| 129 | + let json = "{\"outer-ignored\": 1, \"outer\":[\"ignored\", ".to_owned() + inner_json + "]}"; |
| 130 | + let mut json_reader = new_reader(&json); |
| 131 | + |
| 132 | + // Pre-allocate with expected size to avoid allocations during test execution |
| 133 | + let mut writer = Vec::<u8>::with_capacity(inner_json.len()); |
| 134 | + let mut json_writer = JsonStreamWriter::new(&mut writer); |
| 135 | + |
| 136 | + let json_path = json_path!["outer", 1]; |
| 137 | + |
| 138 | + assert_no_alloc(|| { |
| 139 | + json_reader.seek_to(&json_path)?; |
| 140 | + |
| 141 | + json_reader.transfer_to(&mut json_writer)?; |
| 142 | + // Use permit_alloc because assert_no_alloc currently also forbids dealloc |
| 143 | + permit_alloc(|| json_writer.finish_document())?; |
| 144 | + |
| 145 | + json_reader.skip_to_top_level()?; |
| 146 | + // Use permit_alloc because assert_no_alloc currently also forbids dealloc |
| 147 | + permit_alloc(|| json_reader.consume_trailing_whitespace())?; |
| 148 | + Ok(()) |
| 149 | + }); |
| 150 | + |
| 151 | + assert_eq!(inner_json, std::str::from_utf8(&writer)?); |
| 152 | + Ok(()) |
| 153 | +} |
0 commit comments