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

kaidokert / picojson-rs / 16211723222

11 Jul 2025 04:20AM UTC coverage: 93.666% (+0.3%) from 93.391%
16211723222

Pull #49

github

kaidokert
Implement surrogate pairs
Pull Request #49: Implement surrogate pairs

214 of 222 new or added lines in 3 files covered. (96.4%)

123 existing lines in 2 files now uncovered.

4481 of 4784 relevant lines covered (93.67%)

565.33 hits per line

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

92.14
/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 {
51✔
100
        Self::with_buffer_from_slice(input.as_bytes(), scratch_buffer)
51✔
101
    }
51✔
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 {
52✔
114
        Self::with_config_and_buffer_from_slice(input, scratch_buffer)
52✔
115
    }
52✔
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(
64✔
155
        input: &'a [u8],
64✔
156
        scratch_buffer: &'b mut [u8],
64✔
157
    ) -> Self {
64✔
158
        let copy_on_escape = CopyOnEscape::new(input, scratch_buffer);
64✔
159
        SliceParser {
64✔
160
            tokenizer: Tokenizer::new(),
64✔
161
            buffer: SliceInputBuffer::new(input),
64✔
162
            parser_state: ParserState::new(),
64✔
163
            copy_on_escape,
64✔
164
            unicode_escape_collector: UnicodeEscapeCollector::new(),
64✔
165
        }
64✔
166
    }
64✔
167

168
    fn have_events(&self) -> bool {
2,914✔
169
        self.parser_state.evts.iter().any(|evt| evt.is_some())
5,214✔
170
    }
2,914✔
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
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
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(
20✔
195
        &mut self,
20✔
196
        escape_token: &EventToken,
20✔
197
    ) -> Result<Option<Event<'_, '_>>, ParseError> {
20✔
198
        // Use unified escape token processing
199
        let unescaped_char = EscapeProcessor::process_escape_token(escape_token)?;
20✔
200

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

205
    /// Handles escape sequence events by delegating to CopyOnEscape if we're inside a string or key
206
    fn handle_escape_event(
20✔
207
        &mut self,
20✔
208
        escape_char: u8,
20✔
209
    ) -> Result<Option<Event<'_, '_>>, ParseError> {
20✔
210
        // Clear any pending high surrogate state when we encounter a simple escape
211
        // This ensures that interrupted surrogate pairs (like \uD801\n\uDC37) are properly rejected
212
        self.unicode_escape_collector.reset_all();
20✔
213

214
        if let State::String(_) | State::Key(_) = self.parser_state.state {
20✔
215
            self.copy_on_escape
20✔
216
                .handle_escape(self.buffer.current_pos(), escape_char)?;
20✔
217
        }
×
218
        Ok(None)
18✔
219
    }
20✔
220

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

227
        // Check if we had a pending high surrogate before processing
228
        let had_pending_high_surrogate = self.unicode_escape_collector.has_pending_high_surrogate();
35✔
229

230
        let mut utf8_buf = [0u8; 4];
35✔
231
        let (utf8_bytes_opt, escape_start_pos) =
27✔
232
            crate::escape_processor::process_unicode_escape_sequence(
35✔
233
                current_pos,
35✔
234
                &mut self.unicode_escape_collector,
35✔
235
                hex_slice_provider,
35✔
236
                &mut utf8_buf,
35✔
237
            )?;
8✔
238

239
        // Only process UTF-8 bytes if we have them (not a high surrogate waiting for low surrogate)
240
        if let Some(utf8_bytes) = utf8_bytes_opt {
27✔
241
            if had_pending_high_surrogate {
11✔
242
                // This is completing a surrogate pair - need to consume both escapes
243
                // First call: consume the high surrogate (6 bytes earlier)
244
                self.copy_on_escape
8✔
245
                    .handle_unicode_escape(escape_start_pos, &[])?;
8✔
246
                // Second call: consume the low surrogate and write UTF-8
247
                self.copy_on_escape
8✔
248
                    .handle_unicode_escape(escape_start_pos + 6, utf8_bytes)?;
8✔
249
            } else {
250
                // Single Unicode escape - normal processing
251
                self.copy_on_escape
3✔
252
                    .handle_unicode_escape(escape_start_pos, utf8_bytes)?;
3✔
253
            }
254
        }
16✔
255

256
        Ok(())
27✔
257
    }
