• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kaidokert / picojson-rs / 16245398594

13 Jul 2025 04:36AM UTC coverage: 93.804% (+0.1%) from 93.685%
16245398594

Pull #55

github

web-flow
Merge 16d6f9c0b into d470170ad
Pull Request #55: Big refactor

512 of 562 new or added lines in 5 files covered. (91.1%)

1 existing line in 1 file now uncovered.

4693 of 5003 relevant lines covered (93.8%)

678.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.44
/picojson/src/slice_parser.rs
1
// SPDX-License-Identifier: Apache-2.0
2

3
use crate::copy_on_escape::CopyOnEscape;
4
use crate::escape_processor::UnicodeEscapeCollector;
5
use crate::event_processor::{
6
    finish_tokenizer, have_events, process_begin_escape_sequence_event, process_begin_events,
7
    process_byte_through_tokenizer, process_simple_escape_event, process_simple_events,
8
    process_unicode_escape_events, take_first_event, ContentExtractor, EscapeHandler, EventResult,
9
};
10
use crate::number_parser::NumberExtractor;
11
use crate::parse_error::ParseError;
12
use crate::shared::{
13
    ByteProvider, ContentRange, Event, ParserState, PullParser, State, UnexpectedState,
14
};
15
use crate::slice_input_buffer::{InputBuffer, SliceInputBuffer};
16
use crate::ujson;
17
use ujson::{EventToken, Tokenizer};
18

19
use ujson::{BitStackConfig, DefaultConfig};
20

21
/// A pull parser that parses JSON from a slice.
22
///
23
/// Generic over BitStack storage type for configurable nesting depth.
24
// Lifetime 'a is the input buffer lifetime
25
// lifetime 'b is the scratch/copy buffer lifetime
26
pub struct SliceParser<'a, 'b, C: BitStackConfig = DefaultConfig> {
27
    tokenizer: Tokenizer<C::Bucket, C::Counter>,
28
    buffer: SliceInputBuffer<'a>,
29
    parser_state: ParserState,
30
    copy_on_escape: CopyOnEscape<'a, 'b>,
31
    /// Shared Unicode escape collector for \uXXXX sequences
32
    unicode_escape_collector: UnicodeEscapeCollector,
33
}
34

35
/// Methods for the pull parser.
36
impl<'a> SliceParser<'a, '_, DefaultConfig> {
37
    /// Creates a new parser for the given JSON input.
38
    ///
39
    /// This parser assumes no string escapes will be encountered. If escapes are found,
40
    /// parsing will fail with `ScratchBufferFull` error.
41
    ///
42
    /// For JSON with potential string escapes, use `with_buffer()` instead.
43
    ///
44
    /// # Arguments
45
    /// * `input` - A string slice containing the JSON data to be parsed.
46
    ///
47
    /// # Example
48
    /// ```
49
    /// use picojson::SliceParser;
50
    /// let parser = SliceParser::new(r#"{"name": "value"}"#);
51
    /// ```
52
    pub fn new(input: &'a str) -> Self {
9✔
53
        Self::new_from_slice(input.as_bytes())
9✔
54
    }
9✔
55

56
    /// Creates a new parser from a byte slice.
57
    ///
58
    /// Assumes no string escapes will be encountered. For JSON with escapes, use [`with_buffer_from_slice`].
59
    ///
60
    /// # Example
61
    /// ```
62
    /// # use picojson::SliceParser;
63
    /// let parser = SliceParser::new_from_slice(br#"{"name": "value"}"#);
64
    /// ```
65
    ///
66
    /// [`with_buffer_from_slice`]: Self::with_buffer_from_slice
67
    pub fn new_from_slice(input: &'a [u8]) -> Self {
9✔
68
        Self::with_config_from_slice(input)
9✔
69
    }
9✔
70
}
71

