embeddenator_interop/
ffi.rs1use embeddenator_vsa::{ReversibleVSAConfig, SparseVec};
28use std::ffi::CStr;
29use std::os::raw::c_char;
30use std::ptr;
31use std::slice;
32
33#[repr(C)]
39pub struct SparseVecHandle {
40 _private: [u8; 0],
41}
42
43#[repr(C)]
45pub struct VSAConfigHandle {
46 _private: [u8; 0],
47}
48
49#[repr(C)]
51pub struct ByteBuffer {
52 pub data: *mut u8,
53 pub len: usize,
54 pub capacity: usize,
55}
56
57unsafe fn to_handle<T>(obj: T) -> *mut T {
63 Box::into_raw(Box::new(obj))
64}
65
66unsafe fn from_handle<T>(handle: *mut T) -> Box<T> {
68 assert!(!handle.is_null(), "FFI: null handle");
69 Box::from_raw(handle)
70}
71
72unsafe fn borrow_handle<T>(handle: *const T) -> &'static T {
74 assert!(!handle.is_null(), "FFI: null handle");
75 &*handle
76}
77
78#[allow(dead_code)]
80unsafe fn borrow_handle_mut<T>(handle: *mut T) -> &'static mut T {
81 assert!(!handle.is_null(), "FFI: null handle");
82 &mut *handle
83}
84
85#[no_mangle]
94pub unsafe extern "C" fn sparse_vec_new() -> *mut SparseVecHandle {
95 let vec = SparseVec::new();
96 to_handle(vec) as *mut SparseVecHandle
97}
98
99#[no_mangle]
105pub unsafe extern "C" fn sparse_vec_free(handle: *mut SparseVecHandle) {
106 if !handle.is_null() {
107 let _ = from_handle(handle as *mut SparseVec);
108 }
109}
110
111#[no_mangle]
117pub unsafe extern "C" fn sparse_vec_bundle(
118 a: *const SparseVecHandle,
119 b: *const SparseVecHandle,
120) -> *mut SparseVecHandle {
121 let a_vec = borrow_handle(a as *const SparseVec);
122 let b_vec = borrow_handle(b as *const SparseVec);
123 let result = a_vec.bundle(b_vec);
124 to_handle(result) as *mut SparseVecHandle
125}
126
127#[no_mangle]
133pub unsafe extern "C" fn sparse_vec_bind(
134 a: *const SparseVecHandle,
135 b: *const SparseVecHandle,
136) -> *mut SparseVecHandle {
137 let a_vec = borrow_handle(a as *const SparseVec);
138 let b_vec = borrow_handle(b as *const SparseVec);
139 let result = a_vec.bind(b_vec);
140 to_handle(result) as *mut SparseVecHandle
141}
142
143#[no_mangle]
148pub unsafe extern "C" fn sparse_vec_cosine(
149 a: *const SparseVecHandle,
150 b: *const SparseVecHandle,
151) -> f64 {
152 let a_vec = borrow_handle(a as *const SparseVec);
153 let b_vec = borrow_handle(b as *const SparseVec);
154 a_vec.cosine(b_vec)
155}
156
157#[no_mangle]
163pub unsafe extern "C" fn sparse_vec_to_json(handle: *const SparseVecHandle) -> ByteBuffer {
164 let vec = borrow_handle(handle as *const SparseVec);
165 match serde_json::to_vec(vec) {
166 Ok(mut bytes) => {
167 let len = bytes.len();
168 let capacity = bytes.capacity();
169 let data = bytes.as_mut_ptr();
170 std::mem::forget(bytes);
171 ByteBuffer {
172 data,
173 len,
174 capacity,
175 }
176 }
177 Err(_) => ByteBuffer {
178 data: ptr::null_mut(),
179 len: 0,
180 capacity: 0,
181 },
182 }
183}
184
185#[no_mangle]
192pub unsafe extern "C" fn sparse_vec_from_json(data: *const u8, len: usize) -> *mut SparseVecHandle {
193 if data.is_null() {
194 return ptr::null_mut();
195 }
196 let bytes = slice::from_raw_parts(data, len);
197 match serde_json::from_slice::<SparseVec>(bytes) {
198 Ok(vec) => to_handle(vec) as *mut SparseVecHandle,
199 Err(_) => ptr::null_mut(),
200 }
201}
202
203#[no_mangle]
212pub unsafe extern "C" fn vsa_config_new() -> *mut VSAConfigHandle {
213 let config = ReversibleVSAConfig::default();
214 to_handle(config) as *mut VSAConfigHandle
215}
216
217#[no_mangle]
222pub unsafe extern "C" fn vsa_config_new_custom(
223 block_size: usize,
224 max_path_depth: usize,
225 base_shift: usize,
226 target_sparsity: usize,
227) -> *mut VSAConfigHandle {
228 let config = ReversibleVSAConfig {
229 block_size,
230 max_path_depth,
231 base_shift,
232 target_sparsity,
233 };
234 to_handle(config) as *mut VSAConfigHandle
235}
236
237#[no_mangle]
243pub unsafe extern "C" fn vsa_config_free(handle: *mut VSAConfigHandle) {
244 if !handle.is_null() {
245 let _ = from_handle(handle as *mut ReversibleVSAConfig);
246 }
247}
248
249#[no_mangle]
258pub unsafe extern "C" fn vsa_encode_data(
259 config: *const VSAConfigHandle,
260 data: *const u8,
261 len: usize,
262 path: *const c_char,
263) -> *mut SparseVecHandle {
264 let config_ref = borrow_handle(config as *const ReversibleVSAConfig);
265 let bytes = slice::from_raw_parts(data, len);
266
267 let path_str = if path.is_null() {
268 None
269 } else {
270 CStr::from_ptr(path).to_str().ok()
271 };
272
273 let vec = SparseVec::encode_data(bytes, config_ref, path_str);
274 to_handle(vec) as *mut SparseVecHandle
275}
276
277#[no_mangle]
284pub unsafe extern "C" fn vsa_decode_data(
285 config: *const VSAConfigHandle,
286 vec: *const SparseVecHandle,
287 path: *const c_char,
288 expected_size: usize,
289) -> ByteBuffer {
290 let config_ref = borrow_handle(config as *const ReversibleVSAConfig);
291 let vec_ref = borrow_handle(vec as *const SparseVec);
292
293 let path_str = if path.is_null() {
294 None
295 } else {
296 CStr::from_ptr(path).to_str().ok()
297 };
298
299 let mut decoded = vec_ref.decode_data(config_ref, path_str, expected_size);
300 let len = decoded.len();
301 let capacity = decoded.capacity();
302 let data = decoded.as_mut_ptr();
303 std::mem::forget(decoded);
304
305 ByteBuffer {
306 data,
307 len,
308 capacity,
309 }
310}
311
312#[no_mangle]
322pub unsafe extern "C" fn byte_buffer_free(buffer: ByteBuffer) {
323 if !buffer.data.is_null() {
324 let _ = Vec::from_raw_parts(buffer.data, buffer.len, buffer.capacity);
325 }
326}
327
328#[no_mangle]
338pub unsafe extern "C" fn embeddenator_last_error() -> *const c_char {
339 ptr::null()
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 #[test]
348 fn test_sparse_vec_create_free() {
349 unsafe {
350 let handle = sparse_vec_new();
351 assert!(!handle.is_null());
352 sparse_vec_free(handle);
353 }
354 }
355
356 #[test]
357 fn test_sparse_vec_operations() {
358 unsafe {
359 let a = sparse_vec_new();
360 let b = sparse_vec_new();
361
362 let bundled = sparse_vec_bundle(a, b);
363 assert!(!bundled.is_null());
364
365 let bound = sparse_vec_bind(a, b);
366 assert!(!bound.is_null());
367
368 let cosine = sparse_vec_cosine(a, b);
369 assert!(cosine.is_finite());
370
371 sparse_vec_free(bundled);
372 sparse_vec_free(bound);
373 sparse_vec_free(a);
374 sparse_vec_free(b);
375 }
376 }
377
378 #[test]
379 fn test_sparse_vec_json_roundtrip() {
380 unsafe {
381 let vec = sparse_vec_new();
382
383 let buffer = sparse_vec_to_json(vec);
384 assert!(!buffer.data.is_null());
385 assert!(buffer.len > 0);
386
387 let decoded = sparse_vec_from_json(buffer.data, buffer.len);
388 assert!(!decoded.is_null());
389
390 byte_buffer_free(buffer);
391 sparse_vec_free(vec);
392 sparse_vec_free(decoded);
393 }
394 }
395
396 #[test]
397 fn test_vsa_config() {
398 unsafe {
399 let config = vsa_config_new();
400 assert!(!config.is_null());
401 vsa_config_free(config);
402
403 let custom = vsa_config_new_custom(256, 10, 1000, 200);
404 assert!(!custom.is_null());
405 vsa_config_free(custom);
406 }
407 }
408
409 #[test]
410 fn test_encode_decode() {
411 unsafe {
412 let config = vsa_config_new();
413 let data = b"Hello, FFI!";
414
415 let vec = vsa_encode_data(config, data.as_ptr(), data.len(), ptr::null());
416 assert!(!vec.is_null());
417
418 let decoded = vsa_decode_data(config, vec, ptr::null(), data.len());
419 assert!(!decoded.data.is_null());
420 assert_eq!(decoded.len, data.len());
421
422 byte_buffer_free(decoded);
423 sparse_vec_free(vec);
424 vsa_config_free(config);
425 }
426 }
427}