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

kaidokert / picojson-rs / 16092050615

05 Jul 2025 09:06PM UTC coverage: 94.616% (+0.3%) from 94.304%
16092050615

push

github

web-flow
General refactor and cleanup (#38)

* General refactor and cleanup

* Rename direct_buffer->stream_buffer

68 of 78 new or added lines in 2 files covered. (87.18%)

80 existing lines in 2 files now uncovered.

3743 of 3956 relevant lines covered (94.62%)

80.45 hits per line

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

90.93
/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::{EscapeProcessor, UnicodeEscapeCollector};
5
use crate::shared::{
6
    ContentRange, Event, ParseError, ParserErrorHandler, ParserState, PullParser, State,
7
};
8
use crate::slice_input_buffer::{InputBuffer, SliceInputBuffer};
9
use crate::ujson;
10
use ujson::{EventToken, Tokenizer};
11

12
use ujson::{BitStackConfig, DefaultConfig};
13

14
/// Result of processing a tokenizer event
15
enum EventResult<'a, 'b> {
16
    /// Event processing complete, return this event
17
    Complete(Event<'a, 'b>),
18
    /// Continue processing, no event to return yet
19
    Continue,
20
    /// Extract string content from current state
21
    ExtractString,
22
    /// Extract key content from current state
23
    ExtractKey,
24
    /// Extract number content from current state,
25
    /// bool indicates if number was terminated by container delimiter
26
    ExtractNumber(bool),
27
}
28

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

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

64
    /// Creates a new parser from a byte slice.
65
    ///
66
    /// Assumes no string escapes will be encountered. For JSON with escapes, use [`with_buffer_from_slice`].
67
    ///
68
    /// # Example
69
    /// ```
70
    /// # use picojson::SliceParser;
71
    /// let parser = SliceParser::new_from_slice(br#"{"name": "value"}"#);
72
    /// ```
73
    ///
74
    /// [`with_buffer_from_slice`]: Self::with_buffer_from_slice
75
    pub fn new_from_slice(input: &'a [u8]) -> Self {
9✔
76
        Self::with_config_from_slice(input)
9✔
77
    }
9✔
78
}
79

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

103
    /// Creates a new parser from a byte slice with a scratch buffer.
104
    ///
105
    /// Use when JSON contains string escapes that need unescaping.
106
    ///
107
    /// # Example
108
    /// ```
109
    /// # use picojson::SliceParser;
110
    /// let mut scratch = [0u8; 1024];
111
    /// let parser = SliceParser::with_buffer_from_slice(br#"{"msg": "Hello\nWorld"}"#, &mut scratch);
112
    /// ```
113
    pub fn with_buffer_from_slice(input: &'a [u8], scratch_buffer: &'b mut [u8]) -> Self {
36✔
114
        Self::with_config_and_buffer_from_slice(input, scratch_buffer)
36✔
115
    }
36✔
116
}
117