72
/// Constructor with scratch buffer for SliceParser using DefaultConfig
73
impl<'a, 'b> SliceParser<'a, 'b, DefaultConfig> {
74
    /// Creates a new parser for the given JSON input with external scratch buffer.
75
    ///
76
    /// Use this when your JSON contains string escapes (like `\n`, `\"`, `\u0041`) that
77
    /// need to be unescaped during parsing.
78
    ///
79
    /// # Arguments
80
    /// * `input` - A string slice containing the JSON data to be parsed.
81
    /// * `scratch_buffer` - A mutable byte slice for temporary string unescaping operations.
82
    ///   This buffer needs to be at least as long as the longest
83
    ///   contiguous token (string, key, number) in the input.
84
    ///
85
    /// # Example
86
    /// ```
87
    /// use picojson::SliceParser;
88
    /// let mut scratch = [0u8; 1024];
89
    /// let parser = SliceParser::with_buffer(r#"{"msg": "Hello\nWorld"}"#, &mut scratch);
90
    /// ```
91
    pub fn with_buffer(input: &'a str, scratch_buffer: &'b mut [u8]) -> Self {
51✔
92
        Self::with_buffer_from_slice(input.as_bytes(), scratch_buffer)
51✔
93
    }
51✔
94

95
    /// Creates a new parser from a byte slice with a scratch buffer.
96
    ///
97
    /// Use when JSON contains string escapes that need unescaping.
98
    ///
99
    /// # Example
100
    /// ```
101
    /// # use picojson::SliceParser;
102
    /// let mut scratch = [0u8; 1024];
103
    /// let parser = SliceParser::with_buffer_from_slice(br#"{"msg": "Hello\nWorld"}"#, &mut scratch);
104
    /// ```
105
    pub fn with_buffer_from_slice(input: &'a [u8], scratch_buffer: &'b mut [u8]) -> Self {
52✔
106
        Self::with_config_and_buffer_from_slice(input, scratch_buffer)
52✔
107
    }
52✔
108
}
109

