pjson_rs/parser/
zero_copy.rs

1//! Zero-copy lazy JSON parser with lifetime management
2//!
3//! This parser minimizes memory allocations by working directly with input slices,
4//! providing lazy evaluation and zero-copy string extraction where possible.
5
6use crate::{
7    config::SecurityConfig,
8    domain::{DomainError, DomainResult},
9    parser::ValueType,
10    security::SecurityValidator,
11};
12use std::{marker::PhantomData, str::from_utf8};
13
14/// Zero-copy lazy parser trait with lifetime management
15///
16/// This trait enables parsers that work directly on input buffers without
17/// copying data, using Rust's lifetime system to ensure memory safety.
18pub trait LazyParser<'a> {
19    type Output;
20    type Error;
21
22    /// Parse input lazily, returning references into the original buffer
23    fn parse_lazy(&mut self, input: &'a [u8]) -> Result<Self::Output, Self::Error>;
24
25    /// Get the remaining unparsed bytes
26    fn remaining(&self) -> &'a [u8];
27
28    /// Check if parsing is complete
29    fn is_complete(&self) -> bool;
30
31    /// Reset parser state for reuse
32    fn reset(&mut self);
33}
34
35/// Zero-copy JSON parser implementation
36pub struct ZeroCopyParser<'a> {
37    input: &'a [u8],
38    position: usize,
39    depth: usize,
40    validator: SecurityValidator,
41    _phantom: PhantomData<&'a ()>,
42}
43
44impl<'a> ZeroCopyParser<'a> {
45    /// Create new zero-copy parser
46    pub fn new() -> Self {
47        Self {
48            input: &[],
49            position: 0,
50            depth: 0,
51            validator: SecurityValidator::default(),
52            _phantom: PhantomData,
53        }
54    }
55
56    /// Create parser with custom security configuration
57    pub fn with_security_config(security_config: SecurityConfig) -> Self {
58        Self {
59            input: &[],
60            position: 0,
61            depth: 0,
62            validator: SecurityValidator::new(security_config),
63            _phantom: PhantomData,
64        }
65    }
66
67    /// Parse JSON value starting at current position
68    pub fn parse_value(&mut self) -> DomainResult<LazyJsonValue<'a>> {
69        self.skip_whitespace();
70
71        if self.position >= self.input.len() {
72            return Err(DomainError::InvalidInput(
73                "Unexpected end of input".to_string(),
74            ));
75        }
76
77        let ch = self.input[self.position];
78        match ch {
79            b'"' => self.parse_string(),
80            b'{' => self.parse_object(),
81            b'[' => self.parse_array(),
82            b't' | b'f' => self.parse_boolean(),
83            b'n' => self.parse_null(),
84            b'-' | b'0'..=b'9' => self.parse_number(),
85            _ => {
86                let ch_char = ch as char;
87                Err(DomainError::InvalidInput(format!(
88                    "Unexpected character: {ch_char}"
89                )))
90            }
91        }
92    }
93
94    /// Parse string value without copying
95    fn parse_string(&mut self) -> DomainResult<LazyJsonValue<'a>> {
96        if self.position >= self.input.len() || self.input[self.position] != b'"' {
97            return Err(DomainError::InvalidInput("Expected '\"'".to_string()));
98        }
99
100        let start = self.position + 1; // Skip opening quote
101        self.position += 1;
102
103        // Find closing quote, handling escapes
104        while self.position < self.input.len() {
105            match self.input[self.position] {
106                b'"' => {
107                    let string_slice = &self.input[start..self.position];
108                    self.position += 1; // Skip closing quote
109
110                    // Check if string contains escape sequences
111                    if string_slice.contains(&b'\\') {
112                        // String needs unescaping - we'll need to allocate
113                        let unescaped = self.unescape_string(string_slice)?;
114                        return Ok(LazyJsonValue::StringOwned(unescaped));
115                    } else {
116                        // Zero-copy string reference
117                        return Ok(LazyJsonValue::StringBorrowed(string_slice));
118                    }
119                }
120                b'\\' => {
121                    // Skip escape sequence
122                    self.position += 2;
123                }
124                _ => {
125                    self.position += 1;
126                }
127            }
128        }
129
130        Err(DomainError::InvalidInput("Unterminated string".to_string()))
131    }
132
133    /// Parse object value lazily
134    fn parse_object(&mut self) -> DomainResult<LazyJsonValue<'a>> {
135        self.validator
136            .validate_json_depth(self.depth + 1)
137            .map_err(|e| DomainError::SecurityViolation(e.to_string()))?;
138
139        if self.position >= self.input.len() || self.input[self.position] != b'{' {
140            return Err(DomainError::InvalidInput("Expected '{'".to_string()));
141        }
142
143        let start = self.position;
144        self.position += 1; // Skip '{'
145        self.depth += 1;
146
147        self.skip_whitespace();
148
149        // Handle empty object
150        if self.position < self.input.len() && self.input[self.position] == b'}' {
151            self.position += 1;
152            self.depth -= 1;
153            return Ok(LazyJsonValue::ObjectSlice(
154                &self.input[start..self.position],
155            ));
156        }
157
158        let mut first = true;
159        while self.position < self.input.len() && self.input[self.position] != b'}' {
160            if !first {
161                self.expect_char(b',')?;
162                self.skip_whitespace();
163            }
164            first = false;
165
166            // Parse key (must be string)
167            let _key = self.parse_value()?;
168            self.skip_whitespace();
169            self.expect_char(b':')?;
170            self.skip_whitespace();
171
172            // Parse value
173            let _value = self.parse_value()?;
174            self.skip_whitespace();
175        }
176
177        self.expect_char(b'}')?;
178        self.depth -= 1;
179
180        Ok(LazyJsonValue::ObjectSlice(
181            &self.input[start..self.position],
182        ))
183    }
184
185    /// Parse array value lazily
186    fn parse_array(&mut self) -> DomainResult<LazyJsonValue<'a>> {
187        self.validator
188            .validate_json_depth(self.depth + 1)
189            .map_err(|e| DomainError::SecurityViolation(e.to_string()))?;
190
191        if self.position >= self.input.len() || self.input[self.position] != b'[' {
192            return Err(DomainError::InvalidInput("Expected '['".to_string()));
193        }
194
195        let start = self.position;
196        self.position += 1; // Skip '['
197        self.depth += 1;
198
199        self.skip_whitespace();
200
201        // Handle empty array
202        if self.position < self.input.len() && self.input[self.position] == b']' {
203            self.position += 1;
204            self.depth -= 1;
205            return Ok(LazyJsonValue::ArraySlice(&self.input[start..self.position]));
206        }
207
208        let mut first = true;
209        while self.position < self.input.len() && self.input[self.position] != b']' {
210            if !first {
211                self.expect_char(b',')?;
212                self.skip_whitespace();
213            }
214            first = false;
215
216            // Parse array element
217            let _element = self.parse_value()?;
218            self.skip_whitespace();
219        }
220
221        self.expect_char(b']')?;
222        self.depth -= 1;
223
224        Ok(LazyJsonValue::ArraySlice(&self.input[start..self.position]))
225    }
226
227    /// Parse boolean value
228    fn parse_boolean(&mut self) -> DomainResult<LazyJsonValue<'a>> {
229        if self.position + 4 <= self.input.len()
230            && &self.input[self.position..self.position + 4] == b"true"
231        {
232            self.position += 4;
233            Ok(LazyJsonValue::Boolean(true))
234        } else if self.position + 5 <= self.input.len()
235            && &self.input[self.position..self.position + 5] == b"false"
236        {
237            self.position += 5;
238            Ok(LazyJsonValue::Boolean(false))
239        } else {
240            Err(DomainError::InvalidInput(
241                "Invalid boolean value".to_string(),
242            ))
243        }
244    }
245
246    /// Parse null value
247    fn parse_null(&mut self) -> DomainResult<LazyJsonValue<'a>> {
248        if self.position + 4 <= self.input.len()
249            && &self.input[self.position..self.position + 4] == b"null"
250        {
251            self.position += 4;
252            Ok(LazyJsonValue::Null)
253        } else {
254            Err(DomainError::InvalidInput("Invalid null value".to_string()))
255        }
256    }
257
258    /// Parse number value with zero-copy when possible
259    fn parse_number(&mut self) -> DomainResult<LazyJsonValue<'a>> {
260        let start = self.position;
261
262        // Handle negative sign
263        if self.input[self.position] == b'-' {
264            self.position += 1;
265        }
266
267        // Parse integer part
268        if self.position >= self.input.len() {
269            return Err(DomainError::InvalidInput("Invalid number".to_string()));
270        }
271
272        if self.input[self.position] == b'0' {
273            self.position += 1;
274        } else if self.input[self.position].is_ascii_digit() {
275            while self.position < self.input.len() && self.input[self.position].is_ascii_digit() {
276                self.position += 1;
277            }
278        } else {
279            return Err(DomainError::InvalidInput("Invalid number".to_string()));
280        }
281
282        // Handle decimal part
283        if self.position < self.input.len() && self.input[self.position] == b'.' {
284            self.position += 1;
285            if self.position >= self.input.len() || !self.input[self.position].is_ascii_digit() {
286                return Err(DomainError::InvalidInput(
287                    "Invalid number: missing digits after decimal".to_string(),
288                ));
289            }
290            while self.position < self.input.len() && self.input[self.position].is_ascii_digit() {
291                self.position += 1;
292            }
293        }
294
295        // Handle exponent
296        if self.position < self.input.len()
297            && (self.input[self.position] == b'e' || self.input[self.position] == b'E')
298        {
299            self.position += 1;
300            if self.position < self.input.len()
301                && (self.input[self.position] == b'+' || self.input[self.position] == b'-')
302            {
303                self.position += 1;
304            }
305            if self.position >= self.input.len() || !self.input[self.position].is_ascii_digit() {
306                return Err(DomainError::InvalidInput(
307                    "Invalid number: missing digits in exponent".to_string(),
308                ));
309            }
310            while self.position < self.input.len() && self.input[self.position].is_ascii_digit() {
311                self.position += 1;
312            }
313        }
314
315        let number_slice = &self.input[start..self.position];
316        Ok(LazyJsonValue::NumberSlice(number_slice))
317    }
318
319    /// Skip whitespace characters
320    fn skip_whitespace(&mut self) {
321        while self.position < self.input.len() {
322            match self.input[self.position] {
323                b' ' | b'\t' | b'\n' | b'\r' => {
324                    self.position += 1;
325                }
326                _ => break,
327            }
328        }
329    }
330
331    /// Expect specific character at current position
332    fn expect_char(&mut self, ch: u8) -> DomainResult<()> {
333        if self.position >= self.input.len() || self.input[self.position] != ch {
334            let ch_char = ch as char;
335            return Err(DomainError::InvalidInput(format!("Expected '{ch_char}'")));
336        }
337        self.position += 1;
338        Ok(())
339    }
340
341    /// Unescape string (requires allocation)
342    fn unescape_string(&self, input: &[u8]) -> DomainResult<String> {
343        let mut result = Vec::with_capacity(input.len());
344        let mut i = 0;
345
346        while i < input.len() {
347            if input[i] == b'\\' && i + 1 < input.len() {
348                match input[i + 1] {
349                    b'"' => result.push(b'"'),
350                    b'\\' => result.push(b'\\'),
351                    b'/' => result.push(b'/'),
352                    b'b' => result.push(b'\x08'),
353                    b'f' => result.push(b'\x0C'),
354                    b'n' => result.push(b'\n'),
355                    b'r' => result.push(b'\r'),
356                    b't' => result.push(b'\t'),
357                    b'u' => {
358                        // Unicode escape sequence
359                        if i + 5 < input.len() {
360                            // Simplified: just skip unicode for now
361                            i += 6;
362                            continue;
363                        } else {
364                            return Err(DomainError::InvalidInput(
365                                "Invalid unicode escape".to_string(),
366                            ));
367                        }
368                    }
369                    _ => {
370                        return Err(DomainError::InvalidInput(
371                            "Invalid escape sequence".to_string(),
372                        ));
373                    }
374                }
375                i += 2;
376            } else {
377                result.push(input[i]);
378                i += 1;
379            }
380        }
381
382        String::from_utf8(result)
383            .map_err(|e| DomainError::InvalidInput(format!("Invalid UTF-8: {e}")))
384    }
385}
386
387impl<'a> LazyParser<'a> for ZeroCopyParser<'a> {
388    type Output = LazyJsonValue<'a>;
389    type Error = DomainError;
390
391    fn parse_lazy(&mut self, input: &'a [u8]) -> Result<Self::Output, Self::Error> {
392        // Validate input size first
393        self.validator
394            .validate_input_size(input.len())
395            .map_err(|e| DomainError::SecurityViolation(e.to_string()))?;
396
397        self.input = input;
398        self.position = 0;
399        self.depth = 0;
400
401        self.parse_value()
402    }
403
404    fn remaining(&self) -> &'a [u8] {
405        if self.position < self.input.len() {
406            &self.input[self.position..]
407        } else {
408            &[]
409        }
410    }
411
412    fn is_complete(&self) -> bool {
413        self.position >= self.input.len()
414    }
415
416    fn reset(&mut self) {
417        self.input = &[];
418        self.position = 0;
419        self.depth = 0;
420    }
421}
422
423/// Zero-copy JSON value that references original buffer when possible
424#[derive(Debug, Clone, PartialEq)]
425pub enum LazyJsonValue<'a> {
426    /// String that references original buffer (no escapes)
427    StringBorrowed(&'a [u8]),
428    /// String that required unescaping (allocated)
429    StringOwned(String),
430    /// Number as slice of original buffer
431    NumberSlice(&'a [u8]),
432    /// Boolean value
433    Boolean(bool),
434    /// Null value
435    Null,
436    /// Object as slice of original buffer
437    ObjectSlice(&'a [u8]),
438    /// Array as slice of original buffer
439    ArraySlice(&'a [u8]),
440}
441
442impl<'a> LazyJsonValue<'a> {
443    /// Get value type
444    pub fn value_type(&self) -> ValueType {
445        match self {
446            LazyJsonValue::StringBorrowed(_) | LazyJsonValue::StringOwned(_) => ValueType::String,
447            LazyJsonValue::NumberSlice(_) => ValueType::Number,
448            LazyJsonValue::Boolean(_) => ValueType::Boolean,
449            LazyJsonValue::Null => ValueType::Null,
450            LazyJsonValue::ObjectSlice(_) => ValueType::Object,
451            LazyJsonValue::ArraySlice(_) => ValueType::Array,
452        }
453    }
454
455    /// Convert to string (allocating if needed)
456    pub fn to_string_lossy(&self) -> String {
457        match self {
458            LazyJsonValue::StringBorrowed(bytes) => String::from_utf8_lossy(bytes).to_string(),
459            LazyJsonValue::StringOwned(s) => s.clone(),
460            LazyJsonValue::NumberSlice(bytes) => String::from_utf8_lossy(bytes).to_string(),
461            LazyJsonValue::Boolean(b) => b.to_string(),
462            LazyJsonValue::Null => "null".to_string(),
463            LazyJsonValue::ObjectSlice(bytes) => String::from_utf8_lossy(bytes).to_string(),
464            LazyJsonValue::ArraySlice(bytes) => String::from_utf8_lossy(bytes).to_string(),
465        }
466    }
467
468    /// Try to parse as string without allocation
469    pub fn as_str(&self) -> DomainResult<&str> {
470        match self {
471            LazyJsonValue::StringBorrowed(bytes) => from_utf8(bytes)
472                .map_err(|e| DomainError::InvalidInput(format!("Invalid UTF-8: {e}"))),
473            LazyJsonValue::StringOwned(s) => Ok(s.as_str()),
474            _ => Err(DomainError::InvalidInput(
475                "Value is not a string".to_string(),
476            )),
477        }
478    }
479
480    /// Try to parse as number
481    pub fn as_number(&self) -> DomainResult<f64> {
482        match self {
483            LazyJsonValue::NumberSlice(bytes) => {
484                let s = from_utf8(bytes)
485                    .map_err(|e| DomainError::InvalidInput(format!("Invalid UTF-8: {e}")))?;
486                s.parse::<f64>()
487                    .map_err(|e| DomainError::InvalidInput(format!("Invalid number: {e}")))
488            }
489            _ => Err(DomainError::InvalidInput(
490                "Value is not a number".to_string(),
491            )),
492        }
493    }
494
495    /// Try to parse as boolean
496    pub fn as_boolean(&self) -> DomainResult<bool> {
497        match self {
498            LazyJsonValue::Boolean(b) => Ok(*b),
499            _ => Err(DomainError::InvalidInput(
500                "Value is not a boolean".to_string(),
501            )),
502        }
503    }
504
505    /// Check if value is null
506    pub fn is_null(&self) -> bool {
507        matches!(self, LazyJsonValue::Null)
508    }
509
510    /// Get raw bytes for zero-copy access
511    pub fn as_bytes(&self) -> Option<&'a [u8]> {
512        match self {
513            LazyJsonValue::StringBorrowed(bytes) => Some(bytes),
514            LazyJsonValue::NumberSlice(bytes) => Some(bytes),
515            LazyJsonValue::ObjectSlice(bytes) => Some(bytes),
516            LazyJsonValue::ArraySlice(bytes) => Some(bytes),
517            _ => None,
518        }
519    }
520
521    /// Estimate memory usage (allocated vs referenced)
522    pub fn memory_usage(&self) -> MemoryUsage {
523        match self {
524            LazyJsonValue::StringBorrowed(bytes) => MemoryUsage {
525                allocated_bytes: 0,
526                referenced_bytes: bytes.len(),
527            },
528            LazyJsonValue::StringOwned(s) => MemoryUsage {
529                allocated_bytes: s.len(),
530                referenced_bytes: 0,
531            },
532            LazyJsonValue::NumberSlice(bytes) => MemoryUsage {
533                allocated_bytes: 0,
534                referenced_bytes: bytes.len(),
535            },
536            LazyJsonValue::Boolean(val) => MemoryUsage {
537                allocated_bytes: 0,
538                referenced_bytes: if *val { 4 } else { 5 }, // "true" or "false"
539            },
540            LazyJsonValue::Null => MemoryUsage {
541                allocated_bytes: 0,
542                referenced_bytes: 4, // "null"
543            },
544            LazyJsonValue::ObjectSlice(bytes) => MemoryUsage {
545                allocated_bytes: 0,
546                referenced_bytes: bytes.len(),
547            },
548            LazyJsonValue::ArraySlice(bytes) => MemoryUsage {
549                allocated_bytes: 0,
550                referenced_bytes: bytes.len(),
551            },
552        }
553    }
554}
555
556/// Memory usage statistics for lazy values
557#[derive(Debug, Clone, PartialEq)]
558pub struct MemoryUsage {
559    /// Bytes that were allocated (copied)
560    pub allocated_bytes: usize,
561    /// Bytes that are referenced from original buffer
562    pub referenced_bytes: usize,
563}
564
565impl MemoryUsage {
566    /// Total memory footprint
567    pub fn total(&self) -> usize {
568        self.allocated_bytes + self.referenced_bytes
569    }
570
571    /// Efficiency ratio (0.0 = all copied, 1.0 = all zero-copy)
572    pub fn efficiency(&self) -> f64 {
573        if self.total() == 0 {
574            1.0
575        } else {
576            self.referenced_bytes as f64 / self.total() as f64
577        }
578    }
579}
580
581/// Incremental parser for streaming scenarios
582pub struct IncrementalParser<'a> {
583    #[allow(dead_code)] // Future: base parser for incremental parsing
584    base: ZeroCopyParser<'a>,
585    buffer: Vec<u8>,
586    #[allow(dead_code)] // Future: storage for parsed values in stream
587    complete_values: Vec<LazyJsonValue<'a>>,
588}
589
590impl<'a> Default for IncrementalParser<'a> {
591    fn default() -> Self {
592        Self::new()
593    }
594}
595
596impl<'a> IncrementalParser<'a> {
597    pub fn new() -> Self {
598        Self {
599            base: ZeroCopyParser::new(),
600            buffer: Vec::with_capacity(8192), // 8KB initial capacity
601            complete_values: Vec::new(),
602        }
603    }
604
605    /// Add more data to the parser buffer
606    pub fn feed(&mut self, data: &[u8]) -> DomainResult<()> {
607        self.buffer.extend_from_slice(data);
608        Ok(())
609    }
610
611    /// Parse any complete values from buffer
612    pub fn parse_available(&mut self) -> DomainResult<Vec<LazyJsonValue<'_>>> {
613        // For simplicity, this is a basic implementation
614        // A production version would need more sophisticated buffering
615        if !self.buffer.is_empty() {
616            let mut parser = ZeroCopyParser::new();
617            match parser.parse_lazy(&self.buffer) {
618                Ok(_value) => {
619                    // This is a simplified approach - real implementation would need
620                    // proper lifetime management for incremental parsing
621                    self.buffer.clear();
622                    Ok(vec![])
623                }
624                Err(_e) => Ok(vec![]), // Not enough data yet
625            }
626        } else {
627            Ok(vec![])
628        }
629    }
630
631    /// Check if buffer has complete JSON value
632    pub fn has_complete_value(&self) -> bool {
633        // Simplified check - real implementation would track bracket/brace nesting
634        !self.buffer.is_empty()
635    }
636}
637
638impl<'a> Default for ZeroCopyParser<'a> {
639    fn default() -> Self {
640        Self::new()
641    }
642}
643
644#[cfg(test)]
645mod tests {
646    use super::*;
647
648    #[test]
649    fn test_parse_string() {
650        let mut parser = ZeroCopyParser::new();
651        let input = br#""hello world""#;
652
653        let result = parser.parse_lazy(input).unwrap();
654        match result {
655            LazyJsonValue::StringBorrowed(bytes) => {
656                assert_eq!(bytes, b"hello world");
657            }
658            _ => panic!("Expected string"),
659        }
660    }
661
662    #[test]
663    fn test_parse_escaped_string() {
664        let mut parser = ZeroCopyParser::new();
665        let input = br#""hello \"world\"""#;
666
667        let result = parser.parse_lazy(input).unwrap();
668        match result {
669            LazyJsonValue::StringOwned(s) => {
670                assert_eq!(s, "hello \"world\"");
671            }
672            _ => panic!("Expected owned string due to escapes"),
673        }
674    }
675
676    #[test]
677    fn test_parse_number() {
678        let mut parser = ZeroCopyParser::new();
679        let input = b"123.45";
680
681        let result = parser.parse_lazy(input).unwrap();
682        match result {
683            LazyJsonValue::NumberSlice(bytes) => {
684                assert_eq!(bytes, b"123.45");
685                assert_eq!(result.as_number().unwrap(), 123.45);
686            }
687            _ => panic!("Expected number"),
688        }
689    }
690
691    #[test]
692    fn test_parse_boolean() {
693        let mut parser = ZeroCopyParser::new();
694
695        let result = parser.parse_lazy(b"true").unwrap();
696        assert_eq!(result, LazyJsonValue::Boolean(true));
697
698        parser.reset();
699        let result = parser.parse_lazy(b"false").unwrap();
700        assert_eq!(result, LazyJsonValue::Boolean(false));
701    }
702
703    #[test]
704    fn test_parse_null() {
705        let mut parser = ZeroCopyParser::new();
706        let result = parser.parse_lazy(b"null").unwrap();
707        assert_eq!(result, LazyJsonValue::Null);
708        assert!(result.is_null());
709    }
710
711    #[test]
712    fn test_parse_empty_object() {
713        let mut parser = ZeroCopyParser::new();
714        let result = parser.parse_lazy(b"{}").unwrap();
715
716        match result {
717            LazyJsonValue::ObjectSlice(bytes) => {
718                assert_eq!(bytes, b"{}");
719            }
720            _ => panic!("Expected object"),
721        }
722    }
723
724    #[test]
725    fn test_parse_empty_array() {
726        let mut parser = ZeroCopyParser::new();
727        let result = parser.parse_lazy(b"[]").unwrap();
728
729        match result {
730            LazyJsonValue::ArraySlice(bytes) => {
731                assert_eq!(bytes, b"[]");
732            }
733            _ => panic!("Expected array"),
734        }
735    }
736
737    #[test]
738    fn test_memory_usage() {
739        let mut parser = ZeroCopyParser::new();
740
741        // Zero-copy string
742        let result1 = parser.parse_lazy(br#""hello""#).unwrap();
743        let usage1 = result1.memory_usage();
744        assert_eq!(usage1.allocated_bytes, 0);
745        assert_eq!(usage1.referenced_bytes, 5);
746        assert_eq!(usage1.efficiency(), 1.0);
747
748        // Escaped string (requires allocation)
749        parser.reset();
750        let result2 = parser.parse_lazy(br#""he\"llo""#).unwrap();
751        let usage2 = result2.memory_usage();
752        assert!(usage2.allocated_bytes > 0);
753        assert_eq!(usage2.referenced_bytes, 0);
754        assert_eq!(usage2.efficiency(), 0.0);
755    }
756
757    #[test]
758    fn test_complex_object() {
759        let mut parser = ZeroCopyParser::new();
760        let input = br#"{"name": "test", "value": 42, "active": true}"#;
761
762        let result = parser.parse_lazy(input).unwrap();
763        match result {
764            LazyJsonValue::ObjectSlice(bytes) => {
765                assert_eq!(bytes.len(), input.len());
766            }
767            _ => panic!("Expected object"),
768        }
769    }
770
771    #[test]
772    fn test_parser_reuse() {
773        let mut parser = ZeroCopyParser::new();
774
775        // First parse
776        let result1 = parser.parse_lazy(b"123").unwrap();
777        assert!(matches!(result1, LazyJsonValue::NumberSlice(_)));
778
779        // Reset and reuse
780        parser.reset();
781        let result2 = parser.parse_lazy(br#""hello""#).unwrap();
782        assert!(matches!(result2, LazyJsonValue::StringBorrowed(_)));
783    }
784}