Skip to main content

libsqlite3_sys/
native.rs

1//! Native value codecs and operations behind SQLite's common type dispatch.
2pub use crate::native_types::Kind;
3use crate::{
4    decimal::{Error, Result},
5    native_types::TYPES,
6};
7use chrono::Datelike;
8use std::ffi::c_char;
9use std::hash::{Hash, Hasher};
10
11pub fn encode(kind: Kind, text: &str) -> Result<Vec<u8>> {
12    let payload = match kind {
13        Kind::Uuid => uuid::Uuid::parse_str(text)
14            .map_err(|e| Error(e.to_string()))?
15            .as_bytes()
16            .to_vec(),
17        Kind::Decimal => return crate::decimal::encode(text),
18        Kind::Uint8 | Kind::Uint16 | Kind::Uint32 | Kind::Uint64 => {
19            return crate::unsigned::encode(kind, text);
20        }
21        _ => crate::temporal::encode(kind, text)?,
22    };
23    let descriptor = TYPES.iter().find(|d| d.kind == kind).unwrap();
24    let mut bytes = vec![descriptor.tag];
25    bytes.extend(payload);
26    Ok(bytes)
27}
28
29pub fn text(kind: Kind, bytes: &[u8]) -> Result<String> {
30    match kind {
31        Kind::Uuid => Ok(uuid::Uuid::from_bytes(
32            bytes
33                .try_into()
34                .map_err(|_| Error("invalid UUID storage".into()))?,
35        )
36        .to_string()),
37        Kind::Uint8 | Kind::Uint16 | Kind::Uint32 | Kind::Uint64 => {
38            Ok(crate::unsigned::read(bytes).to_string())
39        }
40        Kind::Decimal => std::str::from_utf8(bytes)
41            .map(str::to_owned)
42            .map_err(|e| Error(e.to_string())),
43        _ => crate::temporal::text(kind, bytes),
44    }
45}
46
47pub fn decode(bytes: &[u8]) -> Option<(Kind, String)> {
48    if bytes.is_empty() {
49        return None;
50    }
51    let descriptor = TYPES
52        .iter()
53        .find(|d| d.tag == bytes[0] && d.kind != Kind::Decimal)?;
54    if bytes.len() != 1 + descriptor.payload_size {
55        return None;
56    }
57    Some((descriptor.kind, text(descriptor.kind, &bytes[1..]).ok()?))
58}
59
60pub fn canonical(kind: Kind, value: &str) -> Result<String> {
61    let bytes = encode(kind, value)?;
62    text(kind, &bytes[1..])
63}
64
65pub fn cast(target: Kind, source: Kind, value: &str) -> Result<String> {
66    let bytes = encode(source, value)?;
67    if crate::unsigned::is_unsigned(target as i32) {
68        let converted =
69            crate::unsigned::evaluate(0, target as i32, source as i32, &bytes[1..], -3, &[])?;
70        return text(target, &converted[1..]);
71    }
72    if target == Kind::Decimal && crate::unsigned::is_unsigned(source as i32) {
73        return canonical(target, &text(source, &bytes[1..])?);
74    }
75    let payload = if target == source {
76        bytes[1..].to_vec()
77    } else {
78        crate::temporal::cast(target, source, &bytes[1..], false)?
79    };
80    text(target, &payload)
81}
82
83#[repr(C)]
84struct FunctionArgument {
85    kind: i32,
86    data: *const c_char,
87    length: i32,
88}
89
90#[unsafe(no_mangle)]
91unsafe extern "C" fn boxdb_native_function(
92    function: i32,
93    argc: i32,
94    args: *const FunctionArgument,
95    out: *mut *mut c_char,
96    out_len: *mut i32,
97) -> i32 {
98    let result = (|| {
99        if !(0..=7).contains(&argc) {
100            return Err(Error("invalid temporal function argument count".into()));
101        }
102        let args = unsafe { std::slice::from_raw_parts(args, argc as usize) }
103            .iter()
104            .map(|arg| crate::temporal_functions::Argument {
105                kind: arg.kind,
106                bytes: unsafe { input(arg.data, arg.length) },
107            })
108            .collect::<Vec<_>>();
109        let (kind, payload) = crate::temporal_functions::evaluate(function, &args)?;
110        let descriptor = TYPES.iter().find(|d| d.kind == kind).unwrap();
111        let mut bytes = vec![descriptor.tag];
112        bytes.extend(payload);
113        Ok(bytes)
114    })();
115    match result {
116        Ok(bytes) => unsafe { crate::decimal::output(&bytes, out, out_len) },
117        Err(error) => {
118            let rc = unsafe { crate::decimal::output(error.0.as_bytes(), out, out_len) };
119            if rc == crate::SQLITE_OK {
120                crate::SQLITE_ERROR
121            } else {
122                rc
123            }
124        }
125    }
126}
127
128fn ordering_key(kind: i32, bytes: &[u8]) -> i128 {
129    match kind {
130        3 | 5 => {
131            i128::from(i64::from_be_bytes(bytes[..8].try_into().unwrap())) * crate::temporal::SECOND
132                + i128::from(u32::from_be_bytes(bytes[8..12].try_into().unwrap()))
133        }
134        4 => {
135            (i128::from(i32::from_be_bytes(bytes[..4].try_into().unwrap()))
136                - i128::from(
137                    chrono::NaiveDate::from_ymd_opt(1970, 1, 1)
138                        .unwrap()
139                        .num_days_from_ce(),
140                ))
141                * crate::temporal::DAY
142        }
143        11 => i128::from(u64::from_be_bytes(bytes.try_into().unwrap())),
144        6 => {
145            let value = crate::temporal::read_interval(bytes).unwrap();
146            (i128::from(value.months) * 30 + i128::from(value.days)) * crate::temporal::DAY
147                + i128::from(value.micros)
148        }
149        _ => 0,
150    }
151}
152
153#[unsafe(no_mangle)]
154unsafe extern "C" fn boxdb_native_compare(
155    kind: i32,
156    a: *const c_char,
157    na: i32,
158    kind_b: i32,
159    b: *const c_char,
160    nb: i32,
161) -> i32 {
162    let a = unsafe { input(a, na) };
163    let b = unsafe { input(b, nb) };
164    if kind == 2 {
165        a.cmp(b) as i32
166    } else {
167        ordering_key(kind, a).cmp(&ordering_key(kind_b, b)) as i32
168    }
169}
170
171#[unsafe(no_mangle)]
172unsafe extern "C" fn boxdb_native_hash(kind: i32, data: *const c_char, length: i32) -> u64 {
173    let bytes = unsafe { input(data, length) };
174    if crate::unsigned::is_unsigned(kind) {
175        // SQLite's numeric Bloom hash saturates REAL/DECIMAL at INT64_MAX.
176        // Equal cross-type numbers must land in the same bucket.
177        return crate::unsigned::read(bytes).min(i64::MAX as u64);
178    }
179    let mut hash = std::collections::hash_map::DefaultHasher::new();
180    (if matches!(kind, 4 | 5) { 4 } else { kind }).hash(&mut hash);
181    if matches!(kind, 4 | 5 | 6) {
182        ordering_key(kind, bytes).hash(&mut hash);
183    } else {
184        bytes.hash(&mut hash);
185    }
186    hash.finish()
187}
188
189unsafe fn input<'a>(data: *const c_char, length: i32) -> &'a [u8] {
190    unsafe { std::slice::from_raw_parts(data.cast(), length as usize) }
191}
192
193#[unsafe(no_mangle)]
194unsafe extern "C" fn boxdb_native_text(
195    kind: i32,
196    data: *const c_char,
197    length: i32,
198    out: *mut *mut c_char,
199    out_len: *mut i32,
200) -> i32 {
201    let result = (|| {
202        let kind = TYPES
203            .iter()
204            .find(|d| d.kind as i32 == kind)
205            .ok_or_else(|| Error("unknown native type".into()))?
206            .kind;
207        text(kind, unsafe { input(data, length) })
208    })();
209    match result {
210        Ok(value) => unsafe { crate::decimal::output(value.as_bytes(), out, out_len) },
211        Err(error) => {
212            let rc = unsafe { crate::decimal::output(error.0.as_bytes(), out, out_len) };
213            if rc == crate::SQLITE_OK {
214                crate::SQLITE_ERROR
215            } else {
216                rc
217            }
218        }
219    }
220}
221
222#[unsafe(no_mangle)]
223unsafe extern "C" fn boxdb_native_eval(
224    operation: i32,
225    target: i32,
226    kind_a: i32,
227    data_a: *const c_char,
228    len_a: i32,
229    kind_b: i32,
230    data_b: *const c_char,
231    len_b: i32,
232    out: *mut *mut c_char,
233    out_len: *mut i32,
234    out_kind: *mut i32,
235) -> i32 {
236    let result = (|| {
237        if crate::unsigned::is_unsigned(target)
238            || crate::unsigned::is_unsigned(kind_a)
239            || crate::unsigned::is_unsigned(kind_b)
240        {
241            if target == 0 || crate::unsigned::is_unsigned(target) {
242                unsafe {
243                    *out_kind = if target == 0 {
244                        kind_a.max(kind_b)
245                    } else {
246                        target
247                    };
248                }
249                return crate::unsigned::evaluate(
250                    operation,
251                    target,
252                    kind_a,
253                    unsafe { input(data_a, len_a) },
254                    kind_b,
255                    unsafe { input(data_b, len_b) },
256                );
257            }
258        }
259        if operation != 0 && operation != 8 {
260            let (kind, payload) = crate::temporal::operation(
261                operation,
262                kind_a,
263                unsafe { input(data_a, len_a) },
264                kind_b,
265                unsafe { input(data_b, len_b) },
266            )?;
267            unsafe {
268                *out_kind = kind;
269            }
270            if kind < 0 {
271                return Ok(payload);
272            }
273            let descriptor = TYPES.iter().find(|d| d.kind as i32 == kind).unwrap();
274            let mut bytes = vec![descriptor.tag];
275            bytes.extend(payload);
276            return Ok(bytes);
277        }
278        let kind = TYPES
279            .iter()
280            .find(|d| d.kind as i32 == target)
281            .ok_or_else(|| Error("unknown native type".into()))?
282            .kind;
283        unsafe {
284            *out_kind = kind as i32;
285        }
286        if kind_a > 0 {
287            let source = TYPES
288                .iter()
289                .find(|d| d.kind as i32 == kind_a)
290                .ok_or_else(|| Error("unknown native type".into()))?
291                .kind;
292            let input = unsafe { input(data_a, len_a) };
293            let payload = if kind == source {
294                text(kind, input)?;
295                input.to_vec()
296            } else {
297                crate::temporal::cast(kind, source, input, operation == 8)?
298            };
299            let descriptor = TYPES.iter().find(|d| d.kind == kind).unwrap();
300            let mut bytes = vec![descriptor.tag];
301            bytes.extend(payload);
302            return Ok(bytes);
303        }
304        if kind_a != 0 {
305            return Err(Error("unsupported native conversion".into()));
306        }
307        let text = std::str::from_utf8(unsafe { input(data_a, len_a) })
308            .map_err(|e| Error(e.to_string()))?;
309        encode(kind, text)
310    })();
311    match result {
312        Ok(bytes) => unsafe { crate::decimal::output(&bytes, out, out_len) },
313        Err(error) => {
314            let rc = unsafe { crate::decimal::output(error.0.as_bytes(), out, out_len) };
315            if rc == crate::SQLITE_OK {
316                crate::SQLITE_ERROR
317            } else {
318                rc
319            }
320        }
321    }
322}