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

kaidokert / picojson-rs / 16103975982

06 Jul 2025 10:50PM UTC coverage: 93.416% (-0.3%) from 93.708%
16103975982

Pull #43

github

kaidokert
Fix parsing off by one bug
Pull Request #43: Fix number parsing off by one bug

14 of 15 new or added lines in 3 files covered. (93.33%)

12 existing lines in 2 files now uncovered.

4058 of 4344 relevant lines covered (93.42%)

539.32 hits per line

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

91.86
/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::number_parser::NumberExtractor;
6
use crate::parse_error::ParseError;
7
use crate::shared::{ContentRange, Event, ParserState, PullParser, State, UnexpectedState};
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
    /// SliceParser handles all delimiter logic directly.
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
        // SliceParser delimiter logic (same as the legacy wrapper)
180
        let current_pos = self.buffer.current_position();
31✔
181
        let end_pos = if !from_container_end && self.buffer.is_empty() {
31✔
182
            // At EOF - use full span
NEW
183
            current_pos
×
184
        } else {
185
            // All other cases - exclude delimiter
186
            current_pos.saturating_sub(1)
31✔
187
        };
188

189
        crate::number_parser::parse_number_event(&self.buffer, start, end_pos)
31✔
190
    }
31✔
191

192
    /// Helper method to handle simple escape tokens using EscapeProcessor
193
    /// Converts EventToken back to original escape character and processes it
194
    fn handle_simple_escape_token(
15✔
195
        &mut self,
15✔
196
        escape_token: &EventToken,
15✔
197
    ) -> Result<Option<Event<'_, '_>>, ParseError> {
15✔
198
        // Use unified escape token processing
199
        let unescaped_char = EscapeProcessor::process_escape_token(escape_token)?;
15✔
200

201
        // Handle the escape using existing logic
202
        self.handle_escape_event(unescaped_char)
15✔
203
    }
15✔
204

205
    /// Handles escape sequence events by delegating to CopyOnEscape if we're inside a string or key
206
    fn handle_escape_event(
15✔
207
        &mut self,
15✔
208
        escape_char: u8,
15✔
209
    ) -> Result<Option<Event<'_, '_>>, ParseError> {
15✔
210
        if let State::String(_) | State::Key(_) = self.parser_state.state {
15✔
211
            self.copy_on_escape
15✔
212
                .handle_escape(self.buffer.current_pos(), escape_char)?;
15✔
213
        }
×
214
        Ok(None)
13✔
215
    }
15✔
216

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

223
        let mut utf8_buf = [0u8; 4];
1✔
224
        let (utf8_bytes, escape_start_pos) =
1✔
225
            crate::escape_processor::process_unicode_escape_sequence(
1✔
226
                current_pos,
1✔
227
                &mut self.unicode_escape_collector,
1✔
228
                hex_slice_provider,
1✔
229
                &mut utf8_buf,
1✔
230
            )?;
×
231

232
        self.copy_on_escape
1✔
233
            .handle_unicode_escape(escape_start_pos, utf8_bytes)
1✔
234
    }
1✔
235

236
    fn pull_tokenizer_events(&mut self) -> Result<(), ParseError> {
1,945✔
237
        use crate::slice_input_buffer::InputBuffer;
238
        if self.buffer.is_past_end() {
1,945✔
239
            return Err(ParseError::EndOfData);
×
240
        }
1,945✔
241
        let mut callback = |event, _len| {
1,945✔
242
            for evt in self.parser_state.evts.iter_mut() {
498✔
243
                if evt.is_none() {
498✔
244
                    *evt = Some(event);
469✔
245
                    return;
469✔
246
                }
29✔
247
            }
248
        };
469✔
249

250
        let res = match self.buffer.consume_byte() {
1,945✔
251
            Err(crate::slice_input_buffer::Error::ReachedEnd) => {
252
                self.tokenizer.finish(&mut callback)
32✔
253
            }
254
            Err(err) => {
×
255
                return Err(err.into());
×
256
            }
257
            Ok(byte) => self.tokenizer.parse_chunk(&[byte], &mut callback),
1,913✔
258
        };
259

260
        if let Err(_tokenizer_error) = res {
1,945✔
261
            return Err(ParseError::TokenizerError);
8✔
262
        }
1,937✔
263
        Ok(())
1,937✔
264
    }