110
/// Generic constructor for SliceParser with custom configurations
111
impl<'a, 'b, C: BitStackConfig> SliceParser<'a, 'b, C> {
112
    /// Creates a new parser with a custom `BitStackConfig`.
113
    ///
114
    /// This parser assumes no string escapes will be encountered. If escapes are found,
115
    /// parsing will fail. For JSON with escapes, use `with_config_and_buffer`.
116
    pub fn with_config(input: &'a str) -> Self {
1✔
117
        Self::with_config_from_slice(input.as_bytes())
1✔
118
    }
1✔
119

120
    /// Creates a new parser from a byte slice with a custom `BitStackConfig`.
121
    ///
122
    /// Assumes no string escapes will be encountered. For JSON with escapes, use [`with_config_and_buffer_from_slice`].
123
    ///
124
    /// [`with_config_and_buffer_from_slice`]: Self::with_config_and_buffer_from_slice
125
    pub fn with_config_from_slice(input: &'a [u8]) -> Self {
10✔
126
        Self::with_config_and_buffer_from_slice(input, &mut [])
10✔
127
    }
10✔
128

129
    /// Creates a new parser with a custom `BitStackConfig` and a user-provided scratch buffer.
130
    ///
131
    /// Use this when your JSON contains string escapes (like `\n`, `\"`, `\u0041`).
132
    ///
133
    /// # Arguments
134
    /// * `input` - A string slice containing the JSON data to be parsed.
135
    /// * `scratch_buffer` - A mutable byte slice for temporary string unescaping operations.
136
    ///   This buffer needs to be at least as long as the longest
137
    ///   contiguous token (string, key, number) in the input.
138
    pub fn with_config_and_buffer(input: &'a str, scratch_buffer: &'b mut [u8]) -> Self {
2✔
139
        Self::with_config_and_buffer_from_slice(input.as_bytes(), scratch_buffer)
2✔
140
    }
2✔
141

142
    /// Creates a new parser from a byte slice with a custom `BitStackConfig` and scratch buffer.
143
    ///
144
    /// Use when JSON contains string escapes that need unescaping.
145
    /// This is the core constructor that all other constructors delegate to.
146
    pub fn with_config_and_buffer_from_slice(
64✔
147
        input: &'a [u8],
64✔
148
        scratch_buffer: &'b mut [u8],
64✔
149
    ) -> Self {
64✔
150
        let copy_on_escape = CopyOnEscape::new(input, scratch_buffer);
64✔
151
        SliceParser {
64✔
152
            tokenizer: Tokenizer::new(),
64✔
153
            buffer: SliceInputBuffer::new(input),
64✔
154
            parser_state: ParserState::new(),
64✔
155
            copy_on_escape,
64✔
156
            unicode_escape_collector: UnicodeEscapeCollector::new(),
64✔
157
        }
64✔
158
    }
64✔
159

160
    /// Returns the next JSON event or an error if parsing fails.
161
    /// Parsing continues until `EndDocument` is returned or an error occurs.
162
    fn next_event_impl(&mut self) -> Result<Event<'_, '_>, ParseError> {
345✔
163
        if self.buffer.is_past_end() {
345✔
164
            return Ok(Event::EndDocument);
2✔
165
        }
343✔
166

167
        loop {
168
            while !have_events(&self.parser_state.evts) {
2,914✔
169
                if !self.pull_tokenizer_events()? {
2,276✔
170
                    return Ok(Event::EndDocument);
36✔
171
                }
2,232✔
172
            }
173

174
            let taken_event = take_first_event(&mut self.parser_state.evts);
638✔
175
            let Some(taken) = taken_event else {
638✔
NEW
176
                return Err(UnexpectedState::StateMismatch.into());
×
177
            };
178

179
            // Try shared event processors first
180
            if let Some(result) =
473✔
181
                process_simple_events(&taken).or_else(|| process_begin_events(&taken, self))
638✔
182
            {
183
                match result {
473✔
184
                    EventResult::Complete(event) => return Ok(event),
134✔
185
                    EventResult::ExtractString => return self.validate_and_extract_string(),
50✔
186
                    EventResult::ExtractKey => return self.validate_and_extract_key(),
74✔
187
                    EventResult::ExtractNumber(from_container_end) => {
31✔
188
                        return self.validate_and_extract_number(from_container_end)
31✔
189
                    }
190
                    EventResult::Continue => continue,
184✔
191
                }
192
            }
165✔
193

194
            // Handle parser-specific events
195
            match taken {
20✔
196
                ujson::Event::Begin(EventToken::EscapeSequence) => {
197
                    process_begin_escape_sequence_event(self)?;
57✔
198
                }
199
                _ if process_unicode_escape_events(&taken, self)? => {
108✔
200
                    // Unicode escape events handled by shared function
62✔
201
                }
62✔
202
                ujson::Event::Begin(
203
                    escape_token @ (EventToken::EscapeQuote
20✔
204
                    | EventToken::EscapeBackslash
205
                    | EventToken::EscapeSlash
206
                    | EventToken::EscapeBackspace
207
                    | EventToken::EscapeFormFeed
208
                    | EventToken::EscapeNewline
209
                    | EventToken::EscapeCarriageReturn
210
                    | EventToken::EscapeTab),
211
                ) => {
212
                    process_simple_escape_event(&escape_token, self)?;
20✔
213
                }
NEW
214
                ujson::Event::End(EventToken::EscapeSequence) => {
×
NEW
215
                    // Ignore in SliceParser since it uses slice-based parsing
×
NEW
216
                }
×
217
                _ => {
18✔
218
                    // All other events continue to next iteration
18✔
219
                }
18✔
220
            }
221
        }
222
    }
345✔
223

224
    /// Pull events from tokenizer and return whether parsing should continue
225
    /// Returns false when past end (equivalent to self.buffer.is_past_end())
226
    fn pull_tokenizer_events(&mut self) -> Result<bool, ParseError> {
2,276✔
227
        if self.buffer.is_past_end() {
2,276✔
NEW
228
            return Ok(false); // Indicate end state instead of error
×
229
        }
2,276✔
230
        // Use ByteProvider implementation to get the next byte and process it
231
        if let Some(byte) = self.next_byte()? {
2,276✔
232
            process_byte_through_tokenizer(byte, &mut self.tokenizer, &mut self.parser_state.evts)?;
2,237✔
233
        } else {
234
            finish_tokenizer(&mut self.tokenizer, &mut self.parser_state.evts)?;
39✔
235
        }
236
        Ok(!self.buffer.is_past_end()) // Return continue state
2,268✔
237
    }
2,276✔
238
}
239