118
/// Generic constructor for SliceParser with custom configurations
119
impl<'a, 'b, C: BitStackConfig> SliceParser<'a, 'b, C> {
120
    /// Creates a new parser with a custom `BitStackConfig`.
121
    ///
122
    /// This parser assumes no string escapes will be encountered. If escapes are found,
123
    /// parsing will fail. For JSON with escapes, use `with_config_and_buffer`.
124
    pub fn with_config(input: &'a str) -> Self {
1✔
125
        Self::with_config_from_slice(input.as_bytes())
1✔
126
    }
1✔
127

128
    /// Creates a new parser from a byte slice with a custom `BitStackConfig`.
129
    ///
130
    /// Assumes no string escapes will be encountered. For JSON with escapes, use [`with_config_and_buffer_from_slice`].
131
    ///
132
    /// [`with_config_and_buffer_from_slice`]: Self::with_config_and_buffer_from_slice
133
    pub fn with_config_from_slice(input: &'a [u8]) -> Self {
10✔
134
        Self::with_config_and_buffer_from_slice(input, &mut [])
10✔
135
    }
10✔
136

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

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

168
    fn have_events(&self) -> bool {
2,403✔
169
        self.parser_state.evts.iter().any(|evt| evt.is_some())
4,367✔
170
    }
2,403✔
171

172
    /// Helper function to parse a number from the buffer given a start position.
173
    /// Uses unified number parsing logic with centralized delimiter handling.
174
    fn parse_number_from_buffer(
31✔
175
        &mut self,
31✔
176
        start: usize,
31✔
177
        from_container_end: bool,
31✔
178
    ) -> Result<Event<'_, '_>, ParseError> {
31✔
179
        crate::number_parser::parse_number_event(&self.buffer, start, from_container_end)
31✔
180
    }
31✔
181

182
    /// Helper method to handle simple escape tokens using EscapeProcessor
183
    /// Converts EventToken back to original escape character and processes it
184
    fn handle_simple_escape_token(
15✔
185
        &mut self,
15✔
186
        escape_token: &EventToken,
15✔
187
    ) -> Result<Option<Event<'_, '_>>, ParseError> {
15✔
188
        // Use unified escape token processing
189
        let unescaped_char = EscapeProcessor::process_escape_token(escape_token)?;
15✔
190

191
        // Handle the escape using existing logic
192
        self.handle_escape_event(unescaped_char)
15✔
193
    }
15✔
194

195
    /// Handles escape sequence events by delegating to CopyOnEscape if we're inside a string or key
196
    fn handle_escape_event(
15✔
197
        &mut self,
15✔
198
        escape_char: u8,
15✔
199
    ) -> Result<Option<Event<'_, '_>>, ParseError> {
15✔
200
        if let State::String(_) | State::Key(_) = self.parser_state.state {
15✔
201
            self.copy_on_escape
15✔
202
                .handle_escape(self.buffer.current_pos(), escape_char)?;
15✔
203
        }
×
204
        Ok(None)
13✔
205
    }
15✔
206

207
    /// Process Unicode escape sequence using shared UnicodeEscapeCollector
208
    /// Extracts hex digits from buffer and processes them through the collector
209
    fn process_unicode_escape_with_collector(&mut self) -> Result<(), ParseError> {
1✔
210
        let current_pos = self.buffer.current_pos();
1✔
211
        let hex_slice_provider = |start, end| self.buffer.slice(start, end).map_err(Into::into);
1✔
212

213
        let mut utf8_buf = [0u8; 4];
1✔
214
        let (utf8_bytes, escape_start_pos) =
1✔
215
            crate::escape_processor::process_unicode_escape_sequence(
1✔
216
                current_pos,
1✔
217
                &mut self.unicode_escape_collector,
1✔
218
                hex_slice_provider,
1✔
219
                &mut utf8_buf,
1✔
220
            )?;
×
221

222
        self.copy_on_escape
1✔
223
            .handle_unicode_escape(escape_start_pos, utf8_bytes)
1✔
224
    }
1✔
225

226
    fn pull_tokenizer_events(&mut self) -> Result<(), ParseError> {
1,945✔
227
        use crate::slice_input_buffer::InputBuffer;
228
        if self.buffer.is_past_end() {
1,945✔
229
            return Err(ParseError::EndOfData);
×
230
        }
1,945✔
231
        let mut callback = |event, _len| {
1,945✔
232
            for evt in self.parser_state.evts.iter_mut() {
498✔
233
                if evt.is_none() {
498✔
234
                    *evt = Some(event);
469✔
235
                    return;
469✔
236
                }
29✔
237
            }
238
        };
469✔
239

240
        let res = match self.buffer.consume_byte() {
1,945✔
241
            Err(crate::slice_input_buffer::Error::ReachedEnd) => {
242
                self.tokenizer.finish(&mut callback)
32✔
243
            }
244
            Err(crate::slice_input_buffer::Error::InvalidSliceBounds) => {
245
                return Err(ParseError::UnexpectedState(
×
246
                    "Invalid slice bounds in consume_byte",
×
247
                ));
×
248
            }
249
            Ok(byte) => self.tokenizer.parse_chunk(&[byte], &mut callback),
1,913✔
250
        };
251

252
        if let Err(_tokenizer_error) = res {
1,945✔
253
            return Err(ParseError::TokenizerError);
8✔
254
        }
1,937✔
255
        Ok(())
1,937✔
256
    }
1,945✔
257

258
    /// Returns the next JSON event or an error if parsing fails.
259
    /// Parsing continues until `EndDocument` is returned or an error occurs.
260
    fn next_event_impl(&mut self) -> Result<Event<'_, '_>, ParseError> {
289✔
261
        if self.buffer.is_past_end() {
289✔
262
            return Ok(Event::EndDocument);
2✔
263
        }
287✔
264
        loop {
265
            while !self.have_events() {
2,403✔
266
                self.pull_tokenizer_events()?;
1,945✔
267
                if self.buffer.is_past_end() {
1,937✔
268
                    return Ok(Event::EndDocument);
29✔
269
                }
1,908✔
270
            }
271
            // Find and move out the first available event to avoid holding mutable borrow during processing
272
            let taken_event = {
458✔
273
                let mut found_event = None;
458✔
274
                for evt in self.parser_state.evts.iter_mut() {
477✔
275
                    if evt.is_some() {
477✔
276
                        found_event = evt.take();
458✔
277
                        break;
458✔
278
                    }
19✔
279
                }
280
                found_event
458✔
281
            };
282

283
            if let Some(taken) = taken_event {
458✔
284
                let res = match taken {
456✔
285
                    // Container events
286
                    ujson::Event::ObjectStart => EventResult::Complete(Event::StartObject),
42✔
287
                    ujson::Event::ObjectEnd => EventResult::Complete(Event::EndObject),
29✔
288
                    ujson::Event::ArrayStart => EventResult::Complete(Event::StartArray),
13✔
289
                    ujson::Event::ArrayEnd => EventResult::Complete(Event::EndArray),
9✔
290

291
                    // String/Key events
292
                    ujson::Event::Begin(EventToken::Key) => {
293
                        self.parser_state.state = State::Key(self.buffer.current_pos());
69✔
294
                        self.copy_on_escape.begin_string(self.buffer.current_pos());
69✔
295
                        EventResult::Continue
69✔
296
                    }
297
                    ujson::Event::End(EventToken::Key) => EventResult::ExtractKey,
69✔
298
                    ujson::Event::Begin(EventToken::String) => {
299
                        self.parser_state.state = State::String(self.buffer.current_pos());
46✔
300
                        self.copy_on_escape.begin_string(self.buffer.current_pos());
46✔
301
                        EventResult::Continue
46✔
302
                    }
303
                    ujson::Event::End(EventToken::String) => EventResult::ExtractString,
41✔
304

305
                    // Number events
306
                    ujson::Event::Begin(
307
                        EventToken::Number
308
                        | EventToken::NumberAndArray
309
                        | EventToken::NumberAndObject,
310
                    ) => {
311
                        let number_start =
33✔
312
                            ContentRange::number_start_from_current(self.buffer.current_pos());
33✔
313
                        self.parser_state.state = State::Number(number_start);
33✔
314
                        EventResult::Continue
33✔
315
                    }
316
                    ujson::Event::End(EventToken::Number) => EventResult::ExtractNumber(false),
17✔
317
                    ujson::Event::End(EventToken::NumberAndArray) => {
318
                        EventResult::ExtractNumber(true)
7✔
319
                    }
320
                    ujson::Event::End(EventToken::NumberAndObject) => {
321
                        EventResult::ExtractNumber(true)
7✔
322
                    }
323
                    // Boolean and null values
324
                    ujson::Event::Begin(
325
                        EventToken::True | EventToken::False | EventToken::Null,
326
                    ) => EventResult::Continue,
14✔
327
                    ujson::Event::End(EventToken::True) => EventResult::Complete(Event::Bool(true)),
6✔
328
                    ujson::Event::End(EventToken::False) => {
329
                        EventResult::Complete(Event::Bool(false))
4✔
330
                    }
331
                    ujson::Event::End(EventToken::Null) => EventResult::Complete(Event::Null),
4✔
332
                    // Escape sequence handling
333
                    ujson::Event::Begin(
334
                        escape_token @ (EventToken::EscapeQuote
15✔
335
                        | EventToken::EscapeBackslash
336
                        | EventToken::EscapeSlash
337
                        | EventToken::EscapeBackspace
338
                        | EventToken::EscapeFormFeed
339
                        | EventToken::EscapeNewline
340
                        | EventToken::EscapeCarriageReturn
341
                        | EventToken::EscapeTab),
342
                    ) => {
343
                        // Use EscapeProcessor for all simple escape sequences
344
                        self.handle_simple_escape_token(&escape_token)?;
15✔
345
                        EventResult::Continue
13✔
346
                    }
347
                    ujson::Event::Begin(EventToken::UnicodeEscape) => {
348
                        // Start Unicode escape collection - reset collector for new sequence
349
                        // Only handle if we're inside a string or key
350
                        match self.parser_state.state {
1✔
351
                            State::String(_) | State::Key(_) => {
1✔
352
                                self.unicode_escape_collector.reset();
1✔
353
                            }
1✔
354
                            _ => {} // Ignore if not in string/key
×
355
                        }
356
                        EventResult::Continue
1✔
357
                    }
358
                    ujson::Event::End(EventToken::UnicodeEscape) => {
359
                        // Handle end of Unicode escape sequence (\uXXXX) using shared collector
360
                        match self.parser_state.state {
1✔
361
                            State::String(_) | State::Key(_) => {
362
                                // Process Unicode escape using shared collector logic
363
                                self.process_unicode_escape_with_collector()?;
1✔
364
                            }
365
                            _ => {} // Ignore if not in string/key context
×
366
                        }
367
                        EventResult::Continue
1✔
368
                    }
369
                    // EscapeSequence events (only emitted when flag is enabled, ignored in original parser)
370
                    ujson::Event::Begin(EventToken::EscapeSequence) => {
371
                        // Ignore in original parser since it uses slice-based parsing
372
                        EventResult::Continue
18✔
373
                    }
374
                    ujson::Event::End(EventToken::EscapeSequence) => {
375
                        // Ignore in original parser since it uses slice-based parsing
376
                        EventResult::Continue
×
377
                    }
378
                    ujson::Event::End(
379
                        EventToken::EscapeQuote
380
                        | EventToken::EscapeBackslash
381
                        | EventToken::EscapeSlash
382
                        | EventToken::EscapeBackspace
383
                        | EventToken::EscapeFormFeed
384
                        | EventToken::EscapeNewline
385
                        | EventToken::EscapeCarriageReturn
386
                        | EventToken::EscapeTab,
387
                    ) => {
388
                        // End of escape sequence - ignored here
389
                        EventResult::Continue
13✔
390
                    }
391
                    #[cfg(test)]
392
                    ujson::Event::Uninitialized => {
393
                        return Err(ParseError::UnexpectedState("Uninitialized event"));
×
394
                    }
395
                };
396
                match res {
456✔
397
                    EventResult::Complete(event) => break Ok(event),
107✔
398
                    EventResult::Continue => continue,
208✔
399
                    EventResult::ExtractKey => {
400
                        if let State::Key(_start) = self.parser_state.state {
69✔
401
                            self.parser_state.state = State::None;
69✔
402
                            // Use CopyOnEscape to get the final key result
403
                            let end_pos = ContentRange::end_position_excluding_delimiter(
69✔
404
                                self.buffer.current_pos(),
69✔
405
                            );
406
                            let key_result = self.copy_on_escape.end_string(end_pos)?;
69✔
407
                            break Ok(Event::Key(key_result));
69✔
408
                        } else {
409
                            break Err(ParserErrorHandler::state_mismatch("key", "end"));
×
410
                        }
411
                    }
412
                    EventResult::ExtractString => {
413
                        if let State::String(_value) = self.parser_state.state {
41✔
414
                            self.parser_state.state = State::None;
41✔
415
                            // Use CopyOnEscape to get the final string result
416
                            let end_pos = ContentRange::end_position_excluding_delimiter(
41✔
417
                                self.buffer.current_pos(),
41✔
418
                            );
419
                            let value_result = self.copy_on_escape.end_string(end_pos)?;
41✔
420
                            break Ok(Event::String(value_result));
41✔
421
                        } else {
422
                            break Err(ParserErrorHandler::state_mismatch("string", "end"));
×
423
                        }
424
                    }
425
                    EventResult::ExtractNumber(from_container_end) => {
31✔
426
                        if let State::Number(start) = self.parser_state.state {
31✔
427
                            // Reset state before parsing to stop selective copying
428
                            self.parser_state.state = State::None;
31✔
429
                            let event = self.parse_number_from_buffer(start, from_container_end)?;
31✔
430
                            break Ok(event);
31✔
431
                        } else {
432
                            break Err(ParseError::UnexpectedState(
×
433
                                "Number end without Number start",
×
434
                            ));
×
435
                        }
436
                    }
437
                }
438
            } else {
439
                // No event available - this shouldn't happen since we ensured have_events() above
440
                break Err(ParseError::UnexpectedState(
×
441
                    "No events available after ensuring events exist",
×
442
                ));
×
443
            }
444
        }
445
    }
289✔
446
}
447

