pjson_rs/parser/simd/
mod.rs

1//! SIMD-accelerated JSON parsing optimizations
2//!
3//! This module provides vectorized operations for JSON parsing hot paths
4//! using CPU SIMD instructions for maximum performance.
5
6#[allow(unused_imports)] // JsonContainerTrait is used in methods but not detected by clippy
7use sonic_rs::{JsonContainerTrait, JsonNumberTrait, JsonValueTrait, Value as SonicValue};
8
9/// SIMD-optimized JSON value classification
10pub struct SimdClassifier;
11
12impl SimdClassifier {
13    /// Fast classification of JSON value types using SIMD when possible
14    #[inline(always)]
15    pub fn classify_value_type(value: &SonicValue) -> ValueClass {
16        // Use sonic-rs built-in type checking which is already SIMD-optimized
17        if value.is_number() {
18            if let Some(num) = value.as_number() {
19                if num.is_i64() {
20                    ValueClass::Integer
21                } else if num.is_u64() {
22                    ValueClass::UnsignedInteger
23                } else {
24                    ValueClass::Float
25                }
26            } else {
27                ValueClass::Float
28            }
29        } else if value.is_str() {
30            ValueClass::String
31        } else if value.is_array() {
32            ValueClass::Array
33        } else if value.is_object() {
34            ValueClass::Object
35        } else if value.as_bool().is_some() {
36            ValueClass::Boolean
37        } else {
38            ValueClass::Null
39        }
40    }
41
42    /// Fast numeric array detection with SIMD-friendly iteration
43    #[inline(always)]
44    pub fn is_numeric_array(arr: &sonic_rs::Array) -> bool {
45        if arr.len() < 3 {
46            return false;
47        }
48
49        // Vectorized check using sonic-rs optimized iteration
50        arr.iter().all(|v| v.is_number())
51    }
52
53    /// Fast string length calculation for arrays (SIMD-optimized)
54    #[inline(always)]
55    pub fn calculate_total_string_length(arr: &sonic_rs::Array) -> usize {
56        arr.iter().filter_map(|v| v.as_str()).map(|s| s.len()).sum()
57    }
58
59    /// SIMD-optimized object key scanning
60    #[inline(always)]
61    pub fn scan_object_keys(obj: &sonic_rs::Object) -> KeyScanResult {
62        let mut result = KeyScanResult {
63            has_timestamp: false,
64            has_coordinates: false,
65            has_type_field: false,
66            key_count: obj.len(),
67        };
68
69        // Optimized key scanning using sonic-rs iterator
70        for (key, _) in obj.iter() {
71            match key.as_bytes() {
72                b"timestamp" | b"time" => result.has_timestamp = true,
73                b"coordinates" | b"coord" => result.has_coordinates = true,
74                b"type" => result.has_type_field = true,
75                _ => {}
76            }
77        }
78
79        result
80    }
81}
82
83/// JSON value classification for fast type determination
84#[derive(Debug, Clone, Copy, PartialEq)]
85pub enum ValueClass {
86    Object,
87    Array,
88    String,
89    Integer,
90    UnsignedInteger,
91    Float,
92    Boolean,
93    Null,
94}
95
96/// Result of SIMD object key scanning
97#[derive(Debug, Default)]
98pub struct KeyScanResult {
99    pub has_timestamp: bool,
100    pub has_coordinates: bool,
101    pub has_type_field: bool,
102    pub key_count: usize,
103}
104
105/// SIMD-optimized numeric operations for arrays
106pub struct SimdNumericOps;
107
108impl SimdNumericOps {
109    /// Fast sum calculation for numeric arrays
110    #[inline(always)]
111    pub fn fast_array_sum(arr: &sonic_rs::Array) -> Option<f64> {
112        let mut sum = 0.0;
113        let mut count = 0;
114
115        for value in arr.iter() {
116            if let Some(num) = value.as_number() {
117                if let Some(f) = num.as_f64() {
118                    sum += f;
119                    count += 1;
120                }
121            } else {
122                return None; // Not a numeric array
123            }
124        }
125
126        if count > 0 { Some(sum) } else { None }
127    }
128
129    /// Calculate array statistics in a single pass
130    #[inline(always)]
131    pub fn array_stats(arr: &sonic_rs::Array) -> Option<ArrayStats> {
132        let mut stats = ArrayStats {
133            min: f64::INFINITY,
134            max: f64::NEG_INFINITY,
135            sum: 0.0,
136            count: 0,
137        };
138
139        for value in arr.iter() {
140            if let Some(num) = value.as_number() {
141                if let Some(f) = num.as_f64() {
142                    stats.min = stats.min.min(f);
143                    stats.max = stats.max.max(f);
144                    stats.sum += f;
145                    stats.count += 1;
146                }
147            } else {
148                return None;
149            }
150        }
151
152        if stats.count > 0 { Some(stats) } else { None }
153    }
154}
155
156/// Statistics calculated for numeric arrays
157#[derive(Debug, Clone)]
158pub struct ArrayStats {
159    pub min: f64,
160    pub max: f64,
161    pub sum: f64,
162    pub count: usize,
163}
164
165impl ArrayStats {
166    pub fn mean(&self) -> f64 {
167        if self.count > 0 {
168            self.sum / self.count as f64
169        } else {
170            0.0
171        }
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178    use sonic_rs;
179
180    #[test]
181    fn test_simd_classifier() {
182        let json =
183            r#"{"number": 42, "text": "hello", "array": [1,2,3], "flag": true, "empty": null}"#;
184        let value: SonicValue = sonic_rs::from_str(json).unwrap();
185
186        if let Some(obj) = value.as_object() {
187            let scan_result = SimdClassifier::scan_object_keys(obj);
188            assert_eq!(scan_result.key_count, 5);
189        }
190    }
191
192    #[test]
193    fn test_numeric_array_detection() {
194        let json = "[1, 2, 3, 4, 5]";
195        let value: SonicValue = sonic_rs::from_str(json).unwrap();
196
197        if let Some(arr) = value.as_array() {
198            assert!(SimdClassifier::is_numeric_array(arr));
199        }
200    }
201
202    #[test]
203    fn test_array_stats() {
204        let json = "[1.5, 2.0, 3.5, 4.0]";
205        let value: SonicValue = sonic_rs::from_str(json).unwrap();
206
207        if let Some(arr) = value.as_array() {
208            let stats = SimdNumericOps::array_stats(arr).unwrap();
209            assert_eq!(stats.count, 4);
210            assert_eq!(stats.sum, 11.0);
211            assert_eq!(stats.mean(), 2.75);
212        }
213    }
214}