Skip to main content

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