448
impl<'a, 'b, C: BitStackConfig> PullParser for SliceParser<'a, 'b, C> {
449
    fn next_event(&mut self) -> Result<Event<'_, '_>, ParseError> {
289✔
450
        self.next_event_impl()
289✔
451
    }
289✔
452
}
453

454
#[cfg(test)]
455
mod tests {
456
    use super::*;
457
    use crate::{ArrayBitStack, BitStackStruct, String};
458

459
    #[test]
460
    fn make_parser() {
1✔
461
        let input = r#"{"key": "value"}"#;
1✔
462
        let mut scratch = [0u8; 1024];
1✔
463
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
464
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
465
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
466
        assert_eq!(
1✔
467
            parser.next_event(),
1✔
468
            Ok(Event::String(String::Borrowed("value")))
469
        );
470
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
471
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
472
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
473
    }
1✔
474

475
    #[test]
476
    fn parse_number() {
1✔
477
        let input = r#"{"key": 124}"#;
1✔
478
        let mut scratch = [0u8; 1024];
1✔
479
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
480
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
481
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
482
        // Check number value using new JsonNumber API
483
        match parser.next_event() {
1✔
484
            Ok(Event::Number(num)) => {
1✔
485
                assert_eq!(num.as_str(), "124");
1✔
486
                assert_eq!(num.as_int(), Some(124));
1✔
487
            }
UNCOV
488
            other => panic!("Expected Number, got: {:?}", other),
×
489
        }
490
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
491
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
492
    }
1✔
493

494
    #[test]
495
    fn parse_bool_and_null() {
1✔
496
        let input = r#"{"key": true, "key2": false, "key3": null}"#;
1✔
497
        let mut scratch = [0u8; 1024];
1✔
498
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
499
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
500
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
501
        assert_eq!(parser.next_event(), Ok(Event::Bool(true)));
1✔
502
        assert_eq!(
1✔
503
            parser.next_event(),
1✔
504
            Ok(Event::Key(String::Borrowed("key2")))
505
        );
506
        assert_eq!(parser.next_event(), Ok(Event::Bool(false)));
1✔
507
        assert_eq!(
1✔
508
            parser.next_event(),
1✔
509
            Ok(Event::Key(String::Borrowed("key3")))
510
        );
511
        assert_eq!(parser.next_event(), Ok(Event::Null));
1✔
512
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
513
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
514
    }
1✔
515

516
    #[test]
517
    fn parse_array() {
1✔
518
        #[cfg(feature = "float-error")]
519
        let input = r#"{"key": [1, 2, 3]}"#; // No floats for float-error config
520
        #[cfg(not(feature = "float-error"))]
521
        let input = r#"{"key": [1, 2.2, 3]}"#; // Include float for other configs
1✔
522

523
        let mut scratch = [0u8; 1024];
1✔
524
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
525
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
526
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
527
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
528

529
        // First number: 1 (integer)
530
        match parser.next_event() {
1✔
531
            Ok(Event::Number(num)) => {
1✔
532
                assert_eq!(num.as_str(), "1");
1✔
533
                assert_eq!(num.as_int(), Some(1));
1✔
534
            }
UNCOV
535
            other => panic!("Expected Number(1), got: {:?}", other),
×
536
        }
537

538
        // Second number: depends on configuration
539
        match parser.next_event() {
1✔
540
            Ok(Event::Number(num)) => {
1✔
541
                #[cfg(feature = "float-error")]
542
                {
543
                    assert_eq!(num.as_str(), "2");
544
                    assert_eq!(num.as_int(), Some(2));
545
                }
546
                #[cfg(not(feature = "float-error"))]
547
                {
548
                    assert_eq!(num.as_str(), "2.2");
1✔
549
                    #[cfg(feature = "float")]
550
                    assert_eq!(num.as_f64(), Some(2.2));
1✔
551
                    #[cfg(not(feature = "float-error"))]
552
                    assert!(num.is_float());
1✔
553
                }
554
            }
UNCOV
555
            other => panic!("Expected Number, got: {:?}", other),
×
556
        }
557

558
        // Third number: 3 (integer)
559
        match parser.next_event() {
1✔
560
            Ok(Event::Number(num)) => {
1✔
561
                assert_eq!(num.as_str(), "3");
1✔
562
                assert_eq!(num.as_int(), Some(3));
1✔
563
            }
UNCOV
564
            other => panic!("Expected Number(3), got: {:?}", other),
×
565
        }
566

567
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
568
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
569
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
570
    }
1✔
571

572
    #[test]
573
    fn test_simple_parser_api() {
1✔
574
        let input = r#"{"name": "test"}"#;
1✔
575
        let mut scratch = [0u8; 1024];
1✔
576
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
577

578
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
579
        assert_eq!(
1✔
580
            parser.next_event(),
1✔
581
            Ok(Event::Key(String::Borrowed("name")))
582
        );
583
        assert_eq!(
1✔
584
            parser.next_event(),
1✔
585
            Ok(Event::String(String::Borrowed("test")))
586
        );
587
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
588
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
589
    }
1✔
590

591
    #[test]
592
    fn test_parser_with_escaped_strings() {
1✔
593
        // Use regular string literal to properly include escape sequences
594
        let input = "{\"name\": \"John\\nDoe\", \"message\": \"Hello\\tWorld!\"}";
1✔
595
        let mut scratch = [0u8; 1024];
1✔
596
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
597

598
        // Test that the parser correctly handles escaped strings
599
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
600

601
        // Key should be simple (no escapes) -> Borrowed
602
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
603
            assert_eq!(&*key, "name");
1✔
604
            // This should be the fast path (borrowed)
605
            assert!(matches!(key, String::Borrowed(_)));
1✔
606
        } else {
UNCOV
607
            panic!("Expected Key event");
×
608
        }
609

610
        // Value should have escapes -> Unescaped
611
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
612
            assert_eq!(&*value, "John\nDoe");
1✔
613
            // This should be the slow path (unescaped)
614
            assert!(matches!(value, String::Unescaped(_)));
1✔
615
        } else {
UNCOV
616
            panic!("Expected String event");
×
617
        }
