1use std::{
5 alloc::{Layout, alloc, dealloc, realloc as system_realloc},
6 slice::from_raw_parts,
7};
8
9use reifydb_type::util::cowvec::CowVec;
10
11#[unsafe(no_mangle)]
13extern "C" fn test_alloc(size: usize) -> *mut u8 {
14 if size == 0 {
15 return ptr::null_mut();
16 }
17
18 let layout = match Layout::from_size_align(size, 8) {
19 Ok(layout) => layout,
20 Err(_) => return ptr::null_mut(),
21 };
22
23 unsafe { alloc(layout) }
24}
25
26#[unsafe(no_mangle)]
28unsafe extern "C" fn test_free(ptr: *mut u8, size: usize) {
29 if ptr.is_null() || size == 0 {
30 return;
31 }
32
33 let layout = match Layout::from_size_align(size, 8) {
34 Ok(layout) => layout,
35 Err(_) => return,
36 };
37
38 unsafe { dealloc(ptr, layout) }
39}
40
41#[unsafe(no_mangle)]
43unsafe extern "C" fn test_realloc(ptr: *mut u8, old_size: usize, new_size: usize) -> *mut u8 {
44 if ptr.is_null() {
45 return test_alloc(new_size);
46 }
47
48 if new_size == 0 {
49 unsafe { test_free(ptr, old_size) };
50 return ptr::null_mut();
51 }
52
53 let old_layout = match Layout::from_size_align(old_size, 8) {
54 Ok(layout) => layout,
55 Err(_) => return ptr::null_mut(),
56 };
57
58 let new_layout = match Layout::from_size_align(new_size, 8) {
59 Ok(layout) => layout,
60 Err(_) => return ptr::null_mut(),
61 };
62
63 unsafe { system_realloc(ptr, old_layout, new_layout.size()) }
64}
65
66unsafe fn get_test_context(ctx: *mut ContextFFI) -> &'static TestContext {
68 unsafe {
69 let txn_ptr = (*ctx).txn_ptr;
70 &*(txn_ptr as *const TestContext)
71 }
72}
73
74#[unsafe(no_mangle)]
76extern "C" fn test_state_get(
77 _operator_id: u64,
78 ctx: *mut ContextFFI,
79 key_ptr: *const u8,
80 key_len: usize,
81 output: *mut BufferFFI,
82) -> i32 {
83 if ctx.is_null() || key_ptr.is_null() || output.is_null() {
84 return FFI_ERROR_NULL_PTR;
85 }
86
87 unsafe {
88 let test_ctx = get_test_context(ctx);
89
90 let key_bytes = from_raw_parts(key_ptr, key_len);
92 let key = EncodedKey(CowVec::new(key_bytes.to_vec()));
93
94 match test_ctx.get_state(&key) {
96 Some(value_bytes) => {
97 let value_ptr = test_alloc(value_bytes.len());
99 if value_ptr.is_null() {
100 return -2; }
102
103 ptr::copy_nonoverlapping(value_bytes.as_ptr(), value_ptr, value_bytes.len());
104
105 (*output).ptr = value_ptr;
106 (*output).len = value_bytes.len();
107 (*output).cap = value_bytes.len();
108
109 FFI_OK
110 }
111 None => FFI_NOT_FOUND,
112 }
113 }
114}
115
116#[unsafe(no_mangle)]
118extern "C" fn test_state_set(
119 _operator_id: u64,
120 ctx: *mut ContextFFI,
121 key_ptr: *const u8,
122 key_len: usize,
123 value_ptr: *const u8,
124 value_len: usize,
125) -> i32 {
126 if ctx.is_null() || key_ptr.is_null() || value_ptr.is_null() {
127 return FFI_ERROR_NULL_PTR;
128 }
129
130 unsafe {
131 let test_ctx = get_test_context(ctx);
132
133 let key_bytes = from_raw_parts(key_ptr, key_len);
135 let key = EncodedKey(CowVec::new(key_bytes.to_vec()));
136
137 let value_bytes = from_raw_parts(value_ptr, value_len);
139
140 test_ctx.set_state(key, value_bytes.to_vec());
142
143 FFI_OK
144 }
145}
146
147#[unsafe(no_mangle)]
149extern "C" fn test_state_remove(_operator_id: u64, ctx: *mut ContextFFI, key_ptr: *const u8, key_len: usize) -> i32 {
150 if ctx.is_null() || key_ptr.is_null() {
151 return FFI_ERROR_NULL_PTR;
152 }
153
154 unsafe {
155 let test_ctx = get_test_context(ctx);
156
157 let key_bytes = from_raw_parts(key_ptr, key_len);
159 let key = EncodedKey(CowVec::new(key_bytes.to_vec()));
160
161 test_ctx.remove_state(&key);
163
164 FFI_OK
165 }
166}
167
168#[unsafe(no_mangle)]
170extern "C" fn test_state_clear(_operator_id: u64, ctx: *mut ContextFFI) -> i32 {
171 if ctx.is_null() {
172 return FFI_ERROR_NULL_PTR;
173 }
174
175 unsafe {
176 let test_ctx = get_test_context(ctx);
177 test_ctx.clear_state();
178 FFI_OK
179 }
180}
181
182#[repr(C)]
184struct TestStateIterator {
185 items: Vec<(Vec<u8>, Vec<u8>)>,
187 position: usize,
189}
190
191#[unsafe(no_mangle)]
193extern "C" fn test_state_prefix(
194 _operator_id: u64,
195 ctx: *mut ContextFFI,
196 prefix_ptr: *const u8,
197 prefix_len: usize,
198 iterator_out: *mut *mut StateIteratorFFI,
199) -> i32 {
200 if ctx.is_null() || iterator_out.is_null() {
201 return FFI_ERROR_NULL_PTR;
202 }
203
204 unsafe {
205 let test_ctx = get_test_context(ctx);
206
207 let prefix_bytes = if prefix_ptr.is_null() || prefix_len == 0 {
209 vec![]
210 } else {
211 from_raw_parts(prefix_ptr, prefix_len).to_vec()
212 };
213
214 let state_store = test_ctx.state_store();
216 let state = state_store.lock().unwrap();
217
218 let mut items: Vec<(Vec<u8>, Vec<u8>)> = state
219 .iter()
220 .filter(|(key, _)| {
221 if prefix_bytes.is_empty() {
222 true } else {
224 key.0.starts_with(&prefix_bytes) }
226 })
227 .map(|(key, value)| (key.0.to_vec(), value.0.to_vec()))
228 .collect();
229
230 items.sort_by(|a, b| a.0.cmp(&b.0));
232
233 let iter = Box::new(TestStateIterator {
235 items,
236 position: 0,
237 });
238
239 *iterator_out = Box::into_raw(iter) as *mut StateIteratorFFI;
241
242 FFI_OK
243 }
244}
245
246#[unsafe(no_mangle)]
248extern "C" fn test_state_iterator_next(
249 iterator: *mut StateIteratorFFI,
250 key_out: *mut BufferFFI,
251 value_out: *mut BufferFFI,
252) -> i32 {
253 if iterator.is_null() || key_out.is_null() || value_out.is_null() {
254 return FFI_ERROR_NULL_PTR;
255 }
256
257 unsafe {
258 let iter = &mut *(iterator as *mut TestStateIterator);
260
261 if iter.position >= iter.items.len() {
263 return FFI_END_OF_ITERATION;
264 }
265
266 let (key, value) = &iter.items[iter.position];
267 iter.position += 1;
268
269 let key_ptr = test_alloc(key.len());
271 if key_ptr.is_null() {
272 return -2; }
274 ptr::copy_nonoverlapping(key.as_ptr(), key_ptr, key.len());
275 (*key_out).ptr = key_ptr;
276 (*key_out).len = key.len();
277 (*key_out).cap = key.len();
278
279 let value_ptr = test_alloc(value.len());
281 if value_ptr.is_null() {
282 test_free(key_ptr, key.len());
284 return -2; }
286 ptr::copy_nonoverlapping(value.as_ptr(), value_ptr, value.len());
287 (*value_out).ptr = value_ptr;
288 (*value_out).len = value.len();
289 (*value_out).cap = value.len();
290
291 FFI_OK
292 }
293}
294
295#[unsafe(no_mangle)]
297extern "C" fn test_state_iterator_free(iterator: *mut StateIteratorFFI) {
298 if iterator.is_null() {
299 return;
300 }
301
302 unsafe {
303 let _ = Box::from_raw(iterator as *mut TestStateIterator);
305 }
306}
307
308const BOUND_UNBOUNDED: u8 = 0;
310const BOUND_INCLUDED: u8 = 1;
311const BOUND_EXCLUDED: u8 = 2;
312
313#[unsafe(no_mangle)]
315extern "C" fn test_state_range(
316 _operator_id: u64,
317 ctx: *mut ContextFFI,
318 start_ptr: *const u8,
319 start_len: usize,
320 start_bound_type: u8,
321 end_ptr: *const u8,
322 end_len: usize,
323 end_bound_type: u8,
324 iterator_out: *mut *mut StateIteratorFFI,
325) -> i32 {
326 if ctx.is_null() || iterator_out.is_null() {
327 return FFI_ERROR_NULL_PTR;
328 }
329
330 unsafe {
331 let test_ctx = get_test_context(ctx);
332
333 let start_key = if start_bound_type == BOUND_UNBOUNDED || start_ptr.is_null() {
335 None
336 } else {
337 Some(from_raw_parts(start_ptr, start_len).to_vec())
338 };
339
340 let end_key = if end_bound_type == BOUND_UNBOUNDED || end_ptr.is_null() {
342 None
343 } else {
344 Some(from_raw_parts(end_ptr, end_len).to_vec())
345 };
346
347 let state_store = test_ctx.state_store();
349 let state = state_store.lock().unwrap();
350
351 let mut items: Vec<(Vec<u8>, Vec<u8>)> = state
352 .iter()
353 .filter(|(key, _)| {
354 let key_bytes = key.0.as_slice();
355
356 let start_ok = match (&start_key, start_bound_type) {
358 (None, _) => true,
359 (Some(start), BOUND_INCLUDED) => key_bytes >= start.as_slice(),
360 (Some(start), BOUND_EXCLUDED) => key_bytes > start.as_slice(),
361 _ => true,
362 };
363
364 let end_ok = match (&end_key, end_bound_type) {
366 (None, _) => true,
367 (Some(end), BOUND_INCLUDED) => key_bytes <= end.as_slice(),
368 (Some(end), BOUND_EXCLUDED) => key_bytes < end.as_slice(),
369 _ => true,
370 };
371
372 start_ok && end_ok
373 })
374 .map(|(key, value)| (key.0.to_vec(), value.0.to_vec()))
375 .collect();
376
377 items.sort_by(|a, b| a.0.cmp(&b.0));
379
380 let iter = Box::new(TestStateIterator {
382 items,
383 position: 0,
384 });
385
386 *iterator_out = Box::into_raw(iter) as *mut StateIteratorFFI;
388
389 FFI_OK
390 }
391}
392
393#[unsafe(no_mangle)]
395unsafe extern "C" fn test_log_message(_operator_id: u64, _level: u32, _message: *const u8, _message_len: usize) {
396 unimplemented!()
397}
398
399extern "C" fn test_store_get(_ctx: *mut ContextFFI, _key: *const u8, _key_len: usize, _output: *mut BufferFFI) -> i32 {
401 unimplemented!()
402}
403
404extern "C" fn test_store_contains_key(
406 _ctx: *mut ContextFFI,
407 _key: *const u8,
408 _key_len: usize,
409 _result: *mut u8,
410) -> i32 {
411 unimplemented!()
412}
413
414extern "C" fn test_store_prefix(
416 _ctx: *mut ContextFFI,
417 _prefix: *const u8,
418 _prefix_len: usize,
419 _iterator_out: *mut *mut StoreIteratorFFI,
420) -> i32 {
421 unimplemented!()
422}
423
424extern "C" fn test_store_range(
426 _ctx: *mut ContextFFI,
427 _start: *const u8,
428 _start_len: usize,
429 _start_bound_type: u8,
430 _end: *const u8,
431 _end_len: usize,
432 _end_bound_type: u8,
433 _iterator_out: *mut *mut StoreIteratorFFI,
434) -> i32 {
435 unimplemented!()
436}
437
438extern "C" fn test_store_iterator_next(
440 _iterator: *mut StoreIteratorFFI,
441 _key_out: *mut BufferFFI,
442 _value_out: *mut BufferFFI,
443) -> i32 {
444 unimplemented!()
445}
446
447extern "C" fn test_store_iterator_free(_iterator: *mut StoreIteratorFFI) {
449 unimplemented!()
450}
451
452use std::ptr;
453
454use reifydb_abi::{
455 callbacks::{
456 builder::BuilderCallbacks, catalog::CatalogCallbacks, host::HostCallbacks, log::LogCallbacks,
457 memory::MemoryCallbacks, rql::RqlCallbacks, state::StateCallbacks, store::StoreCallbacks,
458 },
459 catalog::{namespace::NamespaceFFI, table::TableFFI},
460 constants::{FFI_END_OF_ITERATION, FFI_ERROR_INTERNAL, FFI_ERROR_NULL_PTR, FFI_NOT_FOUND, FFI_OK},
461 context::{
462 context::ContextFFI,
463 iterators::{StateIteratorFFI, StoreIteratorFFI},
464 },
465 data::buffer::BufferFFI,
466};
467use reifydb_core::encoded::key::EncodedKey;
468
469use crate::testing::{
470 context::TestContext,
471 registry::{
472 test_acquire, test_bitvec_ptr, test_commit, test_data_ptr, test_emit_diff, test_grow, test_offsets_ptr,
473 test_release,
474 },
475};
476
477extern "C" fn test_catalog_find_namespace(
479 _ctx: *mut ContextFFI,
480 _namespace_id: u64,
481 _version: u64,
482 _output: *mut NamespaceFFI,
483) -> i32 {
484 1 }
486
487extern "C" fn test_catalog_find_namespace_by_name(
489 _ctx: *mut ContextFFI,
490 _name_ptr: *const u8,
491 _name_len: usize,
492 _version: u64,
493 _output: *mut NamespaceFFI,
494) -> i32 {
495 1 }
497
498extern "C" fn test_catalog_find_table(
500 _ctx: *mut ContextFFI,
501 _table_id: u64,
502 _version: u64,
503 _output: *mut TableFFI,
504) -> i32 {
505 1 }
507
508extern "C" fn test_catalog_find_table_by_name(
510 _ctx: *mut ContextFFI,
511 _namespace_id: u64,
512 _name_ptr: *const u8,
513 _name_len: usize,
514 _version: u64,
515 _output: *mut TableFFI,
516) -> i32 {
517 1 }
519
520extern "C" fn test_catalog_free_namespace(_namespace: *mut NamespaceFFI) {
522 }
524
525extern "C" fn test_catalog_free_table(_table: *mut TableFFI) {
527 }
529
530unsafe extern "C" fn test_rql(
531 _ctx: *mut ContextFFI,
532 _rql_ptr: *const u8,
533 _rql_len: usize,
534 _params_ptr: *const u8,
535 _params_len: usize,
536 _result_out: *mut BufferFFI,
537) -> i32 {
538 FFI_ERROR_INTERNAL
539}
540
541pub fn create_test_callbacks() -> HostCallbacks {
543 HostCallbacks {
544 memory: MemoryCallbacks {
545 alloc: test_alloc,
546 free: test_free,
547 realloc: test_realloc,
548 },
549 state: StateCallbacks {
550 get: test_state_get,
551 set: test_state_set,
552 remove: test_state_remove,
553 clear: test_state_clear,
554 prefix: test_state_prefix,
555 range: test_state_range,
556 iterator_next: test_state_iterator_next,
557 iterator_free: test_state_iterator_free,
558 },
559 log: LogCallbacks {
560 message: test_log_message,
561 },
562 store: StoreCallbacks {
563 get: test_store_get,
564 contains_key: test_store_contains_key,
565 prefix: test_store_prefix,
566 range: test_store_range,
567 iterator_next: test_store_iterator_next,
568 iterator_free: test_store_iterator_free,
569 },
570 catalog: CatalogCallbacks {
571 find_namespace: test_catalog_find_namespace,
572 find_namespace_by_name: test_catalog_find_namespace_by_name,
573 find_table: test_catalog_find_table,
574 find_table_by_name: test_catalog_find_table_by_name,
575 free_namespace: test_catalog_free_namespace,
576 free_table: test_catalog_free_table,
577 },
578 rql: RqlCallbacks {
579 rql: test_rql,
580 },
581 builder: BuilderCallbacks {
582 acquire: test_acquire,
583 data_ptr: test_data_ptr,
584 offsets_ptr: test_offsets_ptr,
585 bitvec_ptr: test_bitvec_ptr,
586 grow: test_grow,
587 commit: test_commit,
588 release: test_release,
589 emit_diff: test_emit_diff,
590 },
591 }
592}