35✔
258

259
    fn pull_tokenizer_events(&mut self) -> Result<(), ParseError> {
2,276✔
260
        use crate::slice_input_buffer::InputBuffer;
261
        if self.buffer.is_past_end() {
2,276✔
262
            return Err(ParseError::EndOfData);
×
263
        }
2,276✔
264
        let mut callback = |event, _len| {
2,276✔
265
            for evt in self.parser_state.evts.iter_mut() {
683✔
266
                if evt.is_none() {
683✔
267
                    *evt = Some(event);
649✔
268
                    return;
649✔
269
                }
34✔
270
            }
271
        };
649✔
272

273
        let res = match self.buffer.consume_byte() {
2,276✔
274
            Err(crate::slice_input_buffer::Error::ReachedEnd) => {
275
                self.tokenizer.finish(&mut callback)
39✔
276
            }
277
            Err(err) => {
×
278
                return Err(err.into());
×
279
            }
280
            Ok(byte) => self.tokenizer.parse_chunk(&[byte], &mut callback),
2,237✔
281
        };
282

283
        if let Err(tokenizer_error) = res {
2,276✔
284
            return Err(ParseError::TokenizerError(tokenizer_error));
8✔
285
        }
2,268✔
286
        Ok(())
2,268✔
287
    }
2,276✔
288

289
    /// Returns the next JSON event or an error if parsing fails.
290
    /// Parsing continues until `EndDocument` is returned or an error occurs.
