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