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

kaidokert / picojson-rs / 16239454628

12 Jul 2025 03:29PM UTC coverage: 94.234% (+0.5%) from 93.685%
16239454628

Pull #53

github

web-flow
Merge a5de05240 into 1db0180ce
Pull Request #53: Big refactor step 1

433 of 447 new or added lines in 3 files covered. (96.87%)

1 existing line in 1 file now uncovered.

4658 of 4943 relevant lines covered (94.23%)

624.61 hits per line

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

92.91
/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
    create_tokenizer_callback, have_events, process_begin_events, process_simple_escape_event,
7
    process_simple_events, process_unicode_escape_events, take_first_event, ContentExtractor,
8
    EscapeHandler, EventResult, ParserContext,
9
};
10
use crate::number_parser::NumberExtractor;
11
use crate::parse_error::ParseError;
12
use crate::shared::{ContentRange, Event, ParserState, PullParser, State, UnexpectedState};
13
use crate::slice_input_buffer::{InputBuffer, SliceInputBuffer};
14
use crate::ujson;
15
use ujson::{EventToken, Tokenizer};
16

17
use ujson::{BitStackConfig, DefaultConfig};
18

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

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

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

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

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

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

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

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

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

158
    fn have_events(&self) -> bool {
2,914✔
159
        have_events(&self.parser_state.evts)
2,914✔
160
    }
2,914✔
161

162
    /// Helper function to parse a number from the buffer given a start position.
163
    /// SliceParser handles all delimiter logic directly.
164
    fn parse_number_from_buffer(
31✔
165
        &mut self,
31✔
166
        start: usize,
31✔
167
        from_container_end: bool,
31✔
168
    ) -> Result<Event<'_, '_>, ParseError> {
31✔
169
        // SliceParser delimiter logic
170
        let current_pos = self.buffer.current_position();
31✔
171
        let end_pos = if !from_container_end && self.buffer.is_empty() {
31✔
172
            // At EOF - use full span
173
            current_pos
×
174
        } else {
175
            // All other cases - exclude delimiter
176
            current_pos.saturating_sub(1)
31✔
177
        };
178

179
        crate::number_parser::parse_number_event(&self.buffer, start, end_pos)
31✔
180
    }
31✔
181

182
    fn pull_tokenizer_events(&mut self) -> Result<(), ParseError> {
2,276✔
183
        use crate::slice_input_buffer::InputBuffer;
184
        if self.buffer.is_past_end() {
2,276✔
185
            return Err(ParseError::EndOfData);
×
186
        }
2,276✔
187
        let mut callback = create_tokenizer_callback(&mut self.parser_state.evts);
2,276✔
188

189
        let res = match self.buffer.consume_byte() {
2,276✔
190
            Err(crate::slice_input_buffer::Error::ReachedEnd) => {
191
                self.tokenizer.finish(&mut callback)
39✔
192
            }
193
            Err(err) => {
×
194
                return Err(err.into());
×
195
            }
196
            Ok(byte) => self.tokenizer.parse_chunk(&[byte], &mut callback),
2,237✔
197
        };
198

199
        if let Err(tokenizer_error) = res {
2,276✔
200
            return Err(ParseError::TokenizerError(tokenizer_error));
8✔
201
        }
2,268✔
202
        Ok(())
2,268✔
203
    }
2,276✔
204

205
    /// Returns the next JSON event or an error if parsing fails.
206
    /// Parsing continues until `EndDocument` is returned or an error occurs.