291
    fn next_event_impl(&mut self) -> Result<Event<'_, '_>, ParseError> {
345✔
292
        if self.buffer.is_past_end() {
345✔
293
            return Ok(Event::EndDocument);
2✔
294
        }
343✔
295
        loop {
296
            while !self.have_events() {
2,914✔
297
                self.pull_tokenizer_events()?;
2,276✔
298
                if self.buffer.is_past_end() {
2,268✔
299
                    return Ok(Event::EndDocument);
36✔
300
                }
2,232✔
301
            }
302
            // Find and move out the first available event to avoid holding mutable borrow during processing
303
            let taken_event = {
638✔
304
                let mut found_event = None;
638✔
305
                for evt in self.parser_state.evts.iter_mut() {
662✔
306
                    if evt.is_some() {
662✔
307
                        found_event = evt.take();
638✔
308
                        break;
638✔
309
                    }
24✔
310
                }
311
                found_event
638✔
312
            };
313

314
            if let Some(taken) = taken_event {
638✔
315
                let res = match taken {
628✔
316
                    // Container events
317
                    ujson::Event::ObjectStart => EventResult::Complete(Event::StartObject),
46✔
318
                    ujson::Event::ObjectEnd => EventResult::Complete(Event::EndObject),
33✔
319
                    ujson::Event::ArrayStart => EventResult::Complete(Event::StartArray),
27✔
320
                    ujson::Event::ArrayEnd => EventResult::Complete(Event::EndArray),
14✔
321

322
                    // String/Key events
323
                    ujson::Event::Begin(EventToken::Key) => {
324
                        self.parser_state.state = State::Key(self.buffer.current_pos());
74✔
325
                        self.copy_on_escape.begin_string(self.buffer.current_pos());
74✔
326
                        EventResult::Continue
74✔
327
                    }
328
                    ujson::Event::End(EventToken::Key) => EventResult::ExtractKey,
74✔
329
                    ujson::Event::Begin(EventToken::String) => {
330
                        self.parser_state.state = State::String(self.buffer.current_pos());
63✔
331
                        self.copy_on_escape.begin_string(self.buffer.current_pos());
63✔
332
                        EventResult::Continue
63✔
333
                    }
334
                    ujson::Event::End(EventToken::String) => EventResult::ExtractString,
50✔
335

336
                    // Number events
337
                    ujson::Event::Begin(
338
                        EventToken::Number
339
                        | EventToken::NumberAndArray
340
                        | EventToken::NumberAndObject,
341
                    ) => {
342
                        let number_start =
33✔
343
                            ContentRange::number_start_from_current(self.buffer.current_pos());
33✔
344
                        self.parser_state.state = State::Number(number_start);
33✔
345
                        EventResult::Continue
33✔
346
                    }
347
                    ujson::Event::End(EventToken::Number) => EventResult::ExtractNumber(false),
17✔
348
                    ujson::Event::End(EventToken::NumberAndArray) => {
349
                        EventResult::ExtractNumber(true)
7✔
350
                    }
351
                    ujson::Event::End(EventToken::NumberAndObject) => {
352
                        EventResult::ExtractNumber(true)
7✔
353
                    }
354
                    // Boolean and null values
355
                    ujson::Event::Begin(
356
                        EventToken::True | EventToken::False | EventToken::Null,
357
                    ) => EventResult::Continue,
14✔
358
                    ujson::Event::End(EventToken::True) => EventResult::Complete(Event::Bool(true)),
6✔
359
                    ujson::Event::End(EventToken::False) => {
360
                        EventResult::Complete(Event::Bool(false))
4✔
361
                    }
362
                    ujson::Event::End(EventToken::Null) => EventResult::Complete(Event::Null),
4✔
363
                    // Escape sequence handling
364
                    ujson::Event::Begin(
365
                        escape_token @ (EventToken::EscapeQuote
20✔
366
                        | EventToken::EscapeBackslash
367
                        | EventToken::EscapeSlash
368
                        | EventToken::EscapeBackspace
369
                        | EventToken::EscapeFormFeed
370
                        | EventToken::EscapeNewline
371
                        | EventToken::EscapeCarriageReturn
372
                        | EventToken::EscapeTab),
373
                    ) => {
374
                        // Use EscapeProcessor for all simple escape sequences
375
                        self.handle_simple_escape_token(&escape_token)?;
20✔
376
                        EventResult::Continue
18✔
377
                    }
378
                    ujson::Event::Begin(EventToken::UnicodeEscape) => {
379
                        // Start Unicode escape collection - reset collector for new sequence
380
                        // Only handle if we're inside a string or key
381
                        match self.parser_state.state {
35✔
382
                            State::String(_) | State::Key(_) => {
35✔
383
                                self.unicode_escape_collector.reset();
35✔
384
                            }
35✔
385
                            _ => {} // Ignore if not in string/key
×
386
                        }
387
                        EventResult::Continue
35✔
388
                    }
389
                    ujson::Event::End(EventToken::UnicodeEscape) => {
390
                        // Handle end of Unicode escape sequence (\uXXXX) using shared collector
391
                        match self.parser_state.state {
35✔
392
                            State::String(_) | State::Key(_) => {
393
                                // Process Unicode escape using shared collector logic
394
                                self.process_unicode_escape_with_collector()?;
35✔
395
                            }
396
                            _ => {} // Ignore if not in string/key context
×
397
                        }
398
                        EventResult::Continue
27✔
399
                    }
400
                    // EscapeSequence events (only emitted when flag is enabled, ignored in original parser)
401
                    ujson::Event::Begin(EventToken::EscapeSequence) => {
402
                        // Ignore in original parser since it uses slice-based parsing
403
                        EventResult::Continue
57✔
404
                    }
405
                    ujson::Event::End(EventToken::EscapeSequence) => {
406
                        // Ignore in original parser since it uses slice-based parsing
407
                        EventResult::Continue
×
408
                    }
409
                    ujson::Event::End(
410
                        EventToken::EscapeQuote
411
                        | EventToken::EscapeBackslash
412
                        | EventToken::EscapeSlash
413
                        | EventToken::EscapeBackspace
414
                        | EventToken::EscapeFormFeed
415
                        | EventToken::EscapeNewline
416
                        | EventToken::EscapeCarriageReturn
417
                        | EventToken::EscapeTab,
418
                    ) => {
419
                        // End of escape sequence - ignored here
420
                        EventResult::Continue
18✔
421
                    }
422
                    #[cfg(test)]
423
                    ujson::Event::Uninitialized => {
424
                        return Err(UnexpectedState::StateMismatch.into());
×
425
                    }
426
                };
427
                match res {
628✔
428
                    EventResult::Complete(event) => break Ok(event),
134✔
429
                    EventResult::Continue => continue,
339✔
430
                    EventResult::ExtractKey => {
431
                        if let State::Key(_start) = self.parser_state.state {
74✔
432
                            // Check for incomplete surrogate pairs before ending the key
433
                            if self.unicode_escape_collector.has_pending_high_surrogate() {
74✔
NEW
434
                                break Err(ParseError::InvalidUnicodeCodepoint);
×
435
                            }
74✔
436
                            self.parser_state.state = State::None;
74✔
437
                            // Use CopyOnEscape to get the final key result
438
                            let end_pos = ContentRange::end_position_excluding_delimiter(
74✔
439
                                self.buffer.current_pos(),
74✔
440
                            );
441
                            let key_result = self.copy_on_escape.end_string(end_pos)?;
74✔
442
                            break Ok(Event::Key(key_result));
74✔
443
                        } else {
444
                            break Err(UnexpectedState::StateMismatch.into());
×
445
                        }
446
                    }
447
                    EventResult::ExtractString => {
448
                        if let State::String(_value) = self.parser_state.state {
50✔
449
                            // Check for incomplete surrogate pairs before ending the string
450
                            if self.unicode_escape_collector.has_pending_high_surrogate() {
50✔
451
                                break Err(ParseError::InvalidUnicodeCodepoint);
1✔
452
                            }
49✔
453
                            self.parser_state.state = State::None;
49✔
454
                            // Use CopyOnEscape to get the final string result
455
                            let end_pos = ContentRange::end_position_excluding_delimiter(
49✔
456
                                self.buffer.current_pos(),
49✔
457
                            );
458
                            let value_result = self.copy_on_escape.end_string(end_pos)?;
49✔
459
                            break Ok(Event::String(value_result));
49✔
460
                        } else {
461
                            break Err(UnexpectedState::StateMismatch.into());
×
462
                        }
463
                    }
464
                    EventResult::ExtractNumber(from_container_end) => {
31✔
465
                        if let State::Number(start) = self.parser_state.state {
31✔
466
                            // Reset state before parsing to stop selective copying
467
                            self.parser_state.state = State::None;
31✔
468
                            let number_token_start = start;
31✔
469
                            let event = self
31✔
470
                                .parse_number_from_buffer(number_token_start, from_container_end)?;
31✔
471
                            break Ok(event);
31✔
472
                        } else {
473
                            break Err(UnexpectedState::StateMismatch.into());
×
474
                        }
475
                    }
476
                }
477
            } else {
478
                // No event available - this shouldn't happen since we ensured have_events() above
479
                break Err(UnexpectedState::StateMismatch.into());
×
480
            }
481
        }
482
    }