1,945✔
265

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

291
            if let Some(taken) = taken_event {
458✔
292
                let res = match taken {
456✔
293
                    // Container events
294
                    ujson::Event::ObjectStart => EventResult::Complete(Event::StartObject),
42✔
295
                    ujson::Event::ObjectEnd => EventResult::Complete(Event::EndObject),
29✔
296
                    ujson::Event::ArrayStart => EventResult::Complete(Event::StartArray),
13✔
297
                    ujson::Event::ArrayEnd => EventResult::Complete(Event::EndArray),
9✔
298

299
                    // String/Key events
300
                    ujson::Event::Begin(EventToken::Key) => {
301
                        self.parser_state.state = State::Key(self.buffer.current_pos());
69✔
302
                        self.copy_on_escape.begin_string(self.buffer.current_pos());
69✔
303
                        EventResult::Continue
69✔
304
                    }
305
                    ujson::Event::End(EventToken::Key) => EventResult::ExtractKey,
69✔
306
                    ujson::Event::Begin(EventToken::String) => {
307
                        self.parser_state.state = State::String(self.buffer.current_pos());
46✔
308
                        self.copy_on_escape.begin_string(self.buffer.current_pos());
46✔
309
                        EventResult::Continue
46✔
310
                    }
311
                    ujson::Event::End(EventToken::String) => EventResult::ExtractString,
41✔
312

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

454
impl<'a, 'b, C: BitStackConfig> PullParser for SliceParser<'a, 'b, C> {
455
    fn next_event(&mut self) -> Result<Event<'_, '_>, ParseError> {
289✔
456
        self.next_event_impl()
289✔
457
    }
289✔
458
}
459

460
#[cfg(test)]
461
mod tests {
462
    use super::*;
463
    use crate::{ArrayBitStack, BitStackStruct, String};
464

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

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

500
    #[test]
501
    fn parse_bool_and_null() {
1✔
502
        let input = r#"{"key": true, "key2": false, "key3": null}"#;
1✔
503
        let mut scratch = [0u8; 1024];
1✔
504
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
505
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
506
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
507
        assert_eq!(parser.next_event(), Ok(Event::Bool(true)));
1✔
508
        assert_eq!(
1✔
509
            parser.next_event(),
1✔
510
            Ok(Event::Key(String::Borrowed("key2")))
511
        );
512
        assert_eq!(parser.next_event(), Ok(Event::Bool(false)));
1✔
513
        assert_eq!(
1✔
514
            parser.next_event(),
1✔
515
            Ok(Event::Key(String::Borrowed("key3")))
516
        );
517
        assert_eq!(parser.next_event(), Ok(Event::Null));
1✔
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 parse_array() {
1✔
524
        #[cfg(feature = "float-error")]
525
        let input = r#"{"key": [1, 2, 3]}"#; // No floats for float-error config
526
        #[cfg(not(feature = "float-error"))]
527
        let input = r#"{"key": [1, 2.2, 3]}"#; // Include float for other configs
1✔
528

529
        let mut scratch = [0u8; 1024];
1✔
530
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
531
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
532
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
533
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
534

535
        // First number: 1 (integer)
536
        match parser.next_event() {
1✔
537
            Ok(Event::Number(num)) => {
1✔
538
                assert_eq!(num.as_str(), "1");
1✔
539
                assert_eq!(num.as_int(), Some(1));
1✔
540
            }
541
            other => panic!("Expected Number(1), got: {:?}", other),
×
542
        }
543

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

564
        // Third number: 3 (integer)
565
        match parser.next_event() {
1✔
566
            Ok(Event::Number(num)) => {
1✔
567
                assert_eq!(num.as_str(), "3");
1✔
568
                assert_eq!(num.as_int(), Some(3));
1✔
569
            }
570
            other => panic!("Expected Number(3), got: {:?}", other),
×
571
        }
572

573
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
574
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
575
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
576
    }
1✔
577

578
    #[test]
579
    fn test_simple_parser_api() {
1✔
580
        let input = r#"{"name": "test"}"#;
1✔
581
        let mut scratch = [0u8; 1024];
1✔
582
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
583

584
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
585
        assert_eq!(
1✔
586
            parser.next_event(),
1✔
587
            Ok(Event::Key(String::Borrowed("name")))
588
        );
589
        assert_eq!(
1✔
590
            parser.next_event(),
1✔
591
            Ok(Event::String(String::Borrowed("test")))
592
        );
593
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
594
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
595
    }
1✔
596

597
    #[test]
598
    fn test_parser_with_escaped_strings() {
1✔
599
        // Use regular string literal to properly include escape sequences
600
        let input = "{\"name\": \"John\\nDoe\", \"message\": \"Hello\\tWorld!\"}";
1✔
601
        let mut scratch = [0u8; 1024];
1✔
602
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
603

604
        // Test that the parser correctly handles escaped strings
605
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
606

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

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

625
        // Second key should be simple -> Borrowed
626
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
627
            assert_eq!(&*key, "message");
1✔
628
            assert!(matches!(key, String::Borrowed(_)));
1✔
629
        } else {
630
            panic!("Expected Key event");
×
631
        }
632

633
        // Second value should have escapes -> Unescaped
634
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
635
            assert_eq!(&*value, "Hello\tWorld!");
1✔
636
            assert!(matches!(value, String::Unescaped(_)));
1✔
637
        } else {
638
            panic!("Expected String event");
×
639
        }
640

641
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
642
    }
1✔
643

644
    #[test]
645
    fn test_copy_on_escape_optimization() {
1✔
646
        // Use regular string literal to include proper escape sequences
647
        let input = "{\"simple\": \"no escapes\", \"complex\": \"has\\nescapes\"}";
1✔
648
        let mut scratch = [0u8; 1024];
1✔
649
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
650

651
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
652

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

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

669
        // "complex" key should be borrowed (fast path)
670
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
671
            assert_eq!(&*key, "complex");
1✔
672
            assert!(matches!(key, String::Borrowed(_)));
1✔
673
        } else {
674
            panic!("Expected Key event");
×
675
        }
676

677
        // "has\\nescapes" value should be unescaped (slow path)
678
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
679
            assert_eq!(&*value, "has\nescapes");
1✔
680
            assert!(matches!(value, String::Unescaped(_)));
1✔
681
        } else {
682
            panic!("Expected String event");
×
683
        }
684

685
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
686
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
687
    }