618

619
        // Second key should be simple -> Borrowed
620
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
621
            assert_eq!(&*key, "message");
1✔
622
            assert!(matches!(key, String::Borrowed(_)));
1✔
623
        } else {
UNCOV
624
            panic!("Expected Key event");
×
625
        }
626

627
        // Second value should have escapes -> Unescaped
628
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
629
            assert_eq!(&*value, "Hello\tWorld!");
1✔
630
            assert!(matches!(value, String::Unescaped(_)));
1✔
631
        } else {
UNCOV
632
            panic!("Expected String event");
×
633
        }
634

635
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
636
    }
1✔
637

638
    #[test]
639
    fn test_copy_on_escape_optimization() {
1✔
640
        // Use regular string literal to include proper escape sequences
641
        let input = "{\"simple\": \"no escapes\", \"complex\": \"has\\nescapes\"}";
1✔
642
        let mut scratch = [0u8; 1024];
1✔
643
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
644

645
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
646

647
        // "simple" key should be borrowed (fast path)
648
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
649
            assert_eq!(&*key, "simple");
1✔
650
            assert!(matches!(key, String::Borrowed(_)));
1✔
651
        } else {
UNCOV
652
            panic!("Expected Key event");
×
653
        }
654

655
        // "no escapes" value should be borrowed (fast path)