240
impl<'a, 'b, C: BitStackConfig> ContentExtractor for SliceParser<'a, 'b, C> {
241
    fn parser_state_mut(&mut self) -> &mut State {
324✔
242
        &mut self.parser_state.state
324✔
243
    }
324✔
244

245
    fn current_position(&self) -> usize {
170✔
246
        self.buffer.current_pos()
170✔
247
    }
170✔
248

249
    fn begin_string_content(&mut self, pos: usize) {
137✔
250
        self.copy_on_escape.begin_string(pos);
137✔
251
    }
137✔
252

253
    fn unicode_escape_collector_mut(
179✔
254
        &mut self,
179✔
255
    ) -> &mut crate::escape_processor::UnicodeEscapeCollector {
179✔
256
        &mut self.unicode_escape_collector
179✔
257
    }
179✔
258

259
    fn extract_string_content(&mut self, _start_pos: usize) -> Result<Event<'_, '_>, ParseError> {
49✔
260
        // Use CopyOnEscape to get the final string result
261
        let end_pos = ContentRange::end_position_excluding_delimiter(self.buffer.current_pos());
49✔
262
        let value_result = self.copy_on_escape.end_string(end_pos)?;
49✔
263
        Ok(Event::String(value_result))
49✔
264
    }
49✔
265

266
    fn extract_key_content(&mut self, _start_pos: usize) -> Result<Event<'_, '_>, ParseError> {
74✔
267
        // Use CopyOnEscape to get the final key result
268
        let end_pos = ContentRange::end_position_excluding_delimiter(self.buffer.current_pos());
74✔
269
        let key_result = self.copy_on_escape.end_string(end_pos)?;
74✔
270
        Ok(Event::Key(key_result))
74✔
271
    }
74✔
272

273
    fn extract_number_content(
31✔
274
        &mut self,
31✔
275
        start_pos: usize,
31✔
276
        from_container_end: bool,
31✔
277
    ) -> Result<Event<'_, '_>, ParseError> {
31✔
278
        // Use shared number parsing with SliceParser-specific document end detection
279
        let at_document_end = self.buffer.is_empty();
31✔
280
        crate::number_parser::parse_number_with_delimiter_logic(
31✔
281
            &self.buffer,
31✔
282
            start_pos,
31✔
283
            from_container_end,
31✔
284
            at_document_end,
31✔
285
        )
286
    }
31✔
287
}
288

289
impl<'a, 'b, C: BitStackConfig> EscapeHandler for SliceParser<'a, 'b, C> {
290
    fn parser_state(&self) -> &State {
302✔
291
        &self.parser_state.state
302✔
292
    }
302✔
293

294
    fn process_unicode_escape_with_collector(&mut self) -> Result<(), ParseError> {
35✔
295
        let current_pos = self.buffer.current_pos();
35✔
296
        let hex_slice_provider = |start, end| self.buffer.slice(start, end).map_err(Into::into);
35✔
297

298
        // Shared Unicode escape processing pattern
299
        let had_pending_high_surrogate = self.unicode_escape_collector.has_pending_high_surrogate();
35✔
300

301
        let mut utf8_buf = [0u8; 4];
35✔
302
        let (utf8_bytes_opt, escape_start_pos) =
27✔
303
            crate::escape_processor::process_unicode_escape_sequence(
35✔
304
                current_pos,
35✔
305
                &mut self.unicode_escape_collector,
35✔
306
                hex_slice_provider,
35✔
307
                &mut utf8_buf,
35✔
308
            )?;
8✔
309

310
        // Handle UTF-8 bytes if we have them (not a high surrogate waiting for low surrogate)
311
        if let Some(utf8_bytes) = utf8_bytes_opt {
27✔
312
            if had_pending_high_surrogate {
11✔
313
                // This is completing a surrogate pair - need to consume both escapes
314
                // First call: consume the high surrogate (6 bytes earlier)
315
                self.copy_on_escape
8✔
316
                    .handle_unicode_escape(escape_start_pos, &[])?;
8✔
317
                // Second call: consume the low surrogate and write UTF-8
318
                self.copy_on_escape
8✔
319
                    .handle_unicode_escape(escape_start_pos + 6, utf8_bytes)?;
8✔
320
            } else {
321
                // Single Unicode escape - normal processing
322
                self.copy_on_escape
3✔
323
                    .handle_unicode_escape(escape_start_pos, utf8_bytes)?;
3✔
324
            }
325
        }
16✔
326

327
        Ok(())
27✔
328
    }
35✔
329

330
    fn handle_simple_escape_char(&mut self, escape_char: u8) -> Result<(), ParseError> {
20✔
331
        self.copy_on_escape
20✔
332
            .handle_escape(self.buffer.current_pos(), escape_char)?;
20✔
333
        Ok(())
18✔
334
    }
20✔
335

336
    /// Append a single literal byte - implement as single-byte range for consistency
NEW
337
    fn append_literal_byte(&mut self, _byte: u8) -> Result<(), ParseError> {
×
338
        // SliceParser doesn't typically need per-byte processing since it works with ranges
339
        // This could be implemented as a single-byte range if needed, but for now it's a no-op
NEW
340
        Ok(())
×
UNCOV
341
    }
×
342
}
343