345✔
483
}
484

485
impl<'a, 'b, C: BitStackConfig> PullParser for SliceParser<'a, 'b, C> {
486
    fn next_event(&mut self) -> Result<Event<'_, '_>, ParseError> {
345✔
487
        self.next_event_impl()
345✔
488
    }
345✔
489
}
490

491
#[cfg(test)]
492
mod tests {
493
    use super::*;
494
    use crate::{ArrayBitStack, BitStackStruct, String};
495

496
    #[test]
497
    fn make_parser() {
1✔
498
        let input = r#"{"key": "value"}"#;
1✔
499
        let mut scratch = [0u8; 1024];
1✔
500
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
501
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
502
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
503
        assert_eq!(
1✔
504
            parser.next_event(),
1✔
505
            Ok(Event::String(String::Borrowed("value")))
506
        );
507
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
508
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
509
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
510
    }
1✔
511

512
    #[test]
513
    fn parse_number() {
1✔
514
        let input = r#"{"key": 124}"#;
1✔
515
        let mut scratch = [0u8; 1024];
1✔
516
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
517
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
518
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
519
        // Check number value using new JsonNumber API
520
        match parser.next_event() {
1✔
521
            Ok(Event::Number(num)) => {
1✔
522
                assert_eq!(num.as_str(), "124");
1✔
523
                assert_eq!(num.as_int(), Some(124));
1✔
524
            }
525
            other => panic!("Expected Number, got: {:?}", other),
×
526
        }
527
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
528
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
529
    }
1✔
530

531
    #[test]
532
    fn parse_bool_and_null() {
1✔
533
        let input = r#"{"key": true, "key2": false, "key3": null}"#;
1✔
534
        let mut scratch = [0u8; 1024];
1✔
535
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
536
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
537
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
538
        assert_eq!(parser.next_event(), Ok(Event::Bool(true)));
1✔
539
        assert_eq!(
1✔
540
            parser.next_event(),
1✔
541
            Ok(Event::Key(String::Borrowed("key2")))
542
        );
543
        assert_eq!(parser.next_event(), Ok(Event::Bool(false)));
1✔
544
        assert_eq!(
1✔
545
            parser.next_event(),
1✔
546
            Ok(Event::Key(String::Borrowed("key3")))
547
        );
548
        assert_eq!(parser.next_event(), Ok(Event::Null));
1✔
549
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
550
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
551
    }
1✔
552

553
    #[test]
554
    fn parse_array() {
1✔
555
        #[cfg(feature = "float-error")]
556
        let input = r#"{"key": [1, 2, 3]}"#; // No floats for float-error config
557
        #[cfg(not(feature = "float-error"))]
558
        let input = r#"{"key": [1, 2.2, 3]}"#; // Include float for other configs
1✔
559

560
        let mut scratch = [0u8; 1024];
1✔
561
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
562
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
563
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
564
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
565

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

575
        // Second number: depends on configuration
576
        match parser.next_event() {
1✔
577
            Ok(Event::Number(num)) => {
1✔
578
                #[cfg(feature = "float-error")]
579
                {
580
                    assert_eq!(num.as_str(), "2");
581
                    assert_eq!(num.as_int(), Some(2));
582
                }
583
                #[cfg(not(feature = "float-error"))]
584
                {
585
                    assert_eq!(num.as_str(), "2.2");
1✔
586
                    #[cfg(feature = "float")]
587
                    assert_eq!(num.as_f64(), Some(2.2));
1✔
588
                    #[cfg(not(feature = "float-error"))]
589
                    assert!(num.is_float());
1✔
590
                }
591
            }
592
            other => panic!("Expected Number, got: {:?}", other),
×
593
        }
594

595
        // Third number: 3 (integer)
596
        match parser.next_event() {
1✔
597
            Ok(Event::Number(num)) => {
1✔
598
                assert_eq!(num.as_str(), "3");
1✔
599
                assert_eq!(num.as_int(), Some(3));
1✔
600
            }
601
            other => panic!("Expected Number(3), got: {:?}", other),
×
602
        }
603

604
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
605
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
606
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
607
    }
1✔
608

609
    #[test]
610
    fn test_simple_parser_api() {
1✔
611
        let input = r#"{"name": "test"}"#;
1✔
612
        let mut scratch = [0u8; 1024];
1✔
613
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
614

615
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
616
        assert_eq!(
1✔
617
            parser.next_event(),
1✔
618
            Ok(Event::Key(String::Borrowed("name")))
619
        );
620
        assert_eq!(
1✔
621
            parser.next_event(),
1✔
622
            Ok(Event::String(String::Borrowed("test")))
623
        );
624
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
625
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
626
    }
1✔
627

628
    #[test]
629
    fn test_parser_with_escaped_strings() {
1✔
630
        // Use regular string literal to properly include escape sequences
631
        let input = "{\"name\": \"John\\nDoe\", \"message\": \"Hello\\tWorld!\"}";
1✔
632
        let mut scratch = [0u8; 1024];
1✔
633
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
634

635
        // Test that the parser correctly handles escaped strings
636
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
637

638
        // Key should be simple (no escapes) -> Borrowed
639
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
640
            assert_eq!(&*key, "name");
1✔
641
            // This should be the fast path (borrowed)
642
            assert!(matches!(key, String::Borrowed(_)));
1✔
643
        } else {
644
            panic!("Expected Key event");
×
645
        }
646

647
        // Value should have escapes -> Unescaped
648
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
649
            assert_eq!(&*value, "John\nDoe");
1✔
650
            // This should be the slow path (unescaped)
651
            assert!(matches!(value, String::Unescaped(_)));
1✔
652
        } else {
653
            panic!("Expected String event");
×
654
        }
