cqlite_core/parser/
zero_copy_parser.rs1use nom::{bytes::complete::tag, character::complete::alphanumeric1, sequence::tuple, IResult};
7use std::borrow::Cow;
8use std::collections::HashMap;
9
10pub 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 pub fn new() -> Self {
24 Self {
25 cache: HashMap::new(),
26 }
27 }
28
29 pub fn intern(&mut self, s: &str) -> &str {
31 if let Some(&interned) = self.cache.get(s) {
32 interned
33 } else {
34 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
43pub struct ZeroCopyIdentifier<'a> {
45 name: Cow<'a, str>,
46 quoted: bool,
47}
48
49impl<'a> ZeroCopyIdentifier<'a> {
50 pub fn borrowed(name: &'a str) -> Self {
52 Self {
53 name: Cow::Borrowed(name),
54 quoted: false,
55 }
56 }
57
58 pub fn quoted_borrowed(name: &'a str) -> Self {
60 Self {
61 name: Cow::Borrowed(name),
62 quoted: true,
63 }
64 }
65
66 pub fn name(&self) -> &str {
68 &self.name
69 }
70
71 pub fn is_quoted(&self) -> bool {
73 self.quoted
74 }
75
76 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#[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 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 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#[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
133pub struct ZeroCopyBuffer<'a> {
135 data: &'a [u8],
136 position: usize,
137}
138
139impl<'a> ZeroCopyBuffer<'a> {
140 pub fn new(data: &'a [u8]) -> Self {
142 Self { data, position: 0 }
143 }
144
145 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 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 pub fn remaining(&self) -> &'a [u8] {
167 &self.data[self.position..]
168 }
169
170 pub fn is_empty(&self) -> bool {
172 self.position >= self.data.len()
173 }
174}
175
176pub struct ZeroCopyParser {
178 interner: StringInterner,
179}
180
181impl ZeroCopyParser {
182 pub fn new() -> Self {
184 Self {
185 interner: StringInterner::new(),
186 }
187 }
188
189 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 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 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#[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 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 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 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}