344
impl<'a, 'b, C: BitStackConfig> PullParser for SliceParser<'a, 'b, C> {
345
    fn next_event(&mut self) -> Result<Event<'_, '_>, ParseError> {
345✔
346
        self.next_event_impl()
345✔
347
    }
345✔
348
}
349

350
impl<'a, 'b, C: BitStackConfig> crate::shared::ByteProvider for SliceParser<'a, 'b, C> {
351
    fn next_byte(&mut self) -> Result<Option<u8>, ParseError> {
2,276✔
352
        use crate::slice_input_buffer::InputBuffer;
353
        match self.buffer.consume_byte() {
2,276✔
354
            Ok(byte) => Ok(Some(byte)),
2,237✔
355
            Err(crate::slice_input_buffer::Error::ReachedEnd) => Ok(None),
39✔
NEW
356
            Err(err) => Err(err.into()),
×
357
        }
358
    }
2,276✔
359
}
360

361
#[cfg(test)]
362
mod tests {
363
    use super::*;
364
    use crate::{ArrayBitStack, BitStackStruct, String};
365

366
    #[test]
367
    fn make_parser() {
1✔
368
        let input = r#"{"key": "value"}"#;
1✔
369
        let mut scratch = [0u8; 1024];
1✔
370
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
371
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
372
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
373
        assert_eq!(
1✔
374
            parser.next_event(),
1✔
375
            Ok(Event::String(String::Borrowed("value")))
376
        );
377
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
378
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
379
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
380
    }
1✔
381

382
    #[test]
383
    fn parse_number() {
1✔
384
        let input = r#"{"key": 124}"#;
1✔
385
        let mut scratch = [0u8; 1024];
1✔
386
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
387
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
388
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
389
        // Check number value using new JsonNumber API
390
        match parser.next_event() {
1✔
391
            Ok(Event::Number(num)) => {
1✔
392
                assert_eq!(num.as_str(), "124");
1✔
393
                assert_eq!(num.as_int(), Some(124));
1✔
394
            }
395
            other => panic!("Expected Number, got: {:?}", other),
×
396
        }
397
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
398
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
399
    }
1✔
400

401
    #[test]
402
    fn parse_bool_and_null() {
1✔
403
        let input = r#"{"key": true, "key2": false, "key3": null}"#;
1✔
404
        let mut scratch = [0u8; 1024];
1✔
405
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
406
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
407
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
408
        assert_eq!(parser.next_event(), Ok(Event::Bool(true)));
1✔
409
        assert_eq!(
1✔
410
            parser.next_event(),
1✔
411
            Ok(Event::Key(String::Borrowed("key2")))
412
        );
413
        assert_eq!(parser.next_event(), Ok(Event::Bool(false)));
1✔
414
        assert_eq!(
1✔
415
            parser.next_event(),
1✔
416
            Ok(Event::Key(String::Borrowed("key3")))
417
        );
418
        assert_eq!(parser.next_event(), Ok(Event::Null));
1✔
419
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
420
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
421
    }
1✔
422

423
    #[test]
424
    fn parse_array() {
1✔
425
        #[cfg(feature = "float-error")]
426
        let input = r#"{"key": [1, 2, 3]}"#; // No floats for float-error config
427
        #[cfg(not(feature = "float-error"))]
428
        let input = r#"{"key": [1, 2.2, 3]}"#; // Include float for other configs
1✔
429

430
        let mut scratch = [0u8; 1024];
1✔
431
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
432
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
433
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
434
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
435

436
        // First number: 1 (integer)
437
        match parser.next_event() {
1✔
438
            Ok(Event::Number(num)) => {
1✔
439
                assert_eq!(num.as_str(), "1");
1✔
440
                assert_eq!(num.as_int(), Some(1));
1✔
441
            }
442
            other => panic!("Expected Number(1), got: {:?}", other),
×
443
        }
444

445
        // Second number: depends on configuration
446
        match parser.next_event() {
1✔
447
            Ok(Event::Number(num)) => {
1✔
448
                #[cfg(feature = "float-error")]
449
                {
450
                    assert_eq!(num.as_str(), "2");
451
                    assert_eq!(num.as_int(), Some(2));
452
                }
453
                #[cfg(not(feature = "float-error"))]
454
                {
455
                    assert_eq!(num.as_str(), "2.2");
1✔
456
                    #[cfg(feature = "float")]
457
                    assert_eq!(num.as_f64(), Some(2.2));
1✔
458
                    #[cfg(not(feature = "float-error"))]
459
                    assert!(num.is_float());
1✔
460
                }
461
            }
462
            other => panic!("Expected Number, got: {:?}", other),
×
463
        }
464

465
        // Third number: 3 (integer)
466
        match parser.next_event() {
1✔
467
            Ok(Event::Number(num)) => {
1✔
468
                assert_eq!(num.as_str(), "3");
1✔
469
                assert_eq!(num.as_int(), Some(3));
1✔
470
            }
471
            other => panic!("Expected Number(3), got: {:?}", other),
×
472
        }
473

474
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
475
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
476
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
477
    }
1✔
478

479
    #[test]
480
    fn test_simple_parser_api() {
1✔
481
        let input = r#"{"name": "test"}"#;
1✔
482
        let mut scratch = [0u8; 1024];
1✔
483
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
484

485
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
486
        assert_eq!(
1✔
487
            parser.next_event(),
1✔
488
            Ok(Event::Key(String::Borrowed("name")))
489
        );
490
        assert_eq!(
1✔
491
            parser.next_event(),
1✔
492
            Ok(Event::String(String::Borrowed("test")))
493
        );
494
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
495
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
496
    }
1✔
497

498
    #[test]
499
    fn test_parser_with_escaped_strings() {
1✔
500
        // Use regular string literal to properly include escape sequences
501
        let input = "{\"name\": \"John\\nDoe\", \"message\": \"Hello\\tWorld!\"}";
1✔
502
        let mut scratch = [0u8; 1024];
1✔
503
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
504

505
        // Test that the parser correctly handles escaped strings
506
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
507

508
        // Key should be simple (no escapes) -> Borrowed
509
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
510
            assert_eq!(&*key, "name");
1✔
511
            // This should be the fast path (borrowed)
512
            assert!(matches!(key, String::Borrowed(_)));
1✔
513
        } else {
514
            panic!("Expected Key event");
×
515
        }
516

517
        // Value should have escapes -> Unescaped
518
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
519
            assert_eq!(&*value, "John\nDoe");
1✔
520
            // This should be the slow path (unescaped)
521
            assert!(matches!(value, String::Unescaped(_)));
1✔
522
        } else {
523
            panic!("Expected String event");
×
524
        }