655

656
        // Second key should be simple -> Borrowed
657
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
658
            assert_eq!(&*key, "message");
1✔
659
            assert!(matches!(key, String::Borrowed(_)));
1✔
660
        } else {
661
            panic!("Expected Key event");
×
662
        }
663

664
        // Second value should have escapes -> Unescaped
665
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
666
            assert_eq!(&*value, "Hello\tWorld!");
1✔
667
            assert!(matches!(value, String::Unescaped(_)));
1✔
668
        } else {
669
            panic!("Expected String event");
×
670
        }
671

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

675
    #[test]
676
    fn test_copy_on_escape_optimization() {
1✔
677
        // Use regular string literal to include proper escape sequences
678
        let input = "{\"simple\": \"no escapes\", \"complex\": \"has\\nescapes\"}";
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

684
        // "simple" key should be borrowed (fast path)
685
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
686
            assert_eq!(&*key, "simple");
1✔
687
            assert!(matches!(key, String::Borrowed(_)));
1✔
688
        } else {
689
            panic!("Expected Key event");
×
690
        }
691

692
        // "no escapes" value should be borrowed (fast path)
693
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
694
            assert_eq!(&*value, "no escapes");
1✔
695
            assert!(matches!(value, String::Borrowed(_)));
1✔
696
        } else {
697
            panic!("Expected String event");
×
698
        }