1✔
688

689
    #[test]
690
    fn test_coe2_integration_multiple_escapes() {
1✔
691
        let input = r#"{"key": "a\nb\tc\rd"}"#;
1✔
692
        let mut scratch = [0u8; 1024];
1✔
693
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
694

695
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
696
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
697

698
        let string_event = parser.next_event().unwrap();
1✔
699
        match string_event {
1✔
700
            Event::String(String::Unescaped(s)) => {
1✔
701
                assert_eq!(s, "a\nb\tc\rd");
1✔
702
            }
703
            _ => panic!("Expected unescaped string value, got: {:?}", string_event),
×
704
        }
705
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
706
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
707
    }
1✔
708

709
    #[test]
710
    fn test_coe2_integration_zero_copy_path() {
1✔
711
        let input = r#"{"simple": "no_escapes_here"}"#;
1✔
712
        let mut scratch = [0u8; 1024];
1✔
713
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
714

715
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
716
        assert_eq!(
1✔
717
            parser.next_event(),
1✔
718
            Ok(Event::Key(String::Borrowed("simple")))
719
        );
720

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

736
    #[test]
737
    fn test_coe2_integration_mixed_strings() {
1✔
738
        let input = r#"["plain", "with\nescapes", "plain2", "more\tescapes"]"#;
1✔
739
        let mut scratch = [0u8; 1024];
1✔
740
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
741

742
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
743

744
        // First string: no escapes -> borrowed
745
        match parser.next_event().unwrap() {
1✔
746
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain"),
1✔
747
            other => panic!("Expected borrowed string, got: {:?}", other),
×
748
        }
749

750
        // Second string: has escapes -> unescaped
751
        match parser.next_event().unwrap() {
1✔
752
            Event::String(String::Unescaped(s)) => assert_eq!(s, "with\nescapes"),
1✔
753
            other => panic!("Expected unescaped string, got: {:?}", other),
×
754
        }
755

756
        // Third string: no escapes -> borrowed
757
        match parser.next_event().unwrap() {
1✔
758
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain2"),
1✔
759
            other => panic!("Expected borrowed string, got: {:?}", other),
×
760
        }
761

762
        // Fourth string: has escapes -> unescaped
763
        match parser.next_event().unwrap() {
1✔
764
            Event::String(String::Unescaped(s)) => assert_eq!(s, "more\tescapes"),
1✔
765
            other => panic!("Expected unescaped string, got: {:?}", other),
×
766
        }
767

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

772
    #[test]
773
    fn test_unicode_escape_integration() {
1✔
774
        let input = r#"{"key": "Hello\u0041World"}"#; // \u0041 = 'A'
1✔
775
        let mut scratch = [0u8; 1024];
1✔
776
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
777

778
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
779
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
780

781
        // The string with Unicode escape should be unescaped
782
        match parser.next_event().unwrap() {
1✔
783
            Event::String(String::Unescaped(s)) => {
1✔
784
                assert_eq!(s, "HelloAWorld");
1✔
785
            }
786
            other => panic!("Expected unescaped string value, got: {:?}", other),
×
787
        }
788

789
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
790
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
791
    }
1✔
792

793
    #[test]
794
    fn test_original_parser_escape_trace() {
1✔
795
        // Test escape sequence processing with logging
796
        let input = r#""a\nb""#;
1✔
797
        let mut scratch = [0u8; 1024];
1✔
798
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
799

800
        // Should get String with unescaped content
801
        let event = parser.next_event().unwrap();
1✔
802
        if let Event::String(s) = event {
1✔
803
            assert_eq!(&*s, "a\nb");
1✔
804
        } else {
805
            panic!("Expected String event, got {:?}", event);
×
806
        }
807

808
        // Should get EndDocument
809
        let event = parser.next_event().unwrap();
1✔
810
        assert_eq!(event, Event::EndDocument);
1✔
811
    }
1✔
812

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

829
    #[test]
830
    fn test_with_config_constructors() {
1✔
831
        // Test with_config constructor (no escapes)
832
        let json = r#"{"simple": "no_escapes"}"#;
1✔
833
        let mut parser = SliceParser::<BitStackStruct<u64, u16>>::with_config(json);
1✔
834

835
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
836
        assert_eq!(
1✔
837
            parser.next_event(),
1✔
838
            Ok(Event::Key(String::Borrowed("simple")))
839
        );
840
        assert_eq!(
1✔
841
            parser.next_event(),
1✔
842
            Ok(Event::String(String::Borrowed("no_escapes")))
843
        );
844
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
845
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
846
    }
1✔
847

848
    #[test]
849
    fn test_with_config_and_buffer_constructors() {
1✔
850
        // Test with_config_and_buffer constructor (with escapes)
851
        let json = r#"{"escaped": "hello\nworld"}"#;
1✔
852
        let mut scratch = [0u8; 64];
1✔
853
        let mut parser =
1✔
854
            SliceParser::<BitStackStruct<u64, u16>>::with_config_and_buffer(json, &mut scratch);
1✔
855

856
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
857
        assert_eq!(
1✔
858
            parser.next_event(),
1✔
859
            Ok(Event::Key(String::Borrowed("escaped")))
860
        );
861

862
        if let Ok(Event::String(s)) = parser.next_event() {
1✔
863
            assert_eq!(s.as_ref(), "hello\nworld"); // Escape should be processed
1✔
864
        } else {
865
            panic!("Expected String event");
×
866
        }
867

868
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
869
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
870
    }
1✔
871

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

880
        // Parse the deep structure
881
        let mut depth = 0;
1✔
882
        while let Ok(event) = parser.next_event() {
17✔
883
            match event {
17✔
884
                Event::StartObject => depth += 1,
5✔
885
                Event::EndObject => depth -= 1,
5✔
886
                Event::EndDocument => break,
1✔
887
                _ => {}
6✔
888
            }
889
        }
890

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