656
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
657
            assert_eq!(&*value, "no escapes");
1✔
658
            assert!(matches!(value, String::Borrowed(_)));
1✔
659
        } else {
UNCOV
660
            panic!("Expected String event");
×
661
        }
662

663
        // "complex" key should be borrowed (fast path)
664
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
665
            assert_eq!(&*key, "complex");
1✔
666
            assert!(matches!(key, String::Borrowed(_)));
1✔
667
        } else {
UNCOV
668
            panic!("Expected Key event");
×
669
        }
670

671
        // "has\\nescapes" value should be unescaped (slow path)
672
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
673
            assert_eq!(&*value, "has\nescapes");
1✔
674
            assert!(matches!(value, String::Unescaped(_)));
1✔
675
        } else {
UNCOV
676
            panic!("Expected String event");
×
677
        }
678

679
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
680
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
681
    }
1✔
682

683
    #[test]
684
    fn test_coe2_integration_multiple_escapes() {
1✔
685
        let input = r#"{"key": "a\nb\tc\rd"}"#;
1✔
686
        let mut scratch = [0u8; 1024];
1✔
687
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
688

689
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
690
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
691

692
        let string_event = parser.next_event().unwrap();
1✔
693
        match string_event {
1✔
694
            Event::String(String::Unescaped(s)) => {
1✔
695
                assert_eq!(s, "a\nb\tc\rd");
1✔
696
            }
UNCOV
697
            _ => panic!("Expected unescaped string value, got: {:?}", string_event),
×
698
        }
699
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
700
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
701
    }
