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

kaidokert / picojson-rs / 16259755145

14 Jul 2025 06:34AM UTC coverage: 90.098% (-3.7%) from 93.785%
16259755145

Pull #56

github

web-flow
Merge 9f3cfb7e4 into 6be34a4b2
Pull Request #56: Another big refactor

454 of 677 new or added lines in 9 files covered. (67.06%)

11 existing lines in 3 files now uncovered.

4786 of 5312 relevant lines covered (90.1%)

723.48 hits per line

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

93.12
/picojson/src/slice_parser.rs
1
// SPDX-License-Identifier: Apache-2.0
2

3
use crate::event_processor::{ContentExtractor, EscapeHandler};
4
use crate::parse_error::ParseError;
5
use crate::parser_core::ParserCore;
6
use crate::shared::{ByteProvider, Event, PullParser};
7
use crate::slice_content_builder::SliceContentBuilder;
8
use crate::slice_input_buffer::InputBuffer;
9
use crate::ujson;
10

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

13
/// A pull parser that parses JSON from a slice.
14
///
15
/// Generic over BitStack storage type for configurable nesting depth.
16
// Lifetime 'a is the input buffer lifetime
17
// lifetime 'b is the scratch/copy buffer lifetime
18
pub struct SliceParser<'a, 'b, C: BitStackConfig = DefaultConfig> {
19
    /// The shared parser core that handles the unified event processing loop
20
    parser_core: ParserCore<C::Bucket, C::Counter>,
21
    /// The content builder that handles SliceParser-specific content extraction
22
    content_builder: SliceContentBuilder<'a, 'b>,
23
}
24

25
/// Methods for the pull parser.
26
impl<'a> SliceParser<'a, '_, DefaultConfig> {
27
    /// Creates a new parser for the given JSON input.
28
    ///
29
    /// This parser assumes no string escapes will be encountered. If escapes are found,
30
    /// parsing will fail with `ScratchBufferFull` error.
31
    ///
32
    /// For JSON with potential string escapes, use `with_buffer()` instead.
33
    ///
34
    /// # Arguments
35
    /// * `input` - A string slice containing the JSON data to be parsed.
36
    ///
37
    /// # Example
38
    /// ```
39
    /// use picojson::SliceParser;
40
    /// let parser = SliceParser::new(r#"{"name": "value"}"#);
41
    /// ```
42
    pub fn new(input: &'a str) -> Self {
9✔
43
        Self::new_from_slice(input.as_bytes())
9✔
44
    }
9✔
45

46
    /// Creates a new parser from a byte slice.
47
    ///
48
    /// Assumes no string escapes will be encountered. For JSON with escapes, use [`with_buffer_from_slice`].
49
    ///
50
    /// # Example
51
    /// ```
52
    /// # use picojson::SliceParser;
53
    /// let parser = SliceParser::new_from_slice(br#"{"name": "value"}"#);
54
    /// ```
55
    ///
56
    /// [`with_buffer_from_slice`]: Self::with_buffer_from_slice
57
    pub fn new_from_slice(input: &'a [u8]) -> Self {
9✔
58
        Self::with_config_from_slice(input)
9✔
59
    }
9✔
60
}
61

62
/// Constructor with scratch buffer for SliceParser using DefaultConfig
63
impl<'a, 'b> SliceParser<'a, 'b, DefaultConfig> {
64
    /// Creates a new parser for the given JSON input with external scratch buffer.
65
    ///
66
    /// Use this when your JSON contains string escapes (like `\n`, `\"`, `\u0041`) that
67
    /// need to be unescaped during parsing.
68
    ///
69
    /// # Arguments
70
    /// * `input` - A string slice containing the JSON data to be parsed.
71
    /// * `scratch_buffer` - A mutable byte slice for temporary string unescaping operations.
72
    ///   This buffer needs to be at least as long as the longest
73
    ///   contiguous token (string, key, number) in the input.
74
    ///
75
    /// # Example
76
    /// ```
77
    /// use picojson::SliceParser;
78
    /// let mut scratch = [0u8; 1024];
79
    /// let parser = SliceParser::with_buffer(r#"{"msg": "Hello\nWorld"}"#, &mut scratch);
80
    /// ```
81
    pub fn with_buffer(input: &'a str, scratch_buffer: &'b mut [u8]) -> Self {
53✔
82
        Self::with_buffer_from_slice(input.as_bytes(), scratch_buffer)
53✔
83
    }
53✔
84

85
    /// Creates a new parser from a byte slice with a scratch buffer.
86
    ///
87
    /// Use when JSON contains string escapes that need unescaping.
88
    ///
89
    /// # Example
90
    /// ```
91
    /// # use picojson::SliceParser;
92
    /// let mut scratch = [0u8; 1024];
93
    /// let parser = SliceParser::with_buffer_from_slice(br#"{"msg": "Hello\nWorld"}"#, &mut scratch);
94
    /// ```
95
    pub fn with_buffer_from_slice(input: &'a [u8], scratch_buffer: &'b mut [u8]) -> Self {
54✔
96
        Self::with_config_and_buffer_from_slice(input, scratch_buffer)
54✔
97
    }
54✔
98
}
99

