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                        let high = Self::parse_hex4(input, i + 2)?;
361                        i += 6;
362
363                        let codepoint = if (0xD800..=0xDBFF).contains(&high) {
364                            // High surrogate: must be followed by a low surrogate.
365                            if i + 1 >= input.len() || input[i] != b'\\' || input[i + 1] != b'u' {
366                                return Err(DomainError::InvalidInput(
367                                    "Unpaired high surrogate in unicode escape".to_string(),
368                                ));
369                            }
370                            let low = Self::parse_hex4(input, i + 2)?;
371                            if !(0xDC00..=0xDFFF).contains(&low) {
372                                return Err(DomainError::InvalidInput(
373                                    "High surrogate not followed by low surrogate".to_string(),
374                                ));
375                            }
376                            i += 6;
377                            0x10000 + (high - 0xD800) * 0x400 + (low - 0xDC00)
378                        } else if (0xDC00..=0xDFFF).contains(&high) {
379                            return Err(DomainError::InvalidInput(
380                                "Unpaired low surrogate in unicode escape".to_string(),
381                            ));
382                        } else {
383                            high
384                        };
385
386                        let ch = char::from_u32(codepoint).ok_or_else(|| {
387                            DomainError::InvalidInput(
388                                "Invalid unicode codepoint in escape sequence".to_string(),
389                            )
390                        })?;
391                        let mut buf = [0u8; 4];
392                        result.extend_from_slice(ch.encode_utf8(&mut buf).as_bytes());
393                        continue;
394                    }
395                    _ => {
396                        return Err(DomainError::InvalidInput(
397                            "Invalid escape sequence".to_string(),
398                        ));
399                    }
400                }
401                i += 2;
402            } else {
403                result.push(input[i]);
404                i += 1;
405            }
406        }
407
408        String::from_utf8(result)
409            .map_err(|e| DomainError::InvalidInput(format!("Invalid UTF-8: {e}")))
410    }
411
412    /// Parse a 4-digit hex escape (`XXXX` in `\uXXXX`) starting at `pos`.
413    fn parse_hex4(input: &[u8], pos: usize) -> DomainResult<u32> {
414        let hex = input
415            .get(pos..pos + 4)
416            .ok_or_else(|| DomainError::InvalidInput("Invalid unicode escape".to_string()))?;
417        // `from_str_radix` alone would accept a leading `+` (e.g. "+041"); reject
418        // anything but plain hex digits so malformed escapes error instead of
419        // silently decoding to an unintended codepoint.
420        if !hex.iter().all(u8::is_ascii_hexdigit) {
421            return Err(DomainError::InvalidInput(
422                "Invalid unicode escape".to_string(),
423            ));
424        }
425        let hex_str = std::str::from_utf8(hex)
426            .map_err(|_| DomainError::InvalidInput("Invalid unicode escape".to_string()))?;
427        u32::from_str_radix(hex_str, 16)
428            .map_err(|_| DomainError::InvalidInput("Invalid unicode escape".to_string()))
429    }
430}
431
432impl<'a> LazyParser<'a> for ZeroCopyParser<'a> {
433    type Output = LazyJsonValue<'a>;
434    type Error = DomainError;
435
436    fn parse_lazy(&mut self, input: &'a [u8]) -> Result<Self::Output, Self::Error> {
437        // Validate input size first
438        self.validator
439            .validate_input_size(input.len())
440            .map_err(|e| DomainError::SecurityViolation(e.to_string()))?;
441
442        self.input = input;
443        self.position = 0;
444        self.depth = 0;
445
446        self.parse_value()
447    }
448
449    fn remaining(&self) -> &'a [u8] {
450        if self.position < self.input.len() {
451            &self.input[self.position..]
452        } else {
453            &[]
454        }
455    }
456
457    fn is_complete(&self) -> bool {
458        self.position >= self.input.len()
459    }
460
461    fn reset(&mut self) {
462        self.input = &[];
463        self.position = 0;
464        self.depth = 0;
465    }
466}
467
468/// Zero-copy JSON value that references original buffer when possible
469#[derive(Debug, Clone, PartialEq)]
470pub enum LazyJsonValue<'a> {
471    /// String that references original buffer (no escapes)
472    StringBorrowed(&'a [u8]),
473    /// String that required unescaping (allocated)
474    StringOwned(String),
475    /// Number as slice of original buffer
476    NumberSlice(&'a [u8]),
477    /// Boolean value
478    Boolean(bool),
479    /// Null value
480    Null,
481    /// Object as slice of original buffer
482    ObjectSlice(&'a [u8]),
483    /// Array as slice of original buffer
484    ArraySlice(&'a [u8]),
485}
486
487impl<'a> LazyJsonValue<'a> {
488    /// Get value type
489    pub fn value_type(&self) -> ValueType {
490        match self {
491            LazyJsonValue::StringBorrowed(_) | LazyJsonValue::StringOwned(_) => ValueType::String,
492            LazyJsonValue::NumberSlice(_) => ValueType::Number,
493            LazyJsonValue::Boolean(_) => ValueType::Boolean,
494            LazyJsonValue::Null => ValueType::Null,
495            LazyJsonValue::ObjectSlice(_) => ValueType::Object,
496            LazyJsonValue::ArraySlice(_) => ValueType::Array,
497        }
498    }
499
500    /// Convert to string (allocating if needed)
501    pub fn to_string_lossy(&self) -> String {
502        match self {
503            LazyJsonValue::StringBorrowed(bytes) => String::from_utf8_lossy(bytes).to_string(),
504            LazyJsonValue::StringOwned(s) => s.clone(),
505            LazyJsonValue::NumberSlice(bytes) => String::from_utf8_lossy(bytes).to_string(),
506            LazyJsonValue::Boolean(b) => b.to_string(),
507            LazyJsonValue::Null => "null".to_string(),
508            LazyJsonValue::ObjectSlice(bytes) => String::from_utf8_lossy(bytes).to_string(),
509            LazyJsonValue::ArraySlice(bytes) => String::from_utf8_lossy(bytes).to_string(),
510        }
511    }
512
513    /// Try to parse as string without allocation
514    pub fn as_str(&self) -> DomainResult<&str> {
515        match self {
516            LazyJsonValue::StringBorrowed(bytes) => from_utf8(bytes)
517                .map_err(|e| DomainError::InvalidInput(format!("Invalid UTF-8: {e}"))),
518            LazyJsonValue::StringOwned(s) => Ok(s.as_str()),
519            _ => Err(DomainError::InvalidInput(
520                "Value is not a string".to_string(),
521            )),
522        }
523    }
524
525    /// Try to parse as number
526    pub fn as_number(&self) -> DomainResult<f64> {
527        match self {
528            LazyJsonValue::NumberSlice(bytes) => {
529                let s = from_utf8(bytes)
530                    .map_err(|e| DomainError::InvalidInput(format!("Invalid UTF-8: {e}")))?;
531                s.parse::<f64>()
532                    .map_err(|e| DomainError::InvalidInput(format!("Invalid number: {e}")))
533            }
534            _ => Err(DomainError::InvalidInput(
535                "Value is not a number".to_string(),
536            )),
537        }
538    }
539
540    /// Try to parse as boolean
541    pub fn as_boolean(&self) -> DomainResult<bool> {
542        match self {
543            LazyJsonValue::Boolean(b) => Ok(*b),
544            _ => Err(DomainError::InvalidInput(
545                "Value is not a boolean".to_string(),
546            )),
547        }
548    }
549
550    /// Check if value is null
551    pub fn is_null(&self) -> bool {
552        matches!(self, LazyJsonValue::Null)
553    }
554
555    /// Get raw bytes for zero-copy access
556    pub fn as_bytes(&self) -> Option<&'a [u8]> {
557        match self {
558            LazyJsonValue::StringBorrowed(bytes) => Some(bytes),
559            LazyJsonValue::NumberSlice(bytes) => Some(bytes),
560            LazyJsonValue::ObjectSlice(bytes) => Some(bytes),
561            LazyJsonValue::ArraySlice(bytes) => Some(bytes),
562            _ => None,
563        }
564    }
565
566    /// Estimate memory usage (allocated vs referenced)
567    pub fn memory_usage(&self) -> MemoryUsage {
568        match self {
569            LazyJsonValue::StringBorrowed(bytes) => MemoryUsage {
570                allocated_bytes: 0,
571                referenced_bytes: bytes.len(),
572            },
573            LazyJsonValue::StringOwned(s) => MemoryUsage {
574                allocated_bytes: s.len(),
575                referenced_bytes: 0,
576            },
577            LazyJsonValue::NumberSlice(bytes) => MemoryUsage {
578                allocated_bytes: 0,
579                referenced_bytes: bytes.len(),
580            },
581            LazyJsonValue::Boolean(val) => MemoryUsage {
582                allocated_bytes: 0,
583                referenced_bytes: if *val { 4 } else { 5 }, // "true" or "false"
584            },
585            LazyJsonValue::Null => MemoryUsage {
586                allocated_bytes: 0,
587                referenced_bytes: 4, // "null"
588            },
589            LazyJsonValue::ObjectSlice(bytes) => MemoryUsage {
590                allocated_bytes: 0,
591                referenced_bytes: bytes.len(),
592            },
593            LazyJsonValue::ArraySlice(bytes) => MemoryUsage {
594                allocated_bytes: 0,
595                referenced_bytes: bytes.len(),
596            },
597        }
598    }
599}
600
601/// Memory usage statistics for lazy values
602#[derive(Debug, Clone, PartialEq)]
603pub struct MemoryUsage {
604    /// Bytes that were allocated (copied)
605    pub allocated_bytes: usize,
606    /// Bytes that are referenced from original buffer
607    pub referenced_bytes: usize,
608}
609
610impl MemoryUsage {
611    /// Total memory footprint
612    pub fn total(&self) -> usize {
613        self.allocated_bytes + self.referenced_bytes
614    }
615
616    /// Efficiency ratio (0.0 = all copied, 1.0 = all zero-copy)
617    pub fn efficiency(&self) -> f64 {
618        if self.total() == 0 {
619            1.0
620        } else {
621            self.referenced_bytes as f64 / self.total() as f64
622        }
623    }
624}
625
626/// Incremental parser for streaming scenarios
627pub struct IncrementalParser<'a> {
628    buffer: Vec<u8>,
629    _phantom: std::marker::PhantomData<&'a ()>,
630}
631
632impl<'a> Default for IncrementalParser<'a> {
633    fn default() -> Self {
634        Self::new()
635    }
636}
637
638impl<'a> IncrementalParser<'a> {
639    /// Create an empty incremental parser with an 8 KiB initial buffer.
640    pub fn new() -> Self {
641        Self {
642            buffer: Vec::with_capacity(8192), // 8KB initial capacity
643            _phantom: std::marker::PhantomData,
644        }
645    }
646
647    /// Add more data to the parser buffer
648    pub fn feed(&mut self, data: &[u8]) -> DomainResult<()> {
649        self.buffer.extend_from_slice(data);
650        Ok(())
651    }
652
653    /// Parse any complete values from buffer
654    pub fn parse_available(&mut self) -> DomainResult<Vec<LazyJsonValue<'_>>> {
655        // For simplicity, this is a basic implementation
656        // A production version would need more sophisticated buffering
657        if !self.buffer.is_empty() {
658            let mut parser = ZeroCopyParser::new();
659            match parser.parse_lazy(&self.buffer) {
660                Ok(_value) => {
661                    // This is a simplified approach - real implementation would need
662                    // proper lifetime management for incremental parsing
663                    self.buffer.clear();
664                    Ok(vec![])
665                }
666                Err(_e) => Ok(vec![]), // Not enough data yet
667            }
668        } else {
669            Ok(vec![])
670        }
671    }
672
673    /// Check if buffer has complete JSON value
674    pub fn has_complete_value(&self) -> bool {
675        // Simplified check - real implementation would track bracket/brace nesting
676        !self.buffer.is_empty()
677    }
678}
679
680impl<'a> Default for ZeroCopyParser<'a> {
681    fn default() -> Self {
682        Self::new()
683    }
684}
685
686#[cfg(test)]
687mod tests {
688    use super::*;
689
690    #[test]
691    fn test_parse_string() {
692        let mut parser = ZeroCopyParser::new();
693        let input = br#""hello world""#;
694
695        let result = parser.parse_lazy(input).unwrap();
696        match result {
697            LazyJsonValue::StringBorrowed(bytes) => {
698                assert_eq!(bytes, b"hello world");
699            }
700            _ => panic!("Expected string"),
701        }
702    }
703
704    #[test]
705    fn test_parse_escaped_string() {
706        let mut parser = ZeroCopyParser::new();
707        let input = br#""hello \"world\"""#;
708
709        let result = parser.parse_lazy(input).unwrap();
710        match result {
711            LazyJsonValue::StringOwned(s) => {
712                assert_eq!(s, "hello \"world\"");
713            }
714            _ => panic!("Expected owned string due to escapes"),
715        }
716    }
717
718    #[test]
719    fn test_parse_number() {
720        let mut parser = ZeroCopyParser::new();
721        let input = b"123.45";
722
723        let result = parser.parse_lazy(input).unwrap();
724        match result {
725            LazyJsonValue::NumberSlice(bytes) => {
726                assert_eq!(bytes, b"123.45");
727                assert_eq!(result.as_number().unwrap(), 123.45);
728            }
729            _ => panic!("Expected number"),
730        }
731    }
732
733    #[test]
734    fn test_parse_boolean() {
735        let mut parser = ZeroCopyParser::new();
736
737        let result = parser.parse_lazy(b"true").unwrap();
738        assert_eq!(result, LazyJsonValue::Boolean(true));
739
740        parser.reset();
741        let result = parser.parse_lazy(b"false").unwrap();
742        assert_eq!(result, LazyJsonValue::Boolean(false));
743    }
744
745    #[test]
746    fn test_parse_null() {
747        let mut parser = ZeroCopyParser::new();
748        let result = parser.parse_lazy(b"null").unwrap();
749        assert_eq!(result, LazyJsonValue::Null);
750        assert!(result.is_null());
751    }
752
753    #[test]
754    fn test_parse_empty_object() {
755        let mut parser = ZeroCopyParser::new();
756        let result = parser.parse_lazy(b"{}").unwrap();
757
758        match result {
759            LazyJsonValue::ObjectSlice(bytes) => {
760                assert_eq!(bytes, b"{}");
761            }
762            _ => panic!("Expected object"),
763        }
764    }
765
766    #[test]
767    fn test_parse_empty_array() {
768        let mut parser = ZeroCopyParser::new();
769        let result = parser.parse_lazy(b"[]").unwrap();
770
771        match result {
772            LazyJsonValue::ArraySlice(bytes) => {
773                assert_eq!(bytes, b"[]");
774            }
775            _ => panic!("Expected array"),
776        }
777    }
778
779    #[test]
780    fn test_memory_usage() {
781        let mut parser = ZeroCopyParser::new();
782
783        // Zero-copy string
784        let result1 = parser.parse_lazy(br#""hello""#).unwrap();
785        let usage1 = result1.memory_usage();
786        assert_eq!(usage1.allocated_bytes, 0);
787        assert_eq!(usage1.referenced_bytes, 5);
788        assert_eq!(usage1.efficiency(), 1.0);
789
790        // Escaped string (requires allocation)
791        parser.reset();
792        let result2 = parser.parse_lazy(br#""he\"llo""#).unwrap();
793        let usage2 = result2.memory_usage();
794        assert!(usage2.allocated_bytes > 0);
795        assert_eq!(usage2.referenced_bytes, 0);
796        assert_eq!(usage2.efficiency(), 0.0);
797    }
798
799    #[test]
800    fn test_complex_object() {
801        let mut parser = ZeroCopyParser::new();
802        let input = br#"{"name": "test", "value": 42, "active": true}"#;
803
804        let result = parser.parse_lazy(input).unwrap();
805        match result {
806            LazyJsonValue::ObjectSlice(bytes) => {
807                assert_eq!(bytes.len(), input.len());
808            }
809            _ => panic!("Expected object"),
810        }
811    }
812
813    #[test]
814    fn test_parser_reuse() {
815        let mut parser = ZeroCopyParser::new();
816
817        // First parse
818        let result1 = parser.parse_lazy(b"123").unwrap();
819        assert!(matches!(result1, LazyJsonValue::NumberSlice(_)));
820
821        // Reset and reuse
822        parser.reset();
823        let result2 = parser.parse_lazy(br#""hello""#).unwrap();
824        assert!(matches!(result2, LazyJsonValue::StringBorrowed(_)));
825    }
826
827    #[test]
828    fn test_escape_sequence_slash() {
829        let mut parser = ZeroCopyParser::new();
830        let input = br#""path\/to\/file""#;
831
832        let result = parser.parse_lazy(input).unwrap();
833        match result {
834            LazyJsonValue::StringOwned(s) => {
835                assert_eq!(s, "path/to/file");
836            }
837            _ => panic!("Expected owned string due to escapes"),
838        }
839    }
840
841    #[test]
842    fn test_escape_sequence_backspace() {
843        let mut parser = ZeroCopyParser::new();
844        let input = br#""text\bwith\bbackspace""#;
845
846        let result = parser.parse_lazy(input).unwrap();
847        match result {
848            LazyJsonValue::StringOwned(s) => {
849                assert_eq!(s, "text\x08with\x08backspace");
850            }
851            _ => panic!("Expected owned string due to escapes"),
852        }
853    }
854
855    #[test]
856    fn test_escape_sequence_formfeed() {
857        let mut parser = ZeroCopyParser::new();
858        let input = br#""text\fwith\fformfeed""#;
859
860        let result = parser.parse_lazy(input).unwrap();
861        match result {
862            LazyJsonValue::StringOwned(s) => {
863                assert_eq!(s, "text\x0Cwith\x0Cformfeed");
864            }
865            _ => panic!("Expected owned string due to escapes"),
866        }
867    }
868
869    #[test]
870    fn test_escape_sequence_unicode_basic() {
871        let mut parser = ZeroCopyParser::new();
872        let input = br#""text\u0041""#;
873
874        let result = parser.parse_lazy(input).unwrap();
875        match result {
876            LazyJsonValue::StringOwned(s) => {
877                assert_eq!(s, "textA");
878            }
879            _ => panic!("Expected owned string due to escapes"),
880        }
881    }
882
883    #[test]
884    fn test_escape_sequence_unicode_surrogate_pair() {
885        let mut parser = ZeroCopyParser::new();
886        // U+1F600 GRINNING FACE, encoded via a UTF-16 surrogate pair escape
887        let input = br#""\uD83D\uDE00""#;
888
889        let result = parser.parse_lazy(input).unwrap();
890        match result {
891            LazyJsonValue::StringOwned(s) => {
892                assert_eq!(s, "\u{1F600}");
893            }
894            _ => panic!("Expected owned string due to escapes"),
895        }
896    }
897
898    #[test]
899    fn test_escape_sequence_unicode_unpaired_high_surrogate_errors() {
900        let mut parser = ZeroCopyParser::new();
901        // Lone high surrogate with no following low surrogate escape
902        let input = br#""\uD83D""#;
903
904        let result = parser.parse_lazy(input);
905        assert!(result.is_err());
906    }
907
908    #[test]
909    fn test_escape_sequence_unicode_lone_low_surrogate_errors() {
910        let mut parser = ZeroCopyParser::new();
911        let input = br#""\uDE00""#;
912
913        let result = parser.parse_lazy(input);
914        assert!(result.is_err());
915    }
916
917    #[test]
918    fn test_escape_sequence_unicode_hex_too_short_errors() {
919        let mut parser = ZeroCopyParser::new();
920        let input = br#""\u00""#;
921
922        let result = parser.parse_lazy(input);
923        assert!(result.is_err());
924    }
925
926    #[test]
927    fn test_escape_sequence_unicode_invalid_hex_digit_errors() {
928        let mut parser = ZeroCopyParser::new();
929        let input = br#""\uZZZZ""#;
930
931        let result = parser.parse_lazy(input);
932        assert!(result.is_err());
933    }
934
935    #[test]
936    fn test_escape_sequence_unicode_leading_plus_rejected() {
937        let mut parser = ZeroCopyParser::new();
938        // Regression test: `u32::from_str_radix` alone accepts a leading '+',
939        // which must not be treated as a valid hex digit.
940        let input = br#""\u+041""#;
941
942        let result = parser.parse_lazy(input);
943        assert!(result.is_err());
944    }
945
946    #[test]
947    fn test_escape_sequence_unicode_high_surrogate_not_followed_by_escape_errors() {
948        let mut parser = ZeroCopyParser::new();
949        // High surrogate followed by plain characters (no backslash at all).
950        let input = br#""\uD83DAB""#;
951
952        let result = parser.parse_lazy(input);
953        assert!(result.is_err());
954    }
955
956    #[test]
957    fn test_escape_sequence_unicode_high_surrogate_followed_by_non_u_escape_errors() {
958        let mut parser = ZeroCopyParser::new();
959        // High surrogate followed by a `\n` escape rather than `\u`.
960        let input = br#""\uD83D\n""#;
961
962        let result = parser.parse_lazy(input);
963        assert!(result.is_err());
964    }
965
966    #[test]
967    fn test_escape_sequence_unicode_high_surrogate_followed_by_non_low_surrogate_errors() {
968        let mut parser = ZeroCopyParser::new();
969        // Second escape is a valid \u escape but its value is not a low surrogate.
970        let input = br#""\uD83D\u0041""#;
971
972        let result = parser.parse_lazy(input);
973        assert!(result.is_err());
974    }
975
976    #[test]
977    fn test_escape_sequence_unicode_null_codepoint() {
978        let mut parser = ZeroCopyParser::new();
979        let input = br#""\u0000""#;
980
981        let result = parser.parse_lazy(input).unwrap();
982        match result {
983            LazyJsonValue::StringOwned(s) => {
984                assert_eq!(s, "\u{0000}");
985            }
986            _ => panic!("Expected owned string due to escapes"),
987        }
988    }
989
990    #[test]
991    fn test_escape_sequence_unicode_two_byte_char() {
992        let mut parser = ZeroCopyParser::new();
993        // U+00E9 LATIN SMALL LETTER E WITH ACUTE, 2-byte UTF-8
994        let input = br#""\u00e9""#;
995
996        let result = parser.parse_lazy(input).unwrap();
997        match result {
998            LazyJsonValue::StringOwned(s) => {
999                assert_eq!(s, "\u{00e9}");
1000            }
1001            _ => panic!("Expected owned string due to escapes"),
1002        }
1003    }
1004
1005    #[test]
1006    fn test_escape_sequence_unicode_three_byte_char() {
1007        let mut parser = ZeroCopyParser::new();
1008        // U+4E2D CJK UNIFIED IDEOGRAPH, 3-byte UTF-8
1009        let input = br#""\u4e2d""#;
1010
1011        let result = parser.parse_lazy(input).unwrap();
1012        match result {
1013            LazyJsonValue::StringOwned(s) => {
1014                assert_eq!(s, "\u{4e2d}");
1015            }
1016            _ => panic!("Expected owned string due to escapes"),
1017        }
1018    }
1019
1020    #[test]
1021    fn test_escape_sequence_unicode_max_bmp_noncharacter() {
1022        let mut parser = ZeroCopyParser::new();
1023        let input = br#""\uffff""#;
1024
1025        let result = parser.parse_lazy(input).unwrap();
1026        match result {
1027            LazyJsonValue::StringOwned(s) => {
1028                assert_eq!(s, "\u{ffff}");
1029            }
1030            _ => panic!("Expected owned string due to escapes"),
1031        }
1032    }
1033
1034    #[test]
1035    fn test_escape_sequence_unicode_lowercase_hex_surrogate_pair() {
1036        let mut parser = ZeroCopyParser::new();
1037        let input = br#""\ud83d\ude00""#;
1038
1039        let result = parser.parse_lazy(input).unwrap();
1040        match result {
1041            LazyJsonValue::StringOwned(s) => {
1042                assert_eq!(s, "\u{1F600}");
1043            }
1044            _ => panic!("Expected owned string due to escapes"),
1045        }
1046    }
1047
1048    #[test]
1049    fn test_escape_sequence_unicode_surrogate_pair_with_surrounding_ascii() {
1050        let mut parser = ZeroCopyParser::new();
1051        let input = br#""a\uD83D\uDE00b""#;
1052
1053        let result = parser.parse_lazy(input).unwrap();
1054        match result {
1055            LazyJsonValue::StringOwned(s) => {
1056                assert_eq!(s, "a\u{1F600}b");
1057            }
1058            _ => panic!("Expected owned string due to escapes"),
1059        }
1060    }
1061
1062    #[test]
1063    fn test_number_parsing_partial() {
1064        let mut parser = ZeroCopyParser::new();
1065        // Parser reads valid prefix and may not error on trailing invalid chars
1066        let result = parser.parse_lazy(b"123");
1067        assert!(result.is_ok());
1068        assert!(matches!(result.unwrap(), LazyJsonValue::NumberSlice(_)));
1069    }
1070
1071    #[test]
1072    fn test_number_parsing_error_overflow() {
1073        let mut parser = ZeroCopyParser::new();
1074        // Very large number that might cause issues
1075        let input = b"99999999999999999999999999999999999999999999999999";
1076        let result = parser.parse_lazy(input);
1077        // Should either parse as number or fail gracefully
1078        assert!(result.is_ok() || result.is_err());
1079    }
1080
1081    #[test]
1082    fn test_incremental_parser_feed() {
1083        let mut parser = IncrementalParser::new();
1084
1085        // Feed some data
1086        let result = parser.feed(b"{\"key\":");
1087        assert!(result.is_ok());
1088
1089        // Feed more data
1090        let result2 = parser.feed(b"\"value\"}");
1091        assert!(result2.is_ok());
1092    }
1093
1094    #[test]
1095    fn test_incremental_parser_multiple_feeds() {
1096        let mut parser = IncrementalParser::new();
1097
1098        parser.feed(b"[1,").unwrap();
1099        parser.feed(b"2,").unwrap();
1100        parser.feed(b"3]").unwrap();
1101    }
1102
1103    #[test]
1104    fn test_lazy_json_value_matches() {
1105        let num = LazyJsonValue::NumberSlice(b"123");
1106        assert!(matches!(num, LazyJsonValue::NumberSlice(_)));
1107        assert!(!num.is_null());
1108
1109        let null = LazyJsonValue::Null;
1110        assert!(null.is_null());
1111        assert!(!matches!(null, LazyJsonValue::NumberSlice(_)));
1112
1113        let bool_val = LazyJsonValue::Boolean(true);
1114        assert!(matches!(bool_val, LazyJsonValue::Boolean(true)));
1115        assert!(!bool_val.is_null());
1116    }
1117
1118    #[test]
1119    fn test_memory_usage_zero_copy_efficiency() {
1120        let borrowed = LazyJsonValue::StringBorrowed(b"test");
1121        let usage = borrowed.memory_usage();
1122        assert_eq!(usage.efficiency(), 1.0);
1123        assert_eq!(usage.allocated_bytes, 0);
1124
1125        let owned = LazyJsonValue::StringOwned("test".to_string());
1126        let usage2 = owned.memory_usage();
1127        assert_eq!(usage2.efficiency(), 0.0);
1128        assert!(usage2.allocated_bytes > 0);
1129    }
1130}