1✔
702

703
    #[test]
704
    fn test_coe2_integration_zero_copy_path() {
1✔
705
        let input = r#"{"simple": "no_escapes_here"}"#;
1✔
706
        let mut scratch = [0u8; 1024];
1✔
707
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
708

709
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
710
        assert_eq!(
1✔
711
            parser.next_event(),
1✔
712
            Ok(Event::Key(String::Borrowed("simple")))
713
        );
714

715
        // This should be borrowed (zero-copy) since no escapes
716
        let string_event = parser.next_event().unwrap();
1✔
717
        match string_event {
1✔
718
            Event::String(String::Borrowed(s)) => {
1✔
719
                assert_eq!(s, "no_escapes_here");
1✔
720
            }
UNCOV
721
            _ => panic!(
×
UNCOV
722
                "Expected borrowed string value for zero-copy, got: {:?}",
×
723
                string_event
724
            ),
725
        }
726
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
727
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
728
    }
1✔
729

730
    #[test]
731
    fn test_coe2_integration_mixed_strings() {
1✔
732
        let input = r#"["plain", "with\nescapes", "plain2", "more\tescapes"]"#;
1✔
733
        let mut scratch = [0u8; 1024];
1✔
734
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
735

736
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
737

738
        // First string: no escapes -> borrowed
739
        match parser.next_event().unwrap() {
1✔
740
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain"),
1✔
UNCOV
741
            other => panic!("Expected borrowed string, got: {:?}", other),
×
742
        }
743

744
        // Second string: has escapes -> unescaped
745
        match parser.next_event().unwrap() {
1✔
746
            Event::String(String::Unescaped(s)) => assert_eq!(s, "with\nescapes"),
1✔
UNCOV
747
            other => panic!("Expected unescaped string, got: {:?}", other),
×
748
        }
749

750
        // Third string: no escapes -> borrowed
751
        match parser.next_event().unwrap() {
1✔
752
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain2"),
1✔
UNCOV
753
            other => panic!("Expected borrowed string, got: {:?}", other),
×
754
        }
755

756
        // Fourth string: has escapes -> unescaped
757
        match parser.next_event().unwrap() {
1✔
758
            Event::String(String::Unescaped(s)) => assert_eq!(s, "more\tescapes"),
1✔
UNCOV
759
            other => panic!("Expected unescaped string, got: {:?}", other),
×
760
        }
761

762
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
763
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
764
    }