100
/// Generic constructor for SliceParser with custom configurations
101
impl<'a, 'b, C: BitStackConfig> SliceParser<'a, 'b, C> {
102
    /// Creates a new parser with a custom `BitStackConfig`.
103
    ///
104
    /// This parser assumes no string escapes will be encountered. If escapes are found,
105
    /// parsing will fail. For JSON with escapes, use `with_config_and_buffer`.
106
    pub fn with_config(input: &'a str) -> Self {
1✔
107
        Self::with_config_from_slice(input.as_bytes())
1✔
108
    }
1✔
109

110
    /// Creates a new parser from a byte slice with a custom `BitStackConfig`.
111
    ///
112
    /// Assumes no string escapes will be encountered. For JSON with escapes, use [`with_config_and_buffer_from_slice`].
113
    ///
114
    /// [`with_config_and_buffer_from_slice`]: Self::with_config_and_buffer_from_slice
115
    pub fn with_config_from_slice(input: &'a [u8]) -> Self {
10✔
116
        Self::with_config_and_buffer_from_slice(input, &mut [])
10✔
117
    }
10✔
118

119
    /// Creates a new parser with a custom `BitStackConfig` and a user-provided scratch buffer.
120
    ///
121
    /// Use this when your JSON contains string escapes (like `\n`, `\"`, `\u0041`).
122
    ///
123
    /// # Arguments
124
    /// * `input` - A string slice containing the JSON data to be parsed.
125
    /// * `scratch_buffer` - A mutable byte slice for temporary string unescaping operations.
126
    ///   This buffer needs to be at least as long as the longest
127
    ///   contiguous token (string, key, number) in the input.
128
    pub fn with_config_and_buffer(input: &'a str, scratch_buffer: &'b mut [u8]) -> Self {
2✔
129
        Self::with_config_and_buffer_from_slice(input.as_bytes(), scratch_buffer)
2✔
130
    }
2✔
131

132
    /// Creates a new parser from a byte slice with a custom `BitStackConfig` and scratch buffer.
133
    ///
134
    /// Use when JSON contains string escapes that need unescaping.
135
    /// This is the core constructor that all other constructors delegate to.
136
    pub fn with_config_and_buffer_from_slice(
66✔
137
        input: &'a [u8],
66✔
138
        scratch_buffer: &'b mut [u8],
66✔
139
    ) -> Self {
66✔
140
        SliceParser {
66✔
141
            parser_core: ParserCore::new(),
66✔
142
            content_builder: SliceContentBuilder::new(input, scratch_buffer),
66✔
143
        }
66✔
144
    }
66✔
145

146
    /// Returns the next JSON event or an error if parsing fails.
147
    /// Parsing continues until `EndDocument` is returned or an error occurs.
148
    fn next_event_impl(&mut self) -> Result<Event<'_, '_>, ParseError> {
345✔
149
        // We need to implement the unified loop locally to avoid borrowing conflicts
150
        // This is essentially a copy of ParserCore::next_event_impl but accessing fields directly
151
        loop {
152
            while !crate::event_processor::have_events(&self.parser_core.parser_state.evts) {
2,927✔
153
                if let Some(byte) = self.next_byte()? {
2,284✔
154
                    crate::event_processor::process_byte_through_tokenizer(
2,243✔
155
                        byte,
2,243✔
156
                        &mut self.parser_core.tokenizer,
2,243✔
157
                        &mut self.parser_core.parser_state.evts,
2,243✔
158
                    )?;
5✔
159
                } else {
160
                    crate::event_processor::finish_tokenizer(
41✔
161
                        &mut self.parser_core.tokenizer,
41✔
162
                        &mut self.parser_core.parser_state.evts,
41✔
163
                    )?;
3✔
164

165
                    if !crate::event_processor::have_events(&self.parser_core.parser_state.evts) {
38✔
166
                        return Ok(Event::EndDocument);
35✔
167
                    }
3✔
168
                }
169
            }
170

171
            let taken_event =
643✔
172
                crate::event_processor::take_first_event(&mut self.parser_core.parser_state.evts);
643✔
173
            let Some(taken) = taken_event else {
643✔
NEW
174
                return Err(crate::shared::UnexpectedState::StateMismatch.into());
×
175
            };
176

177
            // Try shared event processors first
178
            if let Some(result) =
478✔
179
                crate::event_processor::process_simple_events(&taken).or_else(|| {
643✔
180
                    crate::event_processor::process_begin_events(&taken, &mut self.content_builder)
351✔
181
                })
351✔
182
            {
183
                match result {
478✔
184
                    crate::event_processor::EventResult::Complete(event) => return Ok(event),
134✔
185
                    crate::event_processor::EventResult::ExtractString => {
186
                        return self.content_builder.validate_and_extract_string()
50✔
187
                    }
188
                    crate::event_processor::EventResult::ExtractKey => {
189
                        return self.content_builder.validate_and_extract_key()
74✔
190
                    }
191
                    crate::event_processor::EventResult::ExtractNumber(from_container_end) => {
34✔
192
                        return self.extract_number_with_finished_state(from_container_end)
34✔
193
                    }
194
                    crate::event_processor::EventResult::Continue => continue,
186✔
195
                }
196
            }
165✔
197

198
            // Handle parser-specific events (SliceParser uses OnBegin timing)
199
            match taken {
20✔
200
                ujson::Event::Begin(crate::ujson::EventToken::EscapeSequence) => {
201
                    crate::event_processor::process_begin_escape_sequence_event(
57✔
202
                        &mut self.content_builder,
57✔
NEW
203
                    )?;
×
204
                }
205
                _ if crate::event_processor::process_unicode_escape_events(
108✔
206
                    &taken,
108✔
207
                    &mut self.content_builder,
108✔
208
                )? =>
62✔
209
                {
62✔
210
                    // Unicode escape events handled by shared function
62✔
211
                }
62✔
212
                ujson::Event::Begin(
213
                    escape_token @ (crate::ujson::EventToken::EscapeQuote
20✔
214
                    | crate::ujson::EventToken::EscapeBackslash
215
                    | crate::ujson::EventToken::EscapeSlash
216
                    | crate::ujson::EventToken::EscapeBackspace
217
                    | crate::ujson::EventToken::EscapeFormFeed
218
                    | crate::ujson::EventToken::EscapeNewline
219
                    | crate::ujson::EventToken::EscapeCarriageReturn
220
                    | crate::ujson::EventToken::EscapeTab),
221
                ) => {
222
                    // SliceParser-specific: Handle simple escape sequences on Begin events
223
                    // because CopyOnEscape requires starting unescaping immediately when
224
                    // the escape token begins to maintain zero-copy optimization
225
                    crate::event_processor::process_simple_escape_event(
20✔
226
                        &escape_token,
20✔
227
                        &mut self.content_builder,
20✔
228
                    )?;
2✔
229
                }
230
                _ => {
18✔
231
                    // All other events continue to next iteration
18✔
232
                }
18✔
233
            }
234
        }
235
    }
345✔
236

237
    /// Extract number with proper SliceParser document end detection
238
    fn extract_number_with_finished_state(
34✔
239
        &mut self,
34✔
240
        from_container_end: bool,
34✔
241
    ) -> Result<Event<'_, '_>, ParseError> {
34✔
242
        let start_pos = match *self.content_builder.parser_state() {
34✔
243
            crate::shared::State::Number(pos) => pos,
34✔
NEW
244
            _ => return Err(crate::shared::UnexpectedState::StateMismatch.into()),
×
245
        };
246

247
        *self.content_builder.parser_state_mut() = crate::shared::State::None;
34✔
248

249
        // SliceParser doesn't use the finished parameter - it uses buffer emptiness
250
        // Pass finished=false since SliceParser handles document end differently
251
        use crate::content_builder::ContentBuilder;
252
        self.content_builder
34✔
253
            .extract_number(start_pos, from_container_end, false)
34✔
254
    }
34✔
255
}
256

257
impl<'a, 'b, C: BitStackConfig> PullParser for SliceParser<'a, 'b, C> {
258
    fn next_event(&mut self) -> Result<Event<'_, '_>, ParseError> {
348✔
259
        if self.content_builder.buffer().is_past_end() {
348✔
260
            return Ok(Event::EndDocument);
3✔
261
        }
345✔
262
        self.next_event_impl()
345✔
263
    }
348✔
264
}
265

266
impl<'a, 'b, C: BitStackConfig> crate::shared::ByteProvider for SliceParser<'a, 'b, C> {
267
    fn next_byte(&mut self) -> Result<Option<u8>, ParseError> {
2,284✔
268
        use crate::slice_input_buffer::InputBuffer;
269
        match self.content_builder.buffer_mut().consume_byte() {
2,284✔
270
            Ok(byte) => Ok(Some(byte)),
2,243✔
271
            Err(crate::slice_input_buffer::Error::ReachedEnd) => Ok(None),
41✔
272
            Err(err) => Err(err.into()),
×
273
        }
274
    }
2,284✔
275
}
276

277
#[cfg(test)]
278
mod tests {
279
    use super::*;
280
    use crate::{ArrayBitStack, BitStackStruct, String};
281

282
    #[test]
283
    fn make_parser() {
1✔
284
        let input = r#"{"key": "value"}"#;
1✔
285
        let mut scratch = [0u8; 1024];
1✔
286
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
287
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
288
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
289
        assert_eq!(
1✔
290
            parser.next_event(),
1✔
291
            Ok(Event::String(String::Borrowed("value")))
292
        );
293
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
294
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
295
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
296
    }
1✔
297

298
    #[test]
299
    fn parse_number() {
1✔
300
        let input = r#"{"key": 124}"#;
1✔
301
        let mut scratch = [0u8; 1024];
1✔
302
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
303
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
304
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
305
        // Check number value using new JsonNumber API
306
        match parser.next_event() {
1✔
307
            Ok(Event::Number(num)) => {
1✔
308
                assert_eq!(num.as_str(), "124");
1✔
309
                assert_eq!(num.as_int(), Some(124));
1✔
310
            }
311
            other => panic!("Expected Number, got: {:?}", other),
×
312
        }
313
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
314
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
315
    }
1✔
316

317
    #[test]
318
    fn parse_bool_and_null() {
1✔
319
        let input = r#"{"key": true, "key2": false, "key3": null}"#;
1✔
320
        let mut scratch = [0u8; 1024];
1✔
321
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
322
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
323
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
324
        assert_eq!(parser.next_event(), Ok(Event::Bool(true)));
1✔
325
        assert_eq!(
1✔
326
            parser.next_event(),
1✔
327
            Ok(Event::Key(String::Borrowed("key2")))
328
        );
329
        assert_eq!(parser.next_event(), Ok(Event::Bool(false)));
1✔
330
        assert_eq!(
1✔
331
            parser.next_event(),
1✔
332
            Ok(Event::Key(String::Borrowed("key3")))
333
        );
334
        assert_eq!(parser.next_event(), Ok(Event::Null));
1✔
335
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
336
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
337
    }
1✔
338

339
    #[test]
340
    fn parse_array() {
1✔
341
        #[cfg(feature = "float-error")]
342
        let input = r#"{"key": [1, 2, 3]}"#; // No floats for float-error config
343
        #[cfg(not(feature = "float-error"))]
344
        let input = r#"{"key": [1, 2.2, 3]}"#; // Include float for other configs
1✔
345

346
        let mut scratch = [0u8; 1024];
1✔
347
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
348
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
349
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
350
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
351

352
        // First number: 1 (integer)
353
        match parser.next_event() {
1✔
354
            Ok(Event::Number(num)) => {
1✔
355
                assert_eq!(num.as_str(), "1");
1✔
356
                assert_eq!(num.as_int(), Some(1));
1✔
357
            }
358
            other => panic!("Expected Number(1), got: {:?}", other),
×
359
        }
360

361
        // Second number: depends on configuration
362
        match parser.next_event() {
1✔
363
            Ok(Event::Number(num)) => {
1✔
364
                #[cfg(feature = "float-error")]
365
                {
366
                    assert_eq!(num.as_str(), "2");
367
                    assert_eq!(num.as_int(), Some(2));
368
                }
369
                #[cfg(not(feature = "float-error"))]
370
                {
371
                    assert_eq!(num.as_str(), "2.2");
1✔
372
                    #[cfg(feature = "float")]
373
                    assert_eq!(num.as_f64(), Some(2.2));
1✔
374
                    #[cfg(not(feature = "float-error"))]
375
                    assert!(num.is_float());
1✔
376
                }
377
            }
378
            other => panic!("Expected Number, got: {:?}", other),
×
379
        }
380

381
        // Third number: 3 (integer)
382
        match parser.next_event() {
1✔
383
            Ok(Event::Number(num)) => {
1✔
384
                assert_eq!(num.as_str(), "3");
1✔
385
                assert_eq!(num.as_int(), Some(3));
1✔
386
            }
387
            other => panic!("Expected Number(3), got: {:?}", other),
×
388
        }
389

390
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
391
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
392
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
393
    }
1✔
394

395
    #[test]
396
    fn test_simple_parser_api() {
1✔
397
        let input = r#"{"name": "test"}"#;
1✔
398
        let mut scratch = [0u8; 1024];
1✔
399
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
400

401
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
402
        assert_eq!(
1✔
403
            parser.next_event(),
1✔
404
            Ok(Event::Key(String::Borrowed("name")))
405
        );
406
        assert_eq!(
1✔
407
            parser.next_event(),
1✔
408
            Ok(Event::String(String::Borrowed("test")))
409
        );
410
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
411
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
412
    }
1✔
413

414
    #[test]
415
    fn test_parser_with_escaped_strings() {
1✔
416
        // Use regular string literal to properly include escape sequences
417
        let input = "{\"name\": \"John\\nDoe\", \"message\": \"Hello\\tWorld!\"}";
1✔
418
        let mut scratch = [0u8; 1024];
1✔
419
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
420

421
        // Test that the parser correctly handles escaped strings
422
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
423

424
        // Key should be simple (no escapes) -> Borrowed
425
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
426
            assert_eq!(&*key, "name");
1✔
427
            // This should be the fast path (borrowed)
428
            assert!(matches!(key, String::Borrowed(_)));
1✔
429
        } else {
430
            panic!("Expected Key event");
×
431
        }
432

433
        // Value should have escapes -> Unescaped
434
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
435
            assert_eq!(&*value, "John\nDoe");
1✔
436
            // This should be the slow path (unescaped)
437
            assert!(matches!(value, String::Unescaped(_)));
1✔
438
        } else {
439
            panic!("Expected String event");
×
440
        }
441

442
        // Second key should be simple -> Borrowed
443
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
444
            assert_eq!(&*key, "message");
1✔
445
            assert!(matches!(key, String::Borrowed(_)));
1✔
446
        } else {
447
            panic!("Expected Key event");
×
448
        }
449

450
        // Second value should have escapes -> Unescaped
451
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
452
            assert_eq!(&*value, "Hello\tWorld!");
1✔
453
            assert!(matches!(value, String::Unescaped(_)));
1✔
454
        } else {
455
            panic!("Expected String event");
×
456
        }
457

458
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
459
    }
1✔
460

461
    #[test]
462
    fn test_copy_on_escape_optimization() {
1✔
463
        // Use regular string literal to include proper escape sequences
464
        let input = "{\"simple\": \"no escapes\", \"complex\": \"has\\nescapes\"}";
1✔
465
        let mut scratch = [0u8; 1024];
1✔
466
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
467

468
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
469

470
        // "simple" key should be borrowed (fast path)
471
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
472
            assert_eq!(&*key, "simple");
1✔
473
            assert!(matches!(key, String::Borrowed(_)));
1✔
474
        } else {
475
            panic!("Expected Key event");
×
476
        }
477

478
        // "no escapes" value should be borrowed (fast path)
479
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
480
            assert_eq!(&*value, "no escapes");
1✔
481
            assert!(matches!(value, String::Borrowed(_)));
1✔
482
        } else {
483
            panic!("Expected String event");
×
484
        }
485

486
        // "complex" key should be borrowed (fast path)
487
        if let Ok(Event::Key(key)) = parser.next_event() {
1✔
488
            assert_eq!(&*key, "complex");
1✔
489
            assert!(matches!(key, String::Borrowed(_)));
1✔
490
        } else {
491
            panic!("Expected Key event");
×
492
        }
493

494
        // "has\\nescapes" value should be unescaped (slow path)
495
        if let Ok(Event::String(value)) = parser.next_event() {
1✔
496
            assert_eq!(&*value, "has\nescapes");
1✔
497
            assert!(matches!(value, String::Unescaped(_)));
1✔
498
        } else {
499
            panic!("Expected String event");
×
500
        }
501

502
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
503
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
504
    }
1✔
505

506
    #[test]
507
    fn test_coe2_integration_multiple_escapes() {
1✔
508
        let input = r#"{"key": "a\nb\tc\rd"}"#;
1✔
509
        let mut scratch = [0u8; 1024];
1✔
510
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
511

512
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
513
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
514

515
        let string_event = parser.next_event().unwrap();
1✔
516
        match string_event {
1✔
517
            Event::String(String::Unescaped(s)) => {
1✔
518
                assert_eq!(s, "a\nb\tc\rd");
1✔
519
            }
520
            _ => panic!("Expected unescaped string value, got: {:?}", string_event),
×
521
        }
522
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
523
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
524
    }
1✔
525

526
    #[test]
527
    fn test_coe2_integration_zero_copy_path() {
1✔
528
        let input = r#"{"simple": "no_escapes_here"}"#;
1✔
529
        let mut scratch = [0u8; 1024];
1✔
530
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
531

532
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
533
        assert_eq!(
1✔
534
            parser.next_event(),
1✔
535
            Ok(Event::Key(String::Borrowed("simple")))
536
        );
537

538
        // This should be borrowed (zero-copy) since no escapes
539
        let string_event = parser.next_event().unwrap();
1✔
540
        match string_event {
1✔
541
            Event::String(String::Borrowed(s)) => {
1✔
542
                assert_eq!(s, "no_escapes_here");
1✔
543
            }
544
            _ => panic!(
×
545
                "Expected borrowed string value for zero-copy, got: {:?}",
×
546
                string_event
547
            ),
548
        }
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 test_coe2_integration_mixed_strings() {
1✔
555
        let input = r#"["plain", "with\nescapes", "plain2", "more\tescapes"]"#;
1✔
556
        let mut scratch = [0u8; 1024];
1✔
557
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
558

559
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
1✔
560

561
        // First string: no escapes -> borrowed
562
        match parser.next_event().unwrap() {
1✔
563
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain"),
1✔
564
            other => panic!("Expected borrowed string, got: {:?}", other),
×
565
        }
566

567
        // Second string: has escapes -> unescaped
568
        match parser.next_event().unwrap() {
1✔
569
            Event::String(String::Unescaped(s)) => assert_eq!(s, "with\nescapes"),
1✔
570
            other => panic!("Expected unescaped string, got: {:?}", other),
×
571
        }
572

573
        // Third string: no escapes -> borrowed
574
        match parser.next_event().unwrap() {
1✔
575
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain2"),
1✔
576
            other => panic!("Expected borrowed string, got: {:?}", other),
×
577
        }
578

579
        // Fourth string: has escapes -> unescaped
580
        match parser.next_event().unwrap() {
1✔
581
            Event::String(String::Unescaped(s)) => assert_eq!(s, "more\tescapes"),
1✔
582
            other => panic!("Expected unescaped string, got: {:?}", other),
×
583
        }
584

585
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
1✔
586
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
587
    }
1✔
588

589
    #[test]
590
    fn test_unicode_escape_integration() {
1✔
591
        let input = r#"{"key": "Hello\u0041World"}"#; // \u0041 = 'A'
1✔
592
        println!("=== SLICE PARSER DEBUG ===");
1✔
593
        println!("Input: {}", input);
1✔
594
        println!("Expected in string: 'HelloAWorld'");
1✔
595

596
        // Print byte positions for the string part
597
        let string_start = input.find("Hello").unwrap();
1✔
598
        let string_part = &input[string_start..];
1✔
599
        println!("String part: '{}'", string_part);
1✔
600
        for (i, &byte) in string_part.as_bytes().iter().enumerate() {
18✔
601
            println!(
18✔
602
                "  pos {}: '{}' ({:02x})",
18✔
603
                string_start + i,
18✔
604
                byte as char,
18✔
605
                byte
18✔
606
            );
18✔
607
        }
18✔
608

609
        let mut scratch = [0u8; 1024];
1✔
610
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
611

612
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
613
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
614

615
        // The string with Unicode escape should be unescaped
616
        match parser.next_event().unwrap() {
1✔
617
            Event::String(String::Unescaped(s)) => {
1✔
618
                println!("=== RESULT ANALYSIS ===");
1✔
619
                println!("Expected: 'HelloAWorld'");
1✔
620
                println!("Got:      '{}'", s);
1✔
621
                println!("Character breakdown:");
1✔
622
                for (i, ch) in s.char_indices() {
11✔
623
                    println!("  [{}] '{}' = U+{:04X}", i, ch, ch as u32);
11✔
624
                }
11✔
625
                assert_eq!(s, "HelloAWorld");
1✔
626
            }
627
            other => panic!("Expected unescaped string value, got: {:?}", other),
×
628
        }
629

630
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
631
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
632
    }
1✔
633

634
    #[test]
635
    fn test_original_parser_escape_trace() {
1✔
636
        // Test escape sequence processing with logging
637
        let input = r#""a\nb""#;
1✔
638
        let mut scratch = [0u8; 1024];
1✔
639
        let mut parser = SliceParser::with_buffer(input, &mut scratch);
1✔
640

641
        // Should get String with unescaped content
642
        let event = parser.next_event().unwrap();
1✔
643
        if let Event::String(s) = event {
1✔
644
            assert_eq!(&*s, "a\nb");
1✔
645
        } else {
646
            panic!("Expected String event, got {:?}", event);
×
647
        }
648

649
        // Should get EndDocument
650
        let event = parser.next_event().unwrap();
1✔
651
        assert_eq!(event, Event::EndDocument);
1✔
652
    }
1✔
653

654
    #[test]
655
    fn make_parser_from_slice() {
1✔
656
        let input = br#"{"key": "value"}"#;
1✔
657
        let mut scratch = [0u8; 1024];
1✔
658
        let mut parser = SliceParser::with_buffer_from_slice(input, &mut scratch);
1✔
659
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
660
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
1✔
661
        assert_eq!(
1✔
662
            parser.next_event(),
1✔
663
            Ok(Event::String(String::Borrowed("value")))
664
        );
665
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
666
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
667
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
668
    }
1✔
669

670
    #[test]
671
    fn test_with_config_constructors() {
1✔
672
        // Test with_config constructor (no escapes)
673
        let json = r#"{"simple": "no_escapes"}"#;
1✔
674
        let mut parser = SliceParser::<BitStackStruct<u64, u16>>::with_config(json);
1✔
675

676
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
677
        assert_eq!(
1✔
678
            parser.next_event(),
1✔
679
            Ok(Event::Key(String::Borrowed("simple")))
680
        );
681
        assert_eq!(
1✔
682
            parser.next_event(),
1✔
683
            Ok(Event::String(String::Borrowed("no_escapes")))
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_with_config_and_buffer_constructors() {
1✔
691
        // Test with_config_and_buffer constructor (with escapes)
692
        let json = r#"{"escaped": "hello\nworld"}"#;
1✔
693
        let mut scratch = [0u8; 64];
1✔
694
        let mut parser =
1✔
695
            SliceParser::<BitStackStruct<u64, u16>>::with_config_and_buffer(json, &mut scratch);
1✔
696

697
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
1✔
698
        assert_eq!(
1✔
699
            parser.next_event(),
1✔
700
            Ok(Event::Key(String::Borrowed("escaped")))
701
        );
702

703
        if let Ok(Event::String(s)) = parser.next_event() {
1✔
704
            assert_eq!(s.as_ref(), "hello\nworld"); // Escape should be processed
1✔
705
        } else {
706
            panic!("Expected String event");
×
707
        }
708

709
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
1✔
710
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
1✔
711
    }
1✔
712

713
    #[test]
714
    fn test_alternative_config_deep_nesting() {
1✔
715
        // Test that custom BitStack configs can handle deeper nesting
716
        let json = r#"{"a":{"b":{"c":{"d":{"e":"deep"}}}}}"#;
1✔
717
        let mut scratch = [0u8; 64];
1✔
718
        let mut parser =
1✔
719
            SliceParser::<ArrayBitStack<8, u32, u16>>::with_config_and_buffer(json, &mut scratch);
1✔
720

721
        // Parse the deep structure
722
        let mut depth = 0;
1✔
723
        while let Ok(event) = parser.next_event() {
17✔
724
            match event {
17✔
725
                Event::StartObject => depth += 1,
5✔
726
                Event::EndObject => depth -= 1,
5✔
727
                Event::EndDocument => break,
1✔
728
                _ => {}
6✔
729
            }
730
        }
731

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