207
    fn next_event_impl(&mut self) -> Result<Event<'_, '_>, ParseError> {
345✔
208
        if self.buffer.is_past_end() {
345✔
209
            return Ok(Event::EndDocument);
2✔
210
        }
343✔
211
        loop {
212
            while !self.have_events() {
2,914✔
213
                self.pull_tokenizer_events()?;
2,276✔
214
                if self.buffer.is_past_end() {
2,268✔
215
                    return Ok(Event::EndDocument);
36✔
216
                }
2,232✔
217
            }
218
            // Find and move out the first available event to avoid holding mutable borrow during processing
219
            let taken_event = take_first_event(&mut self.parser_state.evts);
638✔
220

221
            if let Some(taken) = taken_event {
638✔
222
                let res = if let Some(simple_result) = process_simple_events(taken.clone()) {
638✔
223
                    simple_result
289✔
224
                } else if let Some(begin_result) = process_begin_events(&taken, self) {
349✔
225
                    begin_result
184✔
226
                } else {
227
                    match taken {
57✔
228
                        // Escape sequence handling using shared functions
229
                        ujson::Event::Begin(
230
                            escape_token @ (EventToken::EscapeQuote
20✔
231
                            | EventToken::EscapeBackslash
232
                            | EventToken::EscapeSlash
233
                            | EventToken::EscapeBackspace
234
                            | EventToken::EscapeFormFeed
235
                            | EventToken::EscapeNewline
236
                            | EventToken::EscapeCarriageReturn
237
                            | EventToken::EscapeTab),
238
                        ) => {
239
                            // Use shared escape processing
240
                            process_simple_escape_event(&escape_token, self)?;
20✔
241
                            EventResult::Continue
18✔
242
                        }
243
                        _ if process_unicode_escape_events(&taken, self)? => {
145✔
244
                            // Unicode escape events handled by shared function
245
                            EventResult::Continue
62✔
246
                        }
247
                        // EscapeSequence events (only emitted when flag is enabled, ignored in original parser)
248
                        ujson::Event::Begin(EventToken::EscapeSequence) => {
249
                            // Ignore in original parser since it uses slice-based parsing
250
                            EventResult::Continue
57✔
251
                        }
252
                        ujson::Event::End(EventToken::EscapeSequence) => {
253
                            // Ignore in original parser since it uses slice-based parsing
NEW
254
                            EventResult::Continue
×
255
                        }
256
                        ujson::Event::End(
257
                            EventToken::EscapeQuote
258
                            | EventToken::EscapeBackslash
259
                            | EventToken::EscapeSlash
260
                            | EventToken::EscapeBackspace
261
                            | EventToken::EscapeFormFeed
262
                            | EventToken::EscapeNewline
263
                            | EventToken::EscapeCarriageReturn
264
                            | EventToken::EscapeTab,
265
                        ) => {
266
                            // End of escape sequence - ignored here
267
                            EventResult::Continue
18✔
268
                        }
269
                        #[cfg(test)]
270
                        ujson::Event::Uninitialized => {
NEW
271
                            return Err(UnexpectedState::StateMismatch.into());
×
272
                        }
273
                        // All other events (container events, primitives, etc.) are handled by process_simple_events
NEW
274
                        _ => EventResult::Continue,
×
275
                    }
276
                };
277
                match res {
628✔
278
                    EventResult::Complete(event) => break Ok(event),
134✔
279
                    EventResult::Continue => continue,
339✔
280
                    EventResult::ExtractKey => {
281
                        break self.validate_and_extract_key();
74✔
282
                    }
283
                    EventResult::ExtractString => {
284
                        break self.validate_and_extract_string();
50✔
285
                    }
286
                    EventResult::ExtractNumber(from_container_end) => {
31✔
287
                        break self.validate_and_extract_number(from_container_end);
31✔
288
                    }
289
                }
290
            } else {
291
                // No event available - this shouldn't happen since we ensured have_events() above
292
                break Err(UnexpectedState::StateMismatch.into());
×
293
            }
294
        }
295
    }
345✔
296
}
297

298
impl<'a, 'b, C: BitStackConfig> ParserContext for SliceParser<'a, 'b, C> {
299
    fn current_position(&self) -> usize {
170✔
300
        self.buffer.current_pos()
170✔
301
    }
170✔
302

303
    fn begin_string_content(&mut self, pos: usize) {
137✔
304
        self.copy_on_escape.begin_string(pos);
137✔
305
    }
137✔
306

307
    fn set_parser_state(&mut self, state: State) {
170✔
308
        self.parser_state.state = state;
170✔
309
    }
170✔
310
}
311

312
impl<'a, 'b, C: BitStackConfig> ContentExtractor for SliceParser<'a, 'b, C> {
313
    fn parser_state_mut(&mut self) -> &mut State {
154✔
314
        &mut self.parser_state.state
154✔
315
    }
154✔
316

317
    fn extract_string_content(&mut self, _start_pos: usize) -> Result<Event<'_, '_>, ParseError> {
49✔
318
        // Use CopyOnEscape to get the final string result
319
        let end_pos = ContentRange::end_position_excluding_delimiter(self.buffer.current_pos());
49✔
320
        let value_result = self.copy_on_escape.end_string(end_pos)?;
49✔
321
        Ok(Event::String(value_result))
49✔
322
    }
49✔
323

324
    fn extract_key_content(&mut self, _start_pos: usize) -> Result<Event<'_, '_>, ParseError> {
74✔
325
        // Use CopyOnEscape to get the final key result
326
        let end_pos = ContentRange::end_position_excluding_delimiter(self.buffer.current_pos());
74✔
327
        let key_result = self.copy_on_escape.end_string(end_pos)?;
74✔
328
        Ok(Event::Key(key_result))
74✔
329
    }
74✔
330

331
    fn extract_number_content(
31✔
332
        &mut self,
31✔
333
        start_pos: usize,
31✔
334
        from_container_end: bool,
31✔
335
    ) -> Result<Event<'_, '_>, ParseError> {
31✔
336
        self.parse_number_from_buffer(start_pos, from_container_end)
31✔
337
    }
31✔
338
}
339

340
impl<'a, 'b, C: BitStackConfig> EscapeHandler for SliceParser<'a, 'b, C> {
341
    fn parser_state(&self) -> &State {
245✔
342
        &self.parser_state.state
245✔
343
    }
245✔
344

345
    fn reset_unicode_collector_all(&mut self) {
20✔
346
        self.unicode_escape_collector.reset_all();
20✔
347
    }
20✔
348

349
    fn reset_unicode_collector(&mut self) {
35✔
350
        self.unicode_escape_collector.reset();
35✔
351
    }
35✔
352

353
    fn has_pending_high_surrogate(&self) -> bool {
124✔
354
        self.unicode_escape_collector.has_pending_high_surrogate()
124✔
355
    }
124✔
356

357
    fn process_unicode_escape_with_collector(&mut self) -> Result<(), ParseError> {
35✔
358
        let current_pos = self.buffer.current_pos();
35✔
359
        let hex_slice_provider = |start, end| self.buffer.slice(start, end).map_err(Into::into);
35✔
360

361
        // Check if we had a pending high surrogate before processing
362
        let had_pending_high_surrogate = self.unicode_escape_collector.has_pending_high_surrogate();
35✔
363

364
        let mut utf8_buf = [0u8; 4];
35✔
365
        let (utf8_bytes_opt, escape_start_pos) =
27✔
366
            crate::escape_processor::process_unicode_escape_sequence(
35✔
367
                current_pos,
35✔
368
                &mut self.unicode_escape_collector,
35✔
369
                hex_slice_provider,
35✔
370
                &mut utf8_buf,
35✔
371
            )?;
8✔
372

373
        // Only process UTF-8 bytes if we have them (not a high surrogate waiting for low surrogate)
374
        if let Some(utf8_bytes) = utf8_bytes_opt {
27✔
375
            if had_pending_high_surrogate {
11✔
376
                // This is completing a surrogate pair - need to consume both escapes
377
                // First call: consume the high surrogate (6 bytes earlier)
378
                self.copy_on_escape
8✔
379
                    .handle_unicode_escape(escape_start_pos, &[])?;
8✔
380
                // Second call: consume the low surrogate and write UTF-8
381
                self.copy_on_escape
8✔
382
                    .handle_unicode_escape(escape_start_pos + 6, utf8_bytes)?;
8✔
383
            } else {
384
                // Single Unicode escape - normal processing
385
                self.copy_on_escape
3✔
386
                    .handle_unicode_escape(escape_start_pos, utf8_bytes)?;
3✔
387
            }
388
        }
16✔
389

390
        Ok(())
27✔
391
    }
35✔
392

393
    fn handle_simple_escape_char(&mut self, escape_char: u8) -> Result<(), ParseError> {
20✔
394
        self.copy_on_escape
20✔
395
            .handle_escape(self.buffer.current_pos(), escape_char)?;
20✔
396
        Ok(())
18✔
397
    }
20✔
398
}
399

400
impl<'a, 'b, C: BitStackConfig> PullParser for SliceParser<'a, 'b, C> {
401
    fn next_event(&mut self) -> Result<Event<'_, '_>, ParseError> {
345✔
402
        self.next_event_impl()
345✔
403
    }
345✔
404
}
405

406
#[cfg(test)]
407
mod tests {
408
    use super::*;
409
    use crate::{ArrayBitStack, BitStackStruct, String};
410

411
    #[test]
412
    fn make_parser() {
1✔
413
        let input = r#"{"key": "value"}"#;
1✔
414
        let mut scratch = [0u8; 1024];
1✔
415
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
416
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
417
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
418
        assert_eq!(
1✔
419
            parser.next_event(),
1✔
420
            Ok(Event::String(String::Borrowed("value")))
421
        );
422
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
423
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
424
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
425
    }
1✔
426

427
    #[test]
428
    fn parse_number() {
1✔
429
        let input = r#"{"key": 124}"#;
1✔
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
        // Check number value using new JsonNumber API
435
        match parser.next_event() {
1✔
436
            Ok(Event::Number(num)) => {
1✔
437
                assert_eq!(num.as_str(), "124");
1✔
438
                assert_eq!(num.as_int(), Some(124));
1✔
439
            }
440
            other => panic!("Expected Number, got: {:?}", other),
×
441
        }
442
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
443
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
444
    }
1✔
445

446
    #[test]
447
    fn parse_bool_and_null() {
1✔
448
        let input = r#"{"key": true, "key2": false, "key3": null}"#;
1✔
449
        let mut scratch = [0u8; 1024];
1✔
450
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
451
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
452
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
453
        assert_eq!(parser.next_event(), Ok(Event::Bool(true)));
1✔
454
        assert_eq!(
1✔
455
            parser.next_event(),
1✔
456
            Ok(Event::Key(String::Borrowed("key2")))
457
        );
458
        assert_eq!(parser.next_event(), Ok(Event::Bool(false)));
1✔
459
        assert_eq!(
1✔
460
            parser.next_event(),
1✔
461
            Ok(Event::Key(String::Borrowed("key3")))
462
        );
463
        assert_eq!(parser.next_event(), Ok(Event::Null));
1✔
464
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
465
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
466
    }
1✔
467

468
    #[test]
469
    fn parse_array() {
1✔
470
        #[cfg(feature = "float-error")]
471
        let input = r#"{"key": [1, 2, 3]}"#; // No floats for float-error config
472
        #[cfg(not(feature = "float-error"))]
473
        let input = r#"{"key": [1, 2.2, 3]}"#; // Include float for other configs
1✔
474

475
        let mut scratch = [0u8; 1024];
1✔
476
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
477
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
478
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
479
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
480

481
        // First number: 1 (integer)
482
        match parser.next_event() {
1✔
483
            Ok(Event::Number(num)) => {
1✔
484
                assert_eq!(num.as_str(), "1");
1✔
485
                assert_eq!(num.as_int(), Some(1));
1✔
486
            }
487
            other => panic!("Expected Number(1), got: {:?}", other),
×
488
        }
489

490
        // Second number: depends on configuration
491
        match parser.next_event() {
1✔
492
            Ok(Event::Number(num)) => {
1✔
493
                #[cfg(feature = "float-error")]
494
                {
495
                    assert_eq!(num.as_str(), "2");
496
                    assert_eq!(num.as_int(), Some(2));
497
                }
498
                #[cfg(not(feature = "float-error"))]
499
                {
500
                    assert_eq!(num.as_str(), "2.2");
1✔
501
                    #[cfg(feature = "float")]
502
                    assert_eq!(num.as_f64(), Some(2.2));
1✔
503
                    #[cfg(not(feature = "float-error"))]
504
                    assert!(num.is_float());
1✔
505
                }
506
            }
507
            other => panic!("Expected Number, got: {:?}", other),
×
508
        }
509

510
        // Third number: 3 (integer)
511
        match parser.next_event() {
1✔
512
            Ok(Event::Number(num)) => {
1✔
513
                assert_eq!(num.as_str(), "3");
1✔
514
                assert_eq!(num.as_int(), Some(3));
1✔
515
            }
516
            other => panic!("Expected Number(3), got: {:?}", other),
×
517
        }
518

519
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
520
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
521
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
522
    }
1✔
523

524
    #[test]
525
    fn test_simple_parser_api() {
1✔
526
        let input = r#"{"name": "test"}"#;
1✔
527
        let mut scratch = [0u8; 1024];
1✔
528
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
529

530
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
531
        assert_eq!(
1✔
532
            parser.next_event(),
1✔
533
            Ok(Event::Key(String::Borrowed("name")))
534
        );
535
        assert_eq!(
1✔
536
            parser.next_event(),
1✔
537
            Ok(Event::String(String::Borrowed("test")))
538
        );
539
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
540
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
541
    }
1✔
542

543
    #[test]
544
    fn test_parser_with_escaped_strings() {
1✔
545
        // Use regular string literal to properly include escape sequences
546
        let input = "{\"name\": \"John\\nDoe\", \"message\": \"Hello\\tWorld!\"}";
1✔
547
        let mut scratch = [0u8; 1024];
1✔
548
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
549

550
        // Test that the parser correctly handles escaped strings
551
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
552

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

562
        // Value should have escapes -> Unescaped
563
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
564
            assert_eq!(&*value, "John\nDoe");
1✔
565
            // This should be the slow path (unescaped)
566
            assert!(matches!(value, String::Unescaped(_)));
1✔
567
        } else {
568
            panic!("Expected String event");
×
569
        }
570

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

579
        // Second value should have escapes -> Unescaped
580
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
581
            assert_eq!(&*value, "Hello\tWorld!");
1✔
582
            assert!(matches!(value, String::Unescaped(_)));
1✔
583
        } else {
584
            panic!("Expected String event");
×
585
        }
586

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

590
    #[test]
591
    fn test_copy_on_escape_optimization() {
1✔
592
        // Use regular string literal to include proper escape sequences
593
        let input = "{\"simple\": \"no escapes\", \"complex\": \"has\\nescapes\"}";
1✔
594
        let mut scratch = [0u8; 1024];
1✔
595
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
596

597
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
598

599
        // "simple" key should be borrowed (fast path)
600
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
601
            assert_eq!(&*key, "simple");
1✔
602
            assert!(matches!(key, String::Borrowed(_)));
1✔
603
        } else {
604
            panic!("Expected Key event");
×
605
        }
606

607
        // "no escapes" value should be borrowed (fast path)
608
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
609
            assert_eq!(&*value, "no escapes");
1✔
610
            assert!(matches!(value, String::Borrowed(_)));
1✔
611
        } else {
612
            panic!("Expected String event");
×
613
        }
614

615
        // "complex" key should be borrowed (fast path)
616
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
617
            assert_eq!(&*key, "complex");
1✔
618
            assert!(matches!(key, String::Borrowed(_)));
1✔
619
        } else {
620
            panic!("Expected Key event");
×
621
        }
622

623
        // "has\\nescapes" value should be unescaped (slow path)
624
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
625
            assert_eq!(&*value, "has\nescapes");
1✔
626
            assert!(matches!(value, String::Unescaped(_)));
1✔
627
        } else {
628
            panic!("Expected String event");
×
629
        }
630

631
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
632
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
633
    }
1✔
634

635
    #[test]
636
    fn test_coe2_integration_multiple_escapes() {
1✔
637
        let input = r#"{"key": "a\nb\tc\rd"}"#;
1✔
638
        let mut scratch = [0u8; 1024];
1✔
639
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
640

641
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
642
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
643

644
        let string_event = parser.next_event().unwrap();
1✔
645
        match string_event {
1✔
646
            Event::String(String::Unescaped(s)) => {
1✔
647
                assert_eq!(s, "a\nb\tc\rd");
1✔
648
            }
649
            _ => panic!("Expected unescaped string value, got: {:?}", string_event),
×
650
        }
651
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
652
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
653
    }
1✔
654

655
    #[test]
656
    fn test_coe2_integration_zero_copy_path() {
1✔
657
        let input = r#"{"simple": "no_escapes_here"}"#;
1✔
658
        let mut scratch = [0u8; 1024];
1✔
659
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
660

661
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
662
        assert_eq!(
1✔
663
            parser.next_event(),
1✔
664
            Ok(Event::Key(String::Borrowed("simple")))
665
        );
666

667
        // This should be borrowed (zero-copy) since no escapes
668
        let string_event = parser.next_event().unwrap();
1✔
669
        match string_event {
1✔
670
            Event::String(String::Borrowed(s)) => {
1✔
671
                assert_eq!(s, "no_escapes_here");
1✔
672
            }
673
            _ => panic!(
×
674
                "Expected borrowed string value for zero-copy, got: {:?}",
×
675
                string_event
676
            ),
677
        }
678
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
679
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
680
    }
1✔
681

682
    #[test]
683
    fn test_coe2_integration_mixed_strings() {
1✔
684
        let input = r#"["plain", "with\nescapes", "plain2", "more\tescapes"]"#;
1✔
685
        let mut scratch = [0u8; 1024];
1✔
686
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
687

688
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
689

690
        // First string: no escapes -> borrowed
691
        match parser.next_event().unwrap() {
1✔
692
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain"),
1✔
693
            other => panic!("Expected borrowed string, got: {:?}", other),
×
694
        }
695

696
        // Second string: has escapes -> unescaped
697
        match parser.next_event().unwrap() {
1✔
698
            Event::String(String::Unescaped(s)) => assert_eq!(s, "with\nescapes"),
1✔
699
            other => panic!("Expected unescaped string, got: {:?}", other),
×
700
        }
701

702
        // Third string: no escapes -> borrowed
703
        match parser.next_event().unwrap() {
1✔
704
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain2"),
1✔
705
            other => panic!("Expected borrowed string, got: {:?}", other),
×
706
        }
707

708
        // Fourth string: has escapes -> unescaped
709
        match parser.next_event().unwrap() {
1✔
710
            Event::String(String::Unescaped(s)) => assert_eq!(s, "more\tescapes"),
1✔
711
            other => panic!("Expected unescaped string, got: {:?}", other),
×
712
        }
713

714
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
715
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
716
    }
1✔
717

718
    #[test]
719
    fn test_unicode_escape_integration() {
1✔
720
        let input = r#"{"key": "Hello\u0041World"}"#; // \u0041 = 'A'
1✔
721
        let mut scratch = [0u8; 1024];
1✔
722
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
723

724
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
725
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
726

727
        // The string with Unicode escape should be unescaped
728
        match parser.next_event().unwrap() {
1✔
729
            Event::String(String::Unescaped(s)) => {
1✔
730
                assert_eq!(s, "HelloAWorld");
1✔
731
            }
732
            other => panic!("Expected unescaped string value, got: {:?}", other),
×
733
        }
734

735
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
736
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
737
    }
1✔
738

739
    #[test]
740
    fn test_original_parser_escape_trace() {
1✔
741
        // Test escape sequence processing with logging
742
        let input = r#""a\nb""#;
1✔
743
        let mut scratch = [0u8; 1024];
1✔
744
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
745

746
        // Should get String with unescaped content
747
        let event = parser.next_event().unwrap();
1✔
748
        if let Event::String(s) = event {
1✔
749
            assert_eq!(&*s, "a\nb");
1✔
750
        } else {
751
            panic!("Expected String event, got {:?}", event);
×
752
        }
753

754
        // Should get EndDocument
755
        let event = parser.next_event().unwrap();
1✔
756
        assert_eq!(event, Event::EndDocument);
1✔
757
    }
1✔
758

759
    #[test]
760
    fn make_parser_from_slice() {
1✔
761
        let input = br#"{"key": "value"}"#;
1✔
762
        let mut scratch = [0u8; 1024];
1✔
763
        let mut parser = SliceParser::with_buffer_from_slice(input, &mut scratch);
1✔
764
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
765
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
766
        assert_eq!(
1✔
767
            parser.next_event(),
1✔
768
            Ok(Event::String(String::Borrowed("value")))
769
        );
770
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
771
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
772
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
773
    }
1✔
774

775
    #[test]
776
    fn test_with_config_constructors() {
1✔
777
        // Test with_config constructor (no escapes)
778
        let json = r#"{"simple": "no_escapes"}"#;
1✔
779
        let mut parser = SliceParser::<BitStackStruct<u64, u16>>::with_config(json);
1✔
780

781
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
782
        assert_eq!(
1✔
783
            parser.next_event(),
1✔
784
            Ok(Event::Key(String::Borrowed("simple")))
785
        );
786
        assert_eq!(
1✔
787
            parser.next_event(),
1✔
788
            Ok(Event::String(String::Borrowed("no_escapes")))
789
        );
790
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
791
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
792
    }
1✔
793

794
    #[test]
795
    fn test_with_config_and_buffer_constructors() {
1✔
796
        // Test with_config_and_buffer constructor (with escapes)
797
        let json = r#"{"escaped": "hello\nworld"}"#;
1✔
798
        let mut scratch = [0u8; 64];
1✔
799
        let mut parser =
1✔
800
            SliceParser::<BitStackStruct<u64, u16>>::with_config_and_buffer(json, &mut scratch);
1✔
801

802
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
803
        assert_eq!(
1✔
804
            parser.next_event(),
1✔
805
            Ok(Event::Key(String::Borrowed("escaped")))
806
        );
807

808
        if let Ok(Event::String(s)) = parser.next_event() {
1✔
809
            assert_eq!(s.as_ref(), "hello\nworld"); // Escape should be processed
1✔
810
        } else {
811
            panic!("Expected String event");
×
812
        }
813

814
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
815
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
816
    }
1✔
817

818
    #[test]
819
    fn test_alternative_config_deep_nesting() {
1✔
820
        // Test that custom BitStack configs can handle deeper nesting
821
        let json = r#"{"a":{"b":{"c":{"d":{"e":"deep"}}}}}"#;
1✔
822
        let mut scratch = [0u8; 64];
1✔
823
        let mut parser =
1✔
824
            SliceParser::<ArrayBitStack<8, u32, u16>>::with_config_and_buffer(json, &mut scratch);
1✔
825

826
        // Parse the deep structure
827
        let mut depth = 0;
1✔
828
        while let Ok(event) = parser.next_event() {
17✔
829
            match event {
17✔
830
                Event::StartObject => depth += 1,
5✔
831
                Event::EndObject => depth -= 1,
5✔
832
                Event::EndDocument => break,
1✔
833
                _ => {}
6✔
834
            }
835
        }
836

837
        // Should have successfully parsed a 5-level deep structure
838
        assert_eq!(depth, 0); // All objects should be closed
1✔
839
    }
1✔
840
}
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