699

700
        // "complex" key should be borrowed (fast path)
701
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
702
            assert_eq!(&*key, "complex");
1✔
703
            assert!(matches!(key, String::Borrowed(_)));
1✔
704
        } else {
705
            panic!("Expected Key event");
×
706
        }
707

708
        // "has\\nescapes" value should be unescaped (slow path)
709
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
710
            assert_eq!(&*value, "has\nescapes");
1✔
711
            assert!(matches!(value, String::Unescaped(_)));
1✔
712
        } else {
713
            panic!("Expected String event");
×
714
        }
715

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

720
    #[test]
721
    fn test_coe2_integration_multiple_escapes() {
1✔
722
        let input = r#"{"key": "a\nb\tc\rd"}"#;
1✔
723
        let mut scratch = [0u8; 1024];
1✔
724
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
725

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

729
        let string_event = parser.next_event().unwrap();
1✔
730
        match string_event {
1✔
731
            Event::String(String::Unescaped(s)) => {
1✔
732
                assert_eq!(s, "a\nb\tc\rd");
1✔
733
            }
734
            _ => panic!("Expected unescaped string value, got: {:?}", string_event),
×
735
        }
736
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
737
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
738
    }
1✔
739

740
    #[test]
741
    fn test_coe2_integration_zero_copy_path() {
1✔
742
        let input = r#"{"simple": "no_escapes_here"}"#;
1✔
743
        let mut scratch = [0u8; 1024];
1✔
744
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
745

746
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
747
        assert_eq!(
1✔
748
            parser.next_event(),
1✔
749
            Ok(Event::Key(String::Borrowed("simple")))
750
        );
751

752
        // This should be borrowed (zero-copy) since no escapes
753
        let string_event = parser.next_event().unwrap();
1✔
754
        match string_event {
1✔
755
            Event::String(String::Borrowed(s)) => {
1✔
756
                assert_eq!(s, "no_escapes_here");
1✔
757
            }
758
            _ => panic!(
×
759
                "Expected borrowed string value for zero-copy, got: {:?}",
×
760
                string_event
761
            ),
762
        }
763
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
764
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
765
    }
1✔
766

767
    #[test]
768
    fn test_coe2_integration_mixed_strings() {
1✔
769
        let input = r#"["plain", "with\nescapes", "plain2", "more\tescapes"]"#;
1✔
770
        let mut scratch = [0u8; 1024];
1✔
771
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
772

773
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
774

775
        // First string: no escapes -> borrowed
776
        match parser.next_event().unwrap() {
1✔
777
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain"),
1✔
778
            other => panic!("Expected borrowed string, got: {:?}", other),
×
779
        }
780

781
        // Second string: has escapes -> unescaped
782
        match parser.next_event().unwrap() {
1✔
783
            Event::String(String::Unescaped(s)) => assert_eq!(s, "with\nescapes"),
1✔
784
            other => panic!("Expected unescaped string, got: {:?}", other),
×
785
        }
786

787
        // Third string: no escapes -> borrowed
788
        match parser.next_event().unwrap() {
1✔
789
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain2"),
1✔
790
            other => panic!("Expected borrowed string, got: {:?}", other),
×
791
        }
792

793
        // Fourth string: has escapes -> unescaped
794
        match parser.next_event().unwrap() {
1✔
795
            Event::String(String::Unescaped(s)) => assert_eq!(s, "more\tescapes"),
1✔
796
            other => panic!("Expected unescaped string, got: {:?}", other),
×
797
        }
798

799
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
800
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
801
    }
1✔
802

803
    #[test]