525

526
        // Second key should be simple -> Borrowed
527
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
528
            assert_eq!(&*key, "message");
1✔
529
            assert!(matches!(key, String::Borrowed(_)));
1✔
530
        } else {
531
            panic!("Expected Key event");
×
532
        }
533

534
        // Second value should have escapes -> Unescaped
535
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
536
            assert_eq!(&*value, "Hello\tWorld!");
1✔
537
            assert!(matches!(value, String::Unescaped(_)));
1✔
538
        } else {
539
            panic!("Expected String event");
×
540
        }
541

542
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
543
    }
1✔
544

545
    #[test]
546
    fn test_copy_on_escape_optimization() {
1✔
547
        // Use regular string literal to include proper escape sequences
548
        let input = "{\"simple\": \"no escapes\", \"complex\": \"has\\nescapes\"}";
1✔
549
        let mut scratch = [0u8; 1024];
1✔
550
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
551

552
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
553

554
        // "simple" key should be borrowed (fast path)
555
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
556
            assert_eq!(&*key, "simple");
1✔
557
            assert!(matches!(key, String::Borrowed(_)));
1✔
558
        } else {
559
            panic!("Expected Key event");
×
560
        }
561

562
        // "no escapes" value should be borrowed (fast path)
563
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
564
            assert_eq!(&*value, "no escapes");
