Skip to main content

cqlite_core/parser/
zero_copy_parser.rs

1//! Zero-copy parsing optimizations
2//!
3//! This module provides zero-copy parsing patterns to reduce string allocations
4//! and improve performance for high-throughput scenarios.
5
6use nom::{bytes::complete::tag, character::complete::alphanumeric1, sequence::tuple, IResult};
7use std::borrow::Cow;
8use std::collections::HashMap;
9
10/// String interning cache for repeated values
11pub struct StringInterner {
12    cache: HashMap<String, &'static str>,
13}
14
15impl Default for StringInterner {
16    fn default() -> Self {
17        Self::new()
18    }
19}
20
21impl StringInterner {
22    /// Create new string interner
23    pub fn new() -> Self {
24        Self {
25            cache: HashMap::new(),
26        }
27    }
28
29    /// Intern a string to reduce allocations for repeated values
30    pub fn intern(&mut self, s: &str) -> &str {
31        if let Some(&interned) = self.cache.get(s) {
32            interned
33        } else {
34            // SAFETY: This leaks memory intentionally for interning
35            // In a production system, this should use a proper interning strategy
36            let leaked: &'static str = Box::leak(s.to_string().into_boxed_str());
37            self.cache.insert(s.to_string(), leaked);
38            leaked
39        }
40    }
41}
42
43/// Zero-copy identifier parser
44pub struct ZeroCopyIdentifier<'a> {
45    name: Cow<'a, str>,
46    quoted: bool,
47}
48
49impl<'a> ZeroCopyIdentifier<'a> {
50    /// Create new identifier from borrowed string
51    pub fn borrowed(name: &'a str) -> Self {
52        Self {
53            name: Cow::Borrowed(name),
54            quoted: false,
55        }
56    }
57
58    /// Create quoted identifier from borrowed string  
59    pub fn quoted_borrowed(name: &'a str) -> Self {
60        Self {
61            name: Cow::Borrowed(name),
62            quoted: true,
63        }
64    }
65
66    /// Get the name as a string reference
67    pub fn name(&self) -> &str {
68        &self.name
69    }
70
71    /// Check if identifier is quoted
72    pub fn is_quoted(&self) -> bool {
73        self.quoted
74    }
75
76    /// Convert to owned version when necessary
77    pub fn into_owned(self) -> ZeroCopyIdentifier<'static> {
78        ZeroCopyIdentifier {
79            name: Cow::Owned(self.name.into_owned()),
80            quoted: self.quoted,
81        }
82    }
83}
84
85/// Zero-copy CQL value parser
86#[derive(Debug)]
87pub enum ZeroCopyValue<'a> {
88    Text(&'a str),
89    Integer(i32),
90    BigInt(i64),
91    Boolean(bool),
92    Blob(&'a [u8]),
93    Null,
94}
95
96impl<'a> ZeroCopyValue<'a> {
97    /// Parse text value without allocation
98    pub fn parse_text(input: &'a str) -> IResult<&'a str, Self> {
99        use nom::{
100            bytes::complete::{tag, take_while},
101            sequence::delimited,
102        };
103
104        let (remaining, text) =
105            delimited(tag("'"), take_while(|c: char| c != '\''), tag("'"))(input)?;
106        Ok((remaining, ZeroCopyValue::Text(text)))
107    }
108
109    /// Convert to owned value when crossing async boundaries
110    pub fn into_owned(self) -> OwnedValue {
111        match self {
112            ZeroCopyValue::Text(s) => OwnedValue::Text(s.to_string()),
113            ZeroCopyValue::Integer(i) => OwnedValue::Integer(i),
114            ZeroCopyValue::BigInt(i) => OwnedValue::BigInt(i),
115            ZeroCopyValue::Boolean(b) => OwnedValue::Boolean(b),
116            ZeroCopyValue::Blob(b) => OwnedValue::Blob(b.to_vec()),
117            ZeroCopyValue::Null => OwnedValue::Null,
118        }
119    }
120}
121
122/// Owned value for when zero-copy is not possible
123#[derive(Debug, Clone)]
124pub enum OwnedValue {
125    Text(String),
126    Integer(i32),
127    BigInt(i64),
128    Boolean(bool),
129    Blob(Vec<u8>),
130    Null,
131}
132
133/// Zero-copy memory buffer for parsing
134pub struct ZeroCopyBuffer<'a> {
135    data: &'a [u8],
136    position: usize,
137}
138
139impl<'a> ZeroCopyBuffer<'a> {
140    /// Create new zero-copy buffer
141    pub fn new(data: &'a [u8]) -> Self {
142        Self { data, position: 0 }
143    }
144
145    /// Read slice without copying
146    pub fn read_slice(&mut self, len: usize) -> Option<&'a [u8]> {
147        if self.position + len <= self.data.len() {
148            let slice = &self.data[self.position..self.position + len];
149            self.position += len;
150            Some(slice)
151        } else {
152            None
153        }
154    }
155
156    /// Peek at next bytes without advancing
157    pub fn peek(&self, len: usize) -> Option<&'a [u8]> {
158        if self.position + len <= self.data.len() {
159            Some(&self.data[self.position..self.position + len])
160        } else {
161            None
162        }
163    }
164
165    /// Get remaining bytes
166    pub fn remaining(&self) -> &'a [u8] {
167        &self.data[self.position..]
168    }
169
170    /// Check if we're at end
171    pub fn is_empty(&self) -> bool {
172        self.position >= self.data.len()
173    }
174}
175
176/// Performance-optimized parser with zero-copy patterns
177pub struct ZeroCopyParser {
178    interner: StringInterner,
179}
180
181impl ZeroCopyParser {
182    /// Create new zero-copy parser
183    pub fn new() -> Self {
184        Self {
185            interner: StringInterner::new(),
186        }
187    }
188
189    /// Parse identifier with zero-copy when possible
190    pub fn parse_identifier<'a>(
191        &mut self,
192        input: &'a str,
193    ) -> IResult<&'a str, ZeroCopyIdentifier<'a>> {
194        let (remaining, name) = alphanumeric1(input)?;
195        Ok((remaining, ZeroCopyIdentifier::borrowed(name)))
196    }
197
198    /// Parse string with interning for repeated values
199    pub fn parse_interned_string<'a>(&mut self, input: &'a str) -> IResult<&'a str, &str> {
200        let (remaining, (_, content, _)) = tuple((tag("'"), alphanumeric1, tag("'")))(input)?;
201        let interned = self.interner.intern(content);
202        Ok((remaining, interned))
203    }
204
205    /// Parse binary data with zero-copy
206    pub fn parse_blob<'a>(&self, buffer: &mut ZeroCopyBuffer<'a>, len: usize) -> Option<&'a [u8]> {
207        buffer.read_slice(len)
208    }
209}
210
211impl Default for ZeroCopyParser {
212    fn default() -> Self {
213        Self::new()
214    }
215}
216
217/// Memory-efficient parsing statistics
218#[derive(Debug, Default)]
219pub struct ZeroCopyStats {
220    pub bytes_parsed_zerocopy: usize,
221    pub bytes_parsed_allocated: usize,
222    pub allocations_avoided: usize,
223    pub strings_interned: usize,
224}
225
226impl ZeroCopyStats {
227    /// Calculate memory efficiency ratio
228    pub fn efficiency_ratio(&self) -> f64 {
229        if self.bytes_parsed_allocated == 0 {
230            return 1.0;
231        }
232        self.bytes_parsed_zerocopy as f64
233            / (self.bytes_parsed_zerocopy + self.bytes_parsed_allocated) as f64
234    }
235
236    /// Calculate allocation avoidance ratio
237    pub fn allocation_avoidance_ratio(&self) -> f64 {
238        let total_potential = self.allocations_avoided + self.bytes_parsed_allocated;
239        if total_potential == 0 {
240            return 1.0;
241        }
242        self.allocations_avoided as f64 / total_potential as f64
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249
250    #[test]
251    fn test_string_interner() {
252        let mut interner = StringInterner::new();
253
254        let s1 = interner.intern("test");
255        let s1_ptr = s1.as_ptr();
256        let s2 = interner.intern("test");
257        let s2_ptr = s2.as_ptr();
258
259        // Should return same reference for same string
260        assert_eq!(s1_ptr, s2_ptr);
261    }
262
263    #[test]
264    fn test_zero_copy_identifier() {
265        let name = "test_table";
266        let id = ZeroCopyIdentifier::borrowed(name);
267
268        assert_eq!(id.name(), name);
269        assert!(!id.is_quoted());
270    }
271
272    #[test]
273    fn test_zero_copy_buffer() {
274        let data = b"hello world";
275        let mut buffer = ZeroCopyBuffer::new(data);
276
277        let slice1 = buffer.read_slice(5).unwrap();
278        assert_eq!(slice1, b"hello");
279
280        let slice2 = buffer.read_slice(6).unwrap();
281        assert_eq!(slice2, b" world");
282
283        assert!(buffer.is_empty());
284    }
285
286    #[test]
287    fn test_zero_copy_value_parsing() {
288        let input = "'test_value'";
289        if let Ok((_, value)) = ZeroCopyValue::parse_text(input) {
290            match value {
291                ZeroCopyValue::Text(s) => assert_eq!(s, "test_value"),
292                _ => panic!("Expected text value"),
293            }
294        } else {
295            panic!("Failed to parse text value");
296        }
297    }
298
299    #[test]
300    fn test_zero_copy_stats() {
301        let mut stats = ZeroCopyStats::default();
302        stats.bytes_parsed_zerocopy = 800;
303        stats.bytes_parsed_allocated = 200;
304        stats.allocations_avoided = 10;
305
306        assert_eq!(stats.efficiency_ratio(), 0.8);
307        assert!(stats.allocation_avoidance_ratio() > 0.0);
308    }
309}