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

kaidokert / picojson-rs / 16245656870

13 Jul 2025 05:12AM UTC coverage: 93.804% (+0.1%) from 93.685%
16245656870

Pull #55

github

web-flow
Merge c33e1ce82 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
                    // SliceParser-specific: Handle simple escape sequences on Begin events
213
                    // because CopyOnEscape requires starting unescaping immediately when
214
                    // the escape token begins to maintain zero-copy optimization
215
                    process_simple_escape_event(&escape_token, self)?;
20✔
216
                }
NEW
217
                ujson::Event::End(EventToken::EscapeSequence) => {
×
NEW
218
                    // Ignore in SliceParser since it uses slice-based parsing
×
NEW
219
                }
×
220
                _ => {
18✔
221
                    // All other events continue to next iteration
18✔
222
                }
18✔
223
            }
224
        }
225
    }
345✔
226

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

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

248
    fn current_position(&self) -> usize {
170✔
249
        self.buffer.current_pos()
170✔
250
    }
170✔
251

252
    fn begin_string_content(&mut self, pos: usize) {
137✔
253
        self.copy_on_escape.begin_string(pos);
137✔
254
    }
137✔
255

256
    fn unicode_escape_collector_mut(
179✔
257
        &mut self,
179✔
258
    ) -> &mut crate::escape_processor::UnicodeEscapeCollector {
179✔
259
        &mut self.unicode_escape_collector
179✔
260
    }
179✔
261

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

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

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

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

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

301
        // Shared Unicode escape processing pattern
302
        let had_pending_high_surrogate = self.unicode_escape_collector.has_pending_high_surrogate();
35✔
303

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

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

330
        Ok(())
27✔
331
    }
35✔
332

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

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

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

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

364
#[cfg(test)]
365
mod tests {
366
    use super::*;
367
    use crate::{ArrayBitStack, BitStackStruct, String};
368

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

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

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

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

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

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

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

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

477
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
478
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
479
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
480
    }
1✔
481

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

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

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

508
        // Test that the parser correctly handles escaped strings
509
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
510

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

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

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

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

545
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
546
    }
1✔
547

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

555
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
556

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

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

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

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

589
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
590
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
591
    }
1✔
592

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

599
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
600
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
601

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

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

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

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

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

646
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
647

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

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

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

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

672
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
673
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
674
    }
1✔
675

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

682
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
683
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
684

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

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

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

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

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

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

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

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

752
    #[test]
753
    fn test_with_config_and_buffer_constructors() {
1✔
754
        // Test with_config_and_buffer constructor (with escapes)
755
        let json = r#"{"escaped": "hello\nworld"}"#;
1✔
756
        let mut scratch = [0u8; 64];
1✔
757
        let mut parser =
1✔
758
            SliceParser::<BitStackStruct<u64, u16>>::with_config_and_buffer(json, &mut scratch);
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("escaped")))
764
        );
765

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

772
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
773
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
774
    }
1✔
775

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

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

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