1✔
565
            assert!(matches!(value, String::Borrowed(_)));
1✔
566
        } else {
567
            panic!("Expected String event");
×
568
        }
569

570
        // "complex" key should be borrowed (fast path)
571
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
572
            assert_eq!(&*key, "complex");
1✔
573
            assert!(matches!(key, String::Borrowed(_)));
1✔
574
        } else {
575
            panic!("Expected Key event");
×
576
        }
577

578
        // "has\\nescapes" value should be unescaped (slow path)
579
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
580
            assert_eq!(&*value, "has\nescapes");
1✔
581
            assert!(matches!(value, String::Unescaped(_)));
1✔
582
        } else {
583
            panic!("Expected String event");
×
584
        }
585

586
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
587
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
588
    }
1✔
589

590
    #[test]
591
    fn test_coe2_integration_multiple_escapes() {
1✔
592
        let input = r#"{"key": "a\nb\tc\rd"}"#;
1✔
593
        let mut scratch = [0u8; 1024];
1✔
594
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
595

596
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
597
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
598

599
        let string_event = parser.next_event().unwrap();
1✔
600
        match string_event {
1✔
601
            Event::String(String::Unescaped(s)) => {
1✔
602
                assert_eq!(s, "a\nb\tc\rd");
1✔
603
            }
604
            _ => panic!("Expected unescaped string value, got: {:?}", string_event),
×
605
        }
606
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
607
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
608
    }
1✔
609

610
    #[test]
611
    fn test_coe2_integration_zero_copy_path() {
1✔
612
        let input = r#"{"simple": "no_escapes_here"}"#;
1✔
613
        let mut scratch = [0u8; 1024];
1✔
614
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
615

616
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
617
        assert_eq!(
1✔
618
            parser.next_event(),
1✔
619
            Ok(Event::Key(String::Borrowed("simple")))
620
        );
621

622
        // This should be borrowed (zero-copy) since no escapes
623
        let string_event = parser.next_event().unwrap();
1✔
624
        match string_event {
1✔
625
            Event::String(String::Borrowed(s)) => {
1✔
626
                assert_eq!(s, "no_escapes_here");
1✔
627
            }
628
            _ => panic!(
×
629
                "Expected borrowed string value for zero-copy, got: {:?}",
×
630
                string_event
631
            ),
632
        }
633
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
634
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
635
    }
1✔
636

637
    #[test]
638
    fn test_coe2_integration_mixed_strings() {
1✔
639
        let input = r#"["plain", "with\nescapes", "plain2", "more\tescapes"]"#;
1✔
640
        let mut scratch = [0u8; 1024];
1✔
641
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
642

643
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
644

645
        // First string: no escapes -> borrowed
646
        match parser.next_event().unwrap() {
1✔
647
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain"),
1✔
648
            other => panic!("Expected borrowed string, got: {:?}", other),
×
649
        }
650

651
        // Second string: has escapes -> unescaped
652
        match parser.next_event().unwrap() {
1✔
653
            Event::String(String::Unescaped(s)) => assert_eq!(s, "with\nescapes"),
1✔
654
            other => panic!("Expected unescaped string, got: {:?}", other),
×
655
        }
656

657
        // Third string: no escapes -> borrowed
658
        match parser.next_event().unwrap() {
1✔
659
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain2"),
1✔
660
            other => panic!("Expected borrowed string, got: {:?}", other),
×
661
        }
662

663
        // Fourth string: has escapes -> unescaped
664
        match parser.next_event().unwrap() {
1✔
665
            Event::String(String::Unescaped(s)) => assert_eq!(s, "more\tescapes"),
1✔
666
            other => panic!("Expected unescaped string, got: {:?}", other),
×
667
        }
668

669
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
670
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
671
    }
1✔
672

673
    #[test]
674
    fn test_unicode_escape_integration() {
1✔
675
        let input = r#"{"key": "Hello\u0041World"}"#; // \u0041 = 'A'
1✔
676
        let mut scratch = [0u8; 1024];
1✔
677
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
678

679
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
680
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
681

682
        // The string with Unicode escape should be unescaped
683
        match parser.next_event().unwrap() {
1✔
684
            Event::String(String::Unescaped(s)) => {
1✔
685
                assert_eq!(s, "HelloAWorld");
1✔
686
            }
687
            other => panic!("Expected unescaped string value, got: {:?}", other),
×
688
        }
689

690
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
691
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
692
    }