1✔
765

766
    #[test]
767
    fn test_unicode_escape_integration() {
1✔
768
        let input = r#"{"key": "Hello\u0041World"}"#; // \u0041 = 'A'
1✔
769
        let mut scratch = [0u8; 1024];
1✔
770
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
771

772
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
773
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
774

775
        // The string with Unicode escape should be unescaped
776
        match parser.next_event().unwrap() {
1✔
777
            Event::String(String::Unescaped(s)) => {
1✔
778
                assert_eq!(s, "HelloAWorld");
1✔
779
            }
UNCOV
780
            other => panic!("Expected unescaped string value, got: {:?}", other),
×
781
        }
782

783
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
784
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
785
    }
1✔
786

787
    #[test]
788
    fn test_original_parser_escape_trace() {
1✔
789
        // Test escape sequence processing with logging
790
        let input = r#""a\nb""#;
1✔
791
        let mut scratch = [0u8; 1024];
1✔
792
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
793

794
        // Should get String with unescaped content
795
        let event = parser.next_event().unwrap();
1✔
796
        if let Event::String(s) = event {
1✔
797
            assert_eq!(&*s, "a\nb");
1✔
798
        } else {
UNCOV
799
            panic!("Expected String event, got {:?}", event);
×
800
        }
801

802
        // Should get EndDocument
803
        let event = parser.next_event().unwrap();
1✔
804
        assert_eq!(event, Event::EndDocument);
1✔
805
    }
