pjson_rs/parser/
mod.rs

1//! High-performance JSON parsing module with hybrid approach
2//!
3//! This module provides both SIMD-optimized parsing and serde fallback,
4//! allowing rapid MVP development while building towards maximum performance.
5
6pub mod buffer_pool;
7pub mod scanner;
8pub mod simd;
9pub mod simd_zero_copy;
10pub mod simple;
11pub mod sonic;
12pub mod value;
13pub mod zero_copy;
14
15pub use buffer_pool::{BufferPool, PooledBuffer, BufferSize, PoolConfig, global_buffer_pool};
16pub use scanner::{JsonScanner, ScanResult, StringLocation};
17pub use simd_zero_copy::{SimdZeroCopyParser, SimdZeroCopyConfig, SimdParseResult, SimdParsingStats};
18pub use simple::{ParseConfig, ParseStats, SimpleParser};
19pub use sonic::{LazyFrame, SonicConfig, SonicParser};
20pub use value::{JsonValue, LazyArray, LazyObject};
21pub use zero_copy::{LazyParser, ZeroCopyParser, LazyJsonValue, MemoryUsage, IncrementalParser};
22
23use crate::{Result, SemanticMeta};
24
25/// High-performance hybrid parser with SIMD acceleration
26pub struct Parser {
27    sonic: SonicParser,
28    simple: SimpleParser,
29    zero_copy_simd: Option<SimdZeroCopyParser<'static>>,
30    use_sonic: bool,
31    use_zero_copy: bool,
32}
33
34impl Parser {
35    /// Create new parser with default configuration (sonic-rs enabled)
36    pub fn new() -> Self {
37        Self {
38            sonic: SonicParser::new(),
39            simple: SimpleParser::new(),
40            zero_copy_simd: None, // Lazy initialization
41            use_sonic: true,
42            use_zero_copy: true, // Enable zero-copy by default
43        }
44    }
45
46    /// Create parser with custom configuration
47    pub fn with_config(config: ParseConfig) -> Self {
48        let sonic_config = SonicConfig {
49            detect_semantics: config.detect_semantics,
50            max_input_size: config.max_size_mb * 1024 * 1024,
51        };
52
53        Self {
54            sonic: SonicParser::with_config(sonic_config),
55            simple: SimpleParser::with_config(config),
56            zero_copy_simd: None, // Lazy initialization
57            use_sonic: true,
58            use_zero_copy: true,
59        }
60    }
61
62    /// Create parser with serde fallback (for compatibility)
63    pub fn with_serde_fallback() -> Self {
64        Self {
65            sonic: SonicParser::new(),
66            simple: SimpleParser::new(),
67            zero_copy_simd: None,
68            use_sonic: false,
69            use_zero_copy: false, // Disable zero-copy for compatibility
70        }
71    }
72
73    /// Create parser optimized for zero-copy performance
74    pub fn zero_copy_optimized() -> Self {
75        Self {
76            sonic: SonicParser::new(),
77            simple: SimpleParser::new(),
78            zero_copy_simd: None, // Will be initialized on first use
79            use_sonic: false, // Use zero-copy instead
80            use_zero_copy: true,
81        }
82    }
83
84    /// Parse JSON bytes into PJS Frame using optimal strategy
85    pub fn parse(&self, input: &[u8]) -> Result<crate::Frame> {
86        if self.use_sonic {
87            // Try sonic-rs first for performance
88            match self.sonic.parse(input) {
89                Ok(frame) => Ok(frame),
90                Err(_) => {
91                    // Fallback to serde for compatibility
92                    self.simple.parse(input)
93                }
94            }
95        } else {
96            self.simple.parse(input)
97        }
98    }
99
100    /// Parse with explicit semantic hints
101    pub fn parse_with_semantics(
102        &self,
103        input: &[u8],
104        semantics: &SemanticMeta,
105    ) -> Result<crate::Frame> {
106        if self.use_sonic {
107            // Sonic parser doesn't support explicit semantics yet
108            // Use simple parser for this case
109            self.simple.parse_with_semantics(input, semantics)
110        } else {
111            self.simple.parse_with_semantics(input, semantics)
112        }
113    }
114
115    /// Get parser statistics (simplified for now)
116    pub fn stats(&self) -> ParseStats {
117        if self.use_sonic {
118            // TODO: Implement stats collection for sonic parser
119            ParseStats::default()
120        } else {
121            self.simple.stats()
122        }
123    }
124}
125
126impl Default for Parser {
127    fn default() -> Self {
128        Self::new()
129    }
130}
131
132/// JSON value types for initial classification
133#[derive(Debug, Clone, Copy, PartialEq)]
134pub enum ValueType {
135    Object,
136    Array,
137    String,
138    Number,
139    Boolean,
140    Null,
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn test_parser_creation() {
149        let parser = Parser::new();
150        assert_eq!(parser.stats().total_parses, 0);
151    }
152
153    #[test]
154    fn test_simple_parsing() {
155        let parser = Parser::new();
156        let input = br#"{"hello": "world"}"#;
157        let result = parser.parse(input);
158        assert!(result.is_ok());
159
160        let frame = result.unwrap();
161        // Simple JSON may not have semantic metadata
162        assert_eq!(frame.payload.len(), input.len());
163    }
164
165    #[test]
166    fn test_numeric_array_parsing() {
167        let parser = Parser::new();
168        let input = b"[1.0, 2.0, 3.0, 4.0]";
169        let result = parser.parse(input);
170        assert!(result.is_ok());
171    }
172
173    #[test]
174    fn test_semantic_parsing() {
175        let parser = Parser::new();
176        let input = b"[1, 2, 3, 4]";
177
178        let semantics = crate::SemanticMeta::new(crate::semantic::SemanticType::NumericArray {
179            dtype: crate::semantic::NumericDType::I32,
180            length: Some(4),
181        });
182
183        let result = parser.parse_with_semantics(input, &semantics);
184        assert!(result.is_ok());
185    }
186
187    #[test]
188    fn test_custom_config() {
189        let config = ParseConfig {
190            detect_semantics: false,
191            max_size_mb: 50,
192            stream_large_arrays: false,
193            stream_threshold: 500,
194        };
195
196        let parser = Parser::with_config(config);
197        let input = br#"{"test": "data"}"#;
198        let result = parser.parse(input);
199        assert!(result.is_ok());
200    }
201}