Skip to main content

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