804
    fn test_unicode_escape_integration() {
1✔
805
        let input = r#"{"key": "Hello\u0041World"}"#; // \u0041 = 'A'
1✔
806
        let mut scratch = [0u8; 1024];
1✔
807
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
808

809
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
810
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
811

812
        // The string with Unicode escape should be unescaped
813
        match parser.next_event().unwrap() {
1✔
814
            Event::String(String::Unescaped(s)) => {
1✔
815
                assert_eq!(s, "HelloAWorld");
1✔
816
            }
817
            other => panic!("Expected unescaped string value, got: {:?}", other),
×
818
        }
819

820
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
821
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
822
    }
1✔
823

824
    #[test]
825
    fn test_original_parser_escape_trace() {
1✔
826
        // Test escape sequence processing with logging
827
        let input = r#""a\nb""#;
1✔
828
        let mut scratch = [0u8; 1024];
1✔
829
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
830

831
        // Should get String with unescaped content
832
        let event = parser.next_event().unwrap();
1✔
833
        if let Event::String(s) = event {
1✔
834
            assert_eq!(&*s, "a\nb");
1✔
835
        } else {
836
            panic!("Expected String event, got {:?}", event);
×
837
        }
838

839
        // Should get EndDocument
840
        let event = parser.next_event().unwrap();
1✔
841
        assert_eq!(event, Event::EndDocument);
1✔
842
    }
1✔
843

844
    #[test]
845
    fn make_parser_from_slice() {
1✔
846
        let input = br#"{"key": "value"}"#;
1✔
847
        let mut scratch = [0u8; 1024];
1✔
848
        let mut parser = SliceParser::with_buffer_from_slice(input, &mut scratch);
1✔
849
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
850
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
851
        assert_eq!(
1✔
852
            parser.next_event(),
1✔
853
            Ok(Event::String(String::Borrowed("value")))
854
        );
855
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
856
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
857
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
858
    }
1✔
859

860
    #[test]
861
    fn test_with_config_constructors() {
1✔
862
        // Test with_config constructor (no escapes)
863
        let json = r#"{"simple": "no_escapes"}"#;
1✔
864
        let mut parser = SliceParser::<BitStackStruct<u64, u16>>::with_config(json);
1✔
865

866
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
867
        assert_eq!(
1✔
868
            parser.next_event(),
1✔
869
            Ok(Event::Key(String::Borrowed("simple")))
870
        );
871
        assert_eq!(
1✔
872
            parser.next_event(),
1✔
873
            Ok(Event::String(String::Borrowed("no_escapes")))
874
        );
875
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
876
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
877
    }
1✔
878

879
    #[test]
880
    fn test_with_config_and_buffer_constructors() {
1✔
881
        // Test with_config_and_buffer constructor (with escapes)
882
        let json = r#"{"escaped": "hello\nworld"}"#;
1✔
883
        let mut scratch = [0u8; 64];
1✔
884
        let mut parser =
1✔
885
            SliceParser::<BitStackStruct<u64, u16>>::with_config_and_buffer(json, &mut scratch);
1✔
886

887
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
888
        assert_eq!(
1✔
889
            parser.next_event(),
1✔
890
            Ok(Event::Key(String::Borrowed("escaped")))
891
        );
892

893
        if let Ok(Event::String(s)) = parser.next_event() {
1✔
894
            assert_eq!(s.as_ref(), "hello\nworld"); // Escape should be processed
1✔
895
        } else {
896
            panic!("Expected String event");
×
897
        }
898

899
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
900
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
901
    }
1✔
902

903
    #[test]
904
    fn test_alternative_config_deep_nesting() {
1✔
905
        // Test that custom BitStack configs can handle deeper nesting
906
        let json = r#"{"a":{"b":{"c":{"d":{"e":"deep"}}}}}"#;
1✔
907
        let mut scratch = [0u8; 64];
1✔
908
        let mut parser =
1✔
909
            SliceParser::<ArrayBitStack<8, u32, u16>>::with_config_and_buffer(json, &mut scratch);
1✔
910

911
        // Parse the deep structure
912
        let mut depth = 0;
1✔
913
        while let Ok(event) = parser.next_event() {
17✔
914
            match event {
17✔
915
                Event::StartObject => depth += 1,
5✔
916
                Event::EndObject => depth -= 1,
5✔
917
                Event::EndDocument => break,
1✔
918
                _ => {}
6✔
919
            }
920
        }
921

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