1✔
693

694
    #[test]
695
    fn test_original_parser_escape_trace() {
1✔
696
        // Test escape sequence processing with logging
697
        let input = r#""a\nb""#;
1✔
698
        let mut scratch = [0u8; 1024];
1✔
699
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
700

701
        // Should get String with unescaped content
702
        let event = parser.next_event().unwrap();
1✔
703
        if let Event::String(s) = event {
1✔
704
            assert_eq!(&*s, "a\nb");
1✔
705
        } else {
706
            panic!("Expected String event, got {:?}", event);
×
707
        }
708

709
        // Should get EndDocument
710
        let event = parser.next_event().unwrap();
1✔
711
        assert_eq!(event, Event::EndDocument);
1✔
712
    }
1✔
713

714
    #[test]
715
    fn make_parser_from_slice() {
1✔
716
        let input = br#"{"key": "value"}"#;
1✔
717
        let mut scratch = [0u8; 1024];
1✔
718
        let mut parser = SliceParser::with_buffer_from_slice(input, &mut scratch);
1✔
719
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
720
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
721
        assert_eq!(
1✔
722
            parser.next_event(),
1✔
723
            Ok(Event::String(String::Borrowed("value")))
724
        );
725
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
726
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
727
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
728
    }
1✔
729

730
    #[test]
731
    fn test_with_config_constructors() {
1✔
732
        // Test with_config constructor (no escapes)
733
        let json = r#"{"simple": "no_escapes"}"#;
1✔
734
        let mut parser = SliceParser::<BitStackStruct<u64, u16>>::with_config(json);
1✔
735

736
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
737
        assert_eq!(
1✔
738
            parser.next_event(),
1✔
739
            Ok(Event::Key(String::Borrowed("simple")))
740
        );
741
        assert_eq!(
1✔
742
            parser.next_event(),
1✔
743
            Ok(Event::String(String::Borrowed("no_escapes")))
744
        );
745
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
746
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
747
    }
1✔
748

749
    #[test]
750
    fn test_with_config_and_buffer_constructors() {
1✔
751
        // Test with_config_and_buffer constructor (with escapes)
752
        let json = r#"{"escaped": "hello\nworld"}"#;
1✔
753
        let mut scratch = [0u8; 64];
1✔
754
        let mut parser =
1✔
755
            SliceParser::<BitStackStruct<u64, u16>>::with_config_and_buffer(json, &mut scratch);
1✔
756

757
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
758
        assert_eq!(
1✔
759
            parser.next_event(),
1✔
760
            Ok(Event::Key(String::Borrowed("escaped")))
761
        );
762

763
        if let Ok(Event::String(s)) = parser.next_event() {
1✔
764
            assert_eq!(s.as_ref(), "hello\nworld"); // Escape should be processed
1✔
765
        } else {
766
            panic!("Expected String event");
×
767
        }
768

769
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
770
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
771
    }
1✔
772

773
    #[test]
774
    fn test_alternative_config_deep_nesting() {
1✔
775
        // Test that custom BitStack configs can handle deeper nesting
776
        let json = r#"{"a":{"b":{"c":{"d":{"e":"deep"}}}}}"#;
1✔
777
        let mut scratch = [0u8; 64];
1✔
778
        let mut parser =
1✔
779
            SliceParser::<ArrayBitStack<8, u32, u16>>::with_config_and_buffer(json, &mut scratch);
1✔
780

781
        // Parse the deep structure
782
        let mut depth = 0;
1✔
783
        while let Ok(event) = parser.next_event() {
17✔
784
            match event {
17✔
785
                Event::StartObject => depth += 1,
5✔
786
                Event::EndObject => depth -= 1,
5✔
787
                Event::EndDocument => break,
1✔
788
                _ => {}
6✔
789
            }
790
        }
791

792
        // Should have successfully parsed a 5-level deep structure
793
        assert_eq!(depth, 0); // All objects should be closed
1✔
794
    }
1✔
795
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc