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

kaidokert / picojson-rs / 16244858033

13 Jul 2025 03:26AM UTC coverage: 93.57% (-0.1%) from 93.685%
16244858033

Pull #55

github

web-flow
Merge 1fc39d919 into d470170ad
Pull Request #55: Big refactor

520 of 582 new or added lines in 5 files covered. (89.35%)

2 existing lines in 2 files now uncovered.

4700 of 5023 relevant lines covered (93.57%)

675.53 hits per line

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

90.52
/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
        loop {
167
            while !have_events(&self.parser_state.evts) {
2,914✔
168
                if !self.pull_tokenizer_events()? {
2,276✔
169
                    return Ok(Event::EndDocument);
36✔
170
                }
2,232✔
171
            }
172
            // Find and move out the first available event to avoid holding mutable borrow during processing
173
            let taken_event = take_first_event(&mut self.parser_state.evts);
638✔
174

175
            if let Some(taken) = taken_event {
638✔
176
                // Process the event directly in the main loop using immediate processing pattern
177
                // First, try the shared simple event processor
178
                if let Some(simple_result) = process_simple_events(taken.clone()) {
638✔
179
                    match simple_result {
289✔
180
                        EventResult::Complete(event) => break Ok(event),
134✔
181
                        EventResult::ExtractString => break self.validate_and_extract_string(),
50✔
182
                        EventResult::ExtractKey => break self.validate_and_extract_key(),
74✔
183
                        EventResult::ExtractNumber(from_container_end) => {
31✔
184
                            break self.validate_and_extract_number(from_container_end)
31✔
185
                        }
NEW
186
                        EventResult::Continue => {
×
NEW
187
                            // Continue to next iteration for more events
×
NEW
188
                        }
×
189
                    }
190
                } else if let Some(begin_result) = process_begin_events(&taken, self) {
349✔
191
                    match begin_result {
184✔
NEW
192
                        EventResult::Complete(event) => break Ok(event),
×
NEW
193
                        EventResult::ExtractString => break self.validate_and_extract_string(),
×
NEW
194
                        EventResult::ExtractKey => break self.validate_and_extract_key(),
×
NEW
195
                        EventResult::ExtractNumber(from_container_end) => {
×
NEW
196
                            break self.validate_and_extract_number(from_container_end)
×
197
                        }
198
                        EventResult::Continue => {
184✔
199
                            // Continue to next iteration for more events
184✔
200
                        }
184✔
201
                    }
202
                } else {
203
                    match taken {
20✔
204
                        // Shared escape sequence lifecycle events (common pattern #1)
205
                        ujson::Event::Begin(EventToken::EscapeSequence) => {
206
                            // Use shared lifecycle interface for escape sequence start
207
                            process_begin_escape_sequence_event(self)?;
57✔
208
                            // Continue to next iteration
209
                        }
210
                        // Shared Unicode escape processing (common pattern #2)
211
                        _ if process_unicode_escape_events(&taken, self)? => {
108✔
212
                            // Unicode escape events handled by shared function
62✔
213
                            // Continue to next iteration
62✔
214
                        }
62✔
215
                        // Simple escape sequence handling - Begin events (SliceParser-specific)
216
                        ujson::Event::Begin(
217
                            escape_token @ (EventToken::EscapeQuote
20✔
218
                            | EventToken::EscapeBackslash
219
                            | EventToken::EscapeSlash
220
                            | EventToken::EscapeBackspace
221
                            | EventToken::EscapeFormFeed
222
                            | EventToken::EscapeNewline
223
                            | EventToken::EscapeCarriageReturn
224
                            | EventToken::EscapeTab),
225
                        ) => {
226
                            // Use shared escape processing
227
                            process_simple_escape_event(&escape_token, self)?;
20✔
228
                            // Continue to next iteration
229
                        }
230
                        // Escape sequence lifecycle - End events (SliceParser-specific)
NEW
231
                        ujson::Event::End(EventToken::EscapeSequence) => {
×
NEW
232
                            // Ignore in original parser since it uses slice-based parsing
×
NEW
233
                            // Continue to next iteration
×
NEW
234
                        }
×
235
                        // All other events (container events, primitives, etc.) are handled by process_simple_events
236
                        _ => {
18✔
237
                            // Continue to next iteration
18✔
238
                        }
18✔
239
                    }
240
                }
241
            } else {
242
                // No event available - this shouldn't happen since we ensured have_events() above
NEW
243
                break Err(UnexpectedState::StateMismatch.into());
×
244
            }
245
        }
246
    }
345✔
247

248
    /// Pull events from tokenizer and return whether parsing should continue
249
    /// Returns false when past end (equivalent to self.buffer.is_past_end())
250
    fn pull_tokenizer_events(&mut self) -> Result<bool, ParseError> {
2,276✔
251
        if self.buffer.is_past_end() {
2,276✔
NEW
252
            return Ok(false); // Indicate end state instead of error
×
253
        }
2,276✔
254
        // Use ByteProvider implementation to get the next byte and process it
255
        if let Some(byte) = self.next_byte()? {
2,276✔
256
            process_byte_through_tokenizer(byte, &mut self.tokenizer, &mut self.parser_state.evts)?;
2,237✔
257
        } else {
258
            finish_tokenizer(&mut self.tokenizer, &mut self.parser_state.evts)?;
39✔
259
        }
260
        Ok(!self.buffer.is_past_end()) // Return continue state
2,268✔
261
    }
2,276✔
262
}
263

264
impl<'a, 'b, C: BitStackConfig> ContentExtractor for SliceParser<'a, 'b, C> {
265
    fn parser_state_mut(&mut self) -> &mut State {
324✔
266
        &mut self.parser_state.state
324✔
267
    }
324✔
268

269
    fn current_position(&self) -> usize {
170✔
270
        self.buffer.current_pos()
170✔
271
    }
170✔
272

273
    fn begin_string_content(&mut self, pos: usize) {
137✔
274
        self.copy_on_escape.begin_string(pos);
137✔
275
    }
137✔
276

277
    fn unicode_escape_collector_mut(
179✔
278
        &mut self,
179✔
279
    ) -> &mut crate::escape_processor::UnicodeEscapeCollector {
179✔
280
        &mut self.unicode_escape_collector
179✔
281
    }
179✔
282

283
    fn extract_string_content(&mut self, _start_pos: usize) -> Result<Event<'_, '_>, ParseError> {
49✔
284
        // Use CopyOnEscape to get the final string result
285
        let end_pos = ContentRange::end_position_excluding_delimiter(self.buffer.current_pos());
49✔
286
        let value_result = self.copy_on_escape.end_string(end_pos)?;
49✔
287
        Ok(Event::String(value_result))
49✔
288
    }
49✔
289

290
    fn extract_key_content(&mut self, _start_pos: usize) -> Result<Event<'_, '_>, ParseError> {
74✔
291
        // Use CopyOnEscape to get the final key result
292
        let end_pos = ContentRange::end_position_excluding_delimiter(self.buffer.current_pos());
74✔
293
        let key_result = self.copy_on_escape.end_string(end_pos)?;
74✔
294
        Ok(Event::Key(key_result))
74✔
295
    }
74✔
296

297
    fn extract_number_content(
31✔
298
        &mut self,
31✔
299
        start_pos: usize,
31✔
300
        from_container_end: bool,
31✔
301
    ) -> Result<Event<'_, '_>, ParseError> {
31✔
302
        // Use shared number parsing with SliceParser-specific document end detection
303
        let at_document_end = self.buffer.is_empty();
31✔
304
        crate::number_parser::parse_number_with_delimiter_logic(
31✔
305
            &self.buffer,
31✔
306
            start_pos,
31✔
307
            from_container_end,
31✔
308
            at_document_end,
31✔
309
        )
310
    }
31✔
311
}
312

313
impl<'a, 'b, C: BitStackConfig> EscapeHandler for SliceParser<'a, 'b, C> {
314
    fn parser_state(&self) -> &State {
302✔
315
        &self.parser_state.state
302✔
316
    }
302✔
317

318
    fn process_unicode_escape_with_collector(&mut self) -> Result<(), ParseError> {
35✔
319
        let current_pos = self.buffer.current_pos();
35✔
320
        let hex_slice_provider = |start, end| self.buffer.slice(start, end).map_err(Into::into);
35✔
321

322
        // Shared Unicode escape processing pattern
323
        let had_pending_high_surrogate = self.unicode_escape_collector.has_pending_high_surrogate();
35✔
324

325
        let mut utf8_buf = [0u8; 4];
35✔
326
        let (utf8_bytes_opt, escape_start_pos) =
27✔
327
            crate::escape_processor::process_unicode_escape_sequence(
35✔
328
                current_pos,
35✔
329
                &mut self.unicode_escape_collector,
35✔
330
                hex_slice_provider,
35✔
331
                &mut utf8_buf,
35✔
332
            )?;
8✔
333

334
        // Handle UTF-8 bytes if we have them (not a high surrogate waiting for low surrogate)
335
        if let Some(utf8_bytes) = utf8_bytes_opt {
27✔
336
            if had_pending_high_surrogate {
11✔
337
                // This is completing a surrogate pair - need to consume both escapes
338
                // First call: consume the high surrogate (6 bytes earlier)
339
                self.copy_on_escape
8✔
340
                    .handle_unicode_escape(escape_start_pos, &[])?;
8✔
341
                // Second call: consume the low surrogate and write UTF-8
342
                self.copy_on_escape
8✔
343
                    .handle_unicode_escape(escape_start_pos + 6, utf8_bytes)?;
8✔
344
            } else {
345
                // Single Unicode escape - normal processing
346
                self.copy_on_escape
3✔
347
                    .handle_unicode_escape(escape_start_pos, utf8_bytes)?;
3✔
348
            }
349
        }
16✔
350

351
        Ok(())
27✔
352
    }
35✔
353

354
    fn handle_simple_escape_char(&mut self, escape_char: u8) -> Result<(), ParseError> {
20✔
355
        self.copy_on_escape
20✔
356
            .handle_escape(self.buffer.current_pos(), escape_char)?;
20✔
357
        Ok(())
18✔
358
    }
20✔
359

360
    /// Append a single literal byte - implement as single-byte range for consistency
NEW
361
    fn append_literal_byte(&mut self, _byte: u8) -> Result<(), ParseError> {
×
362
        // SliceParser doesn't typically need per-byte processing since it works with ranges
363
        // This could be implemented as a single-byte range if needed, but for now it's a no-op
NEW
364
        Ok(())
×
UNCOV
365
    }
×
366
}
367

368
impl<'a, 'b, C: BitStackConfig> PullParser for SliceParser<'a, 'b, C> {
369
    fn next_event(&mut self) -> Result<Event<'_, '_>, ParseError> {
345✔
370
        self.next_event_impl()
345✔
371
    }
345✔
372
}
373

374
impl<'a, 'b, C: BitStackConfig> crate::shared::ByteProvider for SliceParser<'a, 'b, C> {
375
    fn next_byte(&mut self) -> Result<Option<u8>, ParseError> {
2,276✔
376
        use crate::slice_input_buffer::InputBuffer;
377
        match self.buffer.consume_byte() {
2,276✔
378
            Ok(byte) => Ok(Some(byte)),
2,237✔
379
            Err(crate::slice_input_buffer::Error::ReachedEnd) => Ok(None),
39✔
NEW
380
            Err(err) => Err(err.into()),
×
381
        }
382
    }
2,276✔
383
}
384

385
#[cfg(test)]
386
mod tests {
387
    use super::*;
388
    use crate::{ArrayBitStack, BitStackStruct, String};
389

390
    #[test]
391
    fn make_parser() {
1✔
392
        let input = r#"{"key": "value"}"#;
1✔
393
        let mut scratch = [0u8; 1024];
1✔
394
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
395
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
396
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
397
        assert_eq!(
1✔
398
            parser.next_event(),
1✔
399
            Ok(Event::String(String::Borrowed("value")))
400
        );
401
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
402
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
403
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
404
    }
1✔
405

406
    #[test]
407
    fn parse_number() {
1✔
408
        let input = r#"{"key": 124}"#;
1✔
409
        let mut scratch = [0u8; 1024];
1✔
410
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
411
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
412
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
413
        // Check number value using new JsonNumber API
414
        match parser.next_event() {
1✔
415
            Ok(Event::Number(num)) => {
1✔
416
                assert_eq!(num.as_str(), "124");
1✔
417
                assert_eq!(num.as_int(), Some(124));
1✔
418
            }
419
            other => panic!("Expected Number, got: {:?}", other),
×
420
        }
421
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
422
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
423
    }
1✔
424

425
    #[test]
426
    fn parse_bool_and_null() {
1✔
427
        let input = r#"{"key": true, "key2": false, "key3": null}"#;
1✔
428
        let mut scratch = [0u8; 1024];
1✔
429
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
430
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
431
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
432
        assert_eq!(parser.next_event(), Ok(Event::Bool(true)));
1✔
433
        assert_eq!(
1✔
434
            parser.next_event(),
1✔
435
            Ok(Event::Key(String::Borrowed("key2")))
436
        );
437
        assert_eq!(parser.next_event(), Ok(Event::Bool(false)));
1✔
438
        assert_eq!(
1✔
439
            parser.next_event(),
1✔
440
            Ok(Event::Key(String::Borrowed("key3")))
441
        );
442
        assert_eq!(parser.next_event(), Ok(Event::Null));
1✔
443
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
444
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
445
    }
1✔
446

447
    #[test]
448
    fn parse_array() {
1✔
449
        #[cfg(feature = "float-error")]
450
        let input = r#"{"key": [1, 2, 3]}"#; // No floats for float-error config
451
        #[cfg(not(feature = "float-error"))]
452
        let input = r#"{"key": [1, 2.2, 3]}"#; // Include float for other configs
1✔
453

454
        let mut scratch = [0u8; 1024];
1✔
455
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
456
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
457
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
458
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
459

460
        // First number: 1 (integer)
461
        match parser.next_event() {
1✔
462
            Ok(Event::Number(num)) => {
1✔
463
                assert_eq!(num.as_str(), "1");
1✔
464
                assert_eq!(num.as_int(), Some(1));
1✔
465
            }
466
            other => panic!("Expected Number(1), got: {:?}", other),
×
467
        }
468

469
        // Second number: depends on configuration
470
        match parser.next_event() {
1✔
471
            Ok(Event::Number(num)) => {
1✔
472
                #[cfg(feature = "float-error")]
473
                {
474
                    assert_eq!(num.as_str(), "2");
475
                    assert_eq!(num.as_int(), Some(2));
476
                }
477
                #[cfg(not(feature = "float-error"))]
478
                {
479
                    assert_eq!(num.as_str(), "2.2");
1✔
480
                    #[cfg(feature = "float")]
481
                    assert_eq!(num.as_f64(), Some(2.2));
1✔
482
                    #[cfg(not(feature = "float-error"))]
483
                    assert!(num.is_float());
1✔
484
                }
485
            }
486
            other => panic!("Expected Number, got: {:?}", other),
×
487
        }
488

489
        // Third number: 3 (integer)
490
        match parser.next_event() {
1✔
491
            Ok(Event::Number(num)) => {
1✔
492
                assert_eq!(num.as_str(), "3");
1✔
493
                assert_eq!(num.as_int(), Some(3));
1✔
494
            }
495
            other => panic!("Expected Number(3), got: {:?}", other),
×
496
        }
497

498
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
499
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
500
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
501
    }
1✔
502

503
    #[test]
504
    fn test_simple_parser_api() {
1✔
505
        let input = r#"{"name": "test"}"#;
1✔
506
        let mut scratch = [0u8; 1024];
1✔
507
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
508

509
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
510
        assert_eq!(
1✔
511
            parser.next_event(),
1✔
512
            Ok(Event::Key(String::Borrowed("name")))
513
        );
514
        assert_eq!(
1✔
515
            parser.next_event(),
1✔
516
            Ok(Event::String(String::Borrowed("test")))
517
        );
518
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
519
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
520
    }
1✔
521

522
    #[test]
523
    fn test_parser_with_escaped_strings() {
1✔
524
        // Use regular string literal to properly include escape sequences
525
        let input = "{\"name\": \"John\\nDoe\", \"message\": \"Hello\\tWorld!\"}";
1✔
526
        let mut scratch = [0u8; 1024];
1✔
527
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
528

529
        // Test that the parser correctly handles escaped strings
530
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
531

532
        // Key should be simple (no escapes) -> Borrowed
533
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
534
            assert_eq!(&*key, "name");
1✔
535
            // This should be the fast path (borrowed)
536
            assert!(matches!(key, String::Borrowed(_)));
1✔
537
        } else {
538
            panic!("Expected Key event");
×
539
        }
540

541
        // Value should have escapes -> Unescaped
542
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
543
            assert_eq!(&*value, "John\nDoe");
1✔
544
            // This should be the slow path (unescaped)
545
            assert!(matches!(value, String::Unescaped(_)));
1✔
546
        } else {
547
            panic!("Expected String event");
×
548
        }
549

550
        // Second key should be simple -> Borrowed
551
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
552
            assert_eq!(&*key, "message");
1✔
553
            assert!(matches!(key, String::Borrowed(_)));
1✔
554
        } else {
555
            panic!("Expected Key event");
×
556
        }
557

558
        // Second value should have escapes -> Unescaped
559
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
560
            assert_eq!(&*value, "Hello\tWorld!");
1✔
561
            assert!(matches!(value, String::Unescaped(_)));
1✔
562
        } else {
563
            panic!("Expected String event");
×
564
        }
565

566
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
567
    }
1✔
568

569
    #[test]
570
    fn test_copy_on_escape_optimization() {
1✔
571
        // Use regular string literal to include proper escape sequences
572
        let input = "{\"simple\": \"no escapes\", \"complex\": \"has\\nescapes\"}";
1✔
573
        let mut scratch = [0u8; 1024];
1✔
574
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
575

576
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
577

578
        // "simple" key should be borrowed (fast path)
579
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
580
            assert_eq!(&*key, "simple");
1✔
581
            assert!(matches!(key, String::Borrowed(_)));
1✔
582
        } else {
583
            panic!("Expected Key event");
×
584
        }
585

586
        // "no escapes" value should be borrowed (fast path)
587
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
588
            assert_eq!(&*value, "no escapes");
1✔
589
            assert!(matches!(value, String::Borrowed(_)));
1✔
590
        } else {
591
            panic!("Expected String event");
×
592
        }
593

594
        // "complex" key should be borrowed (fast path)
595
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
596
            assert_eq!(&*key, "complex");
1✔
597
            assert!(matches!(key, String::Borrowed(_)));
1✔
598
        } else {
599
            panic!("Expected Key event");
×
600
        }
601

602
        // "has\\nescapes" value should be unescaped (slow path)
603
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
604
            assert_eq!(&*value, "has\nescapes");
1✔
605
            assert!(matches!(value, String::Unescaped(_)));
1✔
606
        } else {
607
            panic!("Expected String event");
×
608
        }
609

610
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
611
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
612
    }
1✔
613

614
    #[test]
615
    fn test_coe2_integration_multiple_escapes() {
1✔
616
        let input = r#"{"key": "a\nb\tc\rd"}"#;
1✔
617
        let mut scratch = [0u8; 1024];
1✔
618
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
619

620
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
621
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
622

623
        let string_event = parser.next_event().unwrap();
1✔
624
        match string_event {
1✔
625
            Event::String(String::Unescaped(s)) => {
1✔
626
                assert_eq!(s, "a\nb\tc\rd");
1✔
627
            }
628
            _ => panic!("Expected unescaped string value, got: {:?}", string_event),
×
629
        }
630
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
631
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
632
    }
1✔
633

634
    #[test]
635
    fn test_coe2_integration_zero_copy_path() {
1✔
636
        let input = r#"{"simple": "no_escapes_here"}"#;
1✔
637
        let mut scratch = [0u8; 1024];
1✔
638
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
639

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

646
        // This should be borrowed (zero-copy) since no escapes
647
        let string_event = parser.next_event().unwrap();
1✔
648
        match string_event {
1✔
649
            Event::String(String::Borrowed(s)) => {
1✔
650
                assert_eq!(s, "no_escapes_here");
1✔
651
            }
652
            _ => panic!(
×
653
                "Expected borrowed string value for zero-copy, got: {:?}",
×
654
                string_event
655
            ),
656
        }
657
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
658
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
659
    }
1✔
660

661
    #[test]
662
    fn test_coe2_integration_mixed_strings() {
1✔
663
        let input = r#"["plain", "with\nescapes", "plain2", "more\tescapes"]"#;
1✔
664
        let mut scratch = [0u8; 1024];
1✔
665
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
666

667
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
668

669
        // First string: no escapes -> borrowed
670
        match parser.next_event().unwrap() {
1✔
671
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain"),
1✔
672
            other => panic!("Expected borrowed string, got: {:?}", other),
×
673
        }
674

675
        // Second string: has escapes -> unescaped
676
        match parser.next_event().unwrap() {
1✔
677
            Event::String(String::Unescaped(s)) => assert_eq!(s, "with\nescapes"),
1✔
678
            other => panic!("Expected unescaped string, got: {:?}", other),
×
679
        }
680

681
        // Third string: no escapes -> borrowed
682
        match parser.next_event().unwrap() {
1✔
683
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain2"),
1✔
684
            other => panic!("Expected borrowed string, got: {:?}", other),
×
685
        }
686

687
        // Fourth string: has escapes -> unescaped
688
        match parser.next_event().unwrap() {
1✔
689
            Event::String(String::Unescaped(s)) => assert_eq!(s, "more\tescapes"),
1✔
690
            other => panic!("Expected unescaped string, got: {:?}", other),
×
691
        }
692

693
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
694
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
695
    }
1✔
696

697
    #[test]
698
    fn test_unicode_escape_integration() {
1✔
699
        let input = r#"{"key": "Hello\u0041World"}"#; // \u0041 = 'A'
1✔
700
        let mut scratch = [0u8; 1024];
1✔
701
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
702

703
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
704
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
705

706
        // The string with Unicode escape should be unescaped
707
        match parser.next_event().unwrap() {
1✔
708
            Event::String(String::Unescaped(s)) => {
1✔
709
                assert_eq!(s, "HelloAWorld");
1✔
710
            }
711
            other => panic!("Expected unescaped string value, got: {:?}", other),
×
712
        }
713

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

718
    #[test]
719
    fn test_original_parser_escape_trace() {
1✔
720
        // Test escape sequence processing with logging
721
        let input = r#""a\nb""#;
1✔
722
        let mut scratch = [0u8; 1024];
1✔
723
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
724

725
        // Should get String with unescaped content
726
        let event = parser.next_event().unwrap();
1✔
727
        if let Event::String(s) = event {
1✔
728
            assert_eq!(&*s, "a\nb");
1✔
729
        } else {
730
            panic!("Expected String event, got {:?}", event);
×
731
        }
732

733
        // Should get EndDocument
734
        let event = parser.next_event().unwrap();
1✔
735
        assert_eq!(event, Event::EndDocument);
1✔
736
    }
1✔
737

738
    #[test]
739
    fn make_parser_from_slice() {
1✔
740
        let input = br#"{"key": "value"}"#;
1✔
741
        let mut scratch = [0u8; 1024];
1✔
742
        let mut parser = SliceParser::with_buffer_from_slice(input, &mut scratch);
1✔
743
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
744
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
745
        assert_eq!(
1✔
746
            parser.next_event(),
1✔
747
            Ok(Event::String(String::Borrowed("value")))
748
        );
749
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
750
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
751
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
752
    }
1✔
753

754
    #[test]
755
    fn test_with_config_constructors() {
1✔
756
        // Test with_config constructor (no escapes)
757
        let json = r#"{"simple": "no_escapes"}"#;
1✔
758
        let mut parser = SliceParser::<BitStackStruct<u64, u16>>::with_config(json);
1✔
759

760
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
761
        assert_eq!(
1✔
762
            parser.next_event(),
1✔
763
            Ok(Event::Key(String::Borrowed("simple")))
764
        );
765
        assert_eq!(
1✔
766
            parser.next_event(),
1✔
767
            Ok(Event::String(String::Borrowed("no_escapes")))
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_with_config_and_buffer_constructors() {
1✔
775
        // Test with_config_and_buffer constructor (with escapes)
776
        let json = r#"{"escaped": "hello\nworld"}"#;
1✔
777
        let mut scratch = [0u8; 64];
1✔
778
        let mut parser =
1✔
779
            SliceParser::<BitStackStruct<u64, u16>>::with_config_and_buffer(json, &mut scratch);
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("escaped")))
785
        );
786

787
        if let Ok(Event::String(s)) = parser.next_event() {
1✔
788
            assert_eq!(s.as_ref(), "hello\nworld"); // Escape should be processed
1✔
789
        } else {
790
            panic!("Expected String event");
×
791
        }
792

793
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
794
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
795
    }
1✔
796

797
    #[test]
798
    fn test_alternative_config_deep_nesting() {
1✔
799
        // Test that custom BitStack configs can handle deeper nesting
800
        let json = r#"{"a":{"b":{"c":{"d":{"e":"deep"}}}}}"#;
1✔
801
        let mut scratch = [0u8; 64];
1✔
802
        let mut parser =
1✔
803
            SliceParser::<ArrayBitStack<8, u32, u16>>::with_config_and_buffer(json, &mut scratch);
1✔
804

805
        // Parse the deep structure
806
        let mut depth = 0;
1✔
807
        while let Ok(event) = parser.next_event() {
17✔
808
            match event {
17✔
809
                Event::StartObject => depth += 1,
5✔
810
                Event::EndObject => depth -= 1,
5✔
811
                Event::EndDocument => break,
1✔
812
                _ => {}
6✔
813
            }
814
        }
815

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