1pub 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 6 => {
144 let value = crate::temporal::read_interval(bytes).unwrap();
145 (i128::from(value.months) * 30 + i128::from(value.days)) * crate::temporal::DAY
146 + i128::from(value.micros)
147 }
148 _ => 0,
149 }
150}
151
152#[unsafe(no_mangle)]
153unsafe extern "C" fn boxdb_native_compare(
154 kind: i32,
155 a: *const c_char,
156 na: i32,
157 kind_b: i32,
158 b: *const c_char,
159 nb: i32,
160) -> i32 {
161 let a = unsafe { input(a, na) };
162 let b = unsafe { input(b, nb) };
163 if kind == 2 {
164 a.cmp(b) as i32
165 } else {
166 ordering_key(kind, a).cmp(&ordering_key(kind_b, b)) as i32
167 }
168}
169
170#[unsafe(no_mangle)]
171unsafe extern "C" fn boxdb_native_hash(kind: i32, data: *const c_char, length: i32) -> u64 {
172 let bytes = unsafe { input(data, length) };
173 if crate::unsigned::is_unsigned(kind) {
174 return crate::unsigned::read(bytes).min(i64::MAX as u64);
177 }
178 let mut hash = std::collections::hash_map::DefaultHasher::new();
179 (if matches!(kind, 4 | 5) { 4 } else { kind }).hash(&mut hash);
180 if matches!(kind, 4 | 5 | 6) {
181 ordering_key(kind, bytes).hash(&mut hash);
182 } else {
183 bytes.hash(&mut hash);
184 }
185 hash.finish()
186}
187
188unsafe fn input<'a>(data: *const c_char, length: i32) -> &'a [u8] {
189 unsafe { std::slice::from_raw_parts(data.cast(), length as usize) }
190}
191
192#[unsafe(no_mangle)]
193unsafe extern "C" fn boxdb_native_text(
194 kind: i32,
195 data: *const c_char,
196 length: i32,
197 out: *mut *mut c_char,
198 out_len: *mut i32,
199) -> i32 {
200 let result = (|| {
201 let kind = TYPES
202 .iter()
203 .find(|d| d.kind as i32 == kind)
204 .ok_or_else(|| Error("unknown native type".into()))?
205 .kind;
206 text(kind, unsafe { input(data, length) })
207 })();
208 match result {
209 Ok(value) => unsafe { crate::decimal::output(value.as_bytes(), out, out_len) },
210 Err(error) => {
211 let rc = unsafe { crate::decimal::output(error.0.as_bytes(), out, out_len) };
212 if rc == crate::SQLITE_OK {
213 crate::SQLITE_ERROR
214 } else {
215 rc
216 }
217 }
218 }
219}
220
221#[unsafe(no_mangle)]
222unsafe extern "C" fn boxdb_native_eval(
223 operation: i32,
224 target: i32,
225 kind_a: i32,
226 data_a: *const c_char,
227 len_a: i32,
228 kind_b: i32,
229 data_b: *const c_char,
230 len_b: i32,
231 out: *mut *mut c_char,
232 out_len: *mut i32,
233 out_kind: *mut i32,
234) -> i32 {
235 let result = (|| {
236 if crate::unsigned::is_unsigned(target)
237 || crate::unsigned::is_unsigned(kind_a)
238 || crate::unsigned::is_unsigned(kind_b)
239 {
240 if target == 0 || crate::unsigned::is_unsigned(target) {
241 unsafe {
242 *out_kind = if target == 0 {
243 kind_a.max(kind_b)
244 } else {
245 target
246 };
247 }
248 return crate::unsigned::evaluate(
249 operation,
250 target,
251 kind_a,
252 unsafe { input(data_a, len_a) },
253 kind_b,
254 unsafe { input(data_b, len_b) },
255 );
256 }
257 }
258 if operation != 0 && operation != 8 {
259 let (kind, payload) = crate::temporal::operation(
260 operation,
261 kind_a,
262 unsafe { input(data_a, len_a) },
263 kind_b,
264 unsafe { input(data_b, len_b) },
265 )?;
266 unsafe {
267 *out_kind = kind;
268 }
269 if kind < 0 {
270 return Ok(payload);
271 }
272 let descriptor = TYPES.iter().find(|d| d.kind as i32 == kind).unwrap();
273 let mut bytes = vec![descriptor.tag];
274 bytes.extend(payload);
275 return Ok(bytes);
276 }
277 let kind = TYPES
278 .iter()
279 .find(|d| d.kind as i32 == target)
280 .ok_or_else(|| Error("unknown native type".into()))?
281 .kind;
282 unsafe {
283 *out_kind = kind as i32;
284 }
285 if kind_a > 0 {
286 let source = TYPES
287 .iter()
288 .find(|d| d.kind as i32 == kind_a)
289 .ok_or_else(|| Error("unknown native type".into()))?
290 .kind;
291 let input = unsafe { input(data_a, len_a) };
292 let payload = if kind == source {
293 text(kind, input)?;
294 input.to_vec()
295 } else {
296 crate::temporal::cast(kind, source, input, operation == 8)?
297 };
298 let descriptor = TYPES.iter().find(|d| d.kind == kind).unwrap();
299 let mut bytes = vec![descriptor.tag];
300 bytes.extend(payload);
301 return Ok(bytes);
302 }
303 if kind_a != 0 {
304 return Err(Error("unsupported native conversion".into()));
305 }
306 let text = std::str::from_utf8(unsafe { input(data_a, len_a) })
307 .map_err(|e| Error(e.to_string()))?;
308 encode(kind, text)
309 })();
310 match result {
311 Ok(bytes) => unsafe { crate::decimal::output(&bytes, out, out_len) },
312 Err(error) => {
313 let rc = unsafe { crate::decimal::output(error.0.as_bytes(), out, out_len) };
314 if rc == crate::SQLITE_OK {
315 crate::SQLITE_ERROR
316 } else {
317 rc
318 }
319 }
320 }
321}