1✔
806

807
    #[test]
808
    fn make_parser_from_slice() {
1✔
809
        let input = br#"{"key": "value"}"#;
1✔
810
        let mut scratch = [0u8; 1024];
1✔
811
        let mut parser = SliceParser::with_buffer_from_slice(input, &mut scratch);
1✔
812
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
813
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
814
        assert_eq!(
1✔
815
            parser.next_event(),
1✔
816
            Ok(Event::String(String::Borrowed("value")))
817
        );
818
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
819
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
820
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
821
    }
1✔
822

823
    #[test]
824
    fn test_with_config_constructors() {
1✔
825
        // Test with_config constructor (no escapes)
826
        let json = r#"{"simple": "no_escapes"}"#;
1✔
827
        let mut parser = SliceParser::<BitStackStruct<u64, u16>>::with_config(json);
1✔
828

829
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
830
        assert_eq!(
1✔
831
            parser.next_event(),
1✔
832
            Ok(Event::Key(String::Borrowed("simple")))
833
        );
834
        assert_eq!(
1✔
835
            parser.next_event(),
1✔
836
            Ok(Event::String(String::Borrowed("no_escapes")))
837
        );
838
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
839
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
840
    }
1✔
841

842
    #[test]
843
    fn test_with_config_and_buffer_constructors() {
1✔
844
        // Test with_config_and_buffer constructor (with escapes)
845
        let json = r#"{"escaped": "hello\nworld"}"#;
1✔
846
        let mut scratch = [0u8; 64];
1✔
847
        let mut parser =
1✔
848
            SliceParser::<BitStackStruct<u64, u16>>::with_config_and_buffer(json, &mut scratch);
1✔
849

850
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
851
        assert_eq!(
1✔
852
            parser.next_event(),
1✔
853
            Ok(Event::Key(String::Borrowed("escaped")))
854
        );
855

856
        if let Ok(Event::String(s)) = parser.next_event() {
1✔
857
            assert_eq!(s.as_ref(), "hello\nworld"); // Escape should be processed
1✔
858
        } else {
UNCOV
859
            panic!("Expected String event");
×
860
        }
861

862
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
863
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
864
    }
1✔
865

866
    #[test]
867
    fn test_alternative_config_deep_nesting() {
1✔
868
        // Test that custom BitStack configs can handle deeper nesting
869
        let json = r#"{"a":{"b":{"c":{"d":{"e":"deep"}}}}}"#;
1✔
870
        let mut scratch = [0u8; 64];
1✔
871
        let mut parser =
1✔
872
            SliceParser::<ArrayBitStack<8, u32, u16>>::with_config_and_buffer(json, &mut scratch);
1✔
873

874
        // Parse the deep structure
875
        let mut depth = 0;
1✔
876
        while let Ok(event) = parser.next_event() {
17✔
877
            match event {
17✔
878
                Event::StartObject => depth += 1,
5✔
879
                Event::EndObject => depth -= 1,
5✔
880
                Event::EndDocument => break,
1✔
881
                _ => {}
6✔
882
            }
883
        }
884

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