1use std::ffi::{CStr, CString};
6use std::os::raw::{c_char, c_int};
7use std::ptr;
8use std::sync::{Arc, Mutex};
9
10use crate::corpus::ConCorpus;
11use crate::keys::{hash_frame_bytes, ContentHash, FrameKey};
12use crate::select::Select;
13
14pub const RKRDB_OK: c_int = 0;
15pub const RKRDB_ERR: c_int = -1;
16pub const RKRDB_NOT_FOUND: c_int = -2;
17pub const RKRDB_NULL: c_int = -3;
18
19struct Handle {
20 corpus: Arc<ConCorpus>,
22 last_keys: Vec<FrameKey>,
23 last_error: String,
24}
25
26static HANDLES: Mutex<Vec<Option<Box<Handle>>>> = Mutex::new(Vec::new());
27
28fn push_handle(h: Handle) -> usize {
29 let mut g = HANDLES.lock().unwrap();
30 for (i, slot) in g.iter_mut().enumerate() {
31 if slot.is_none() {
32 *slot = Some(Box::new(h));
33 return i;
34 }
35 }
36 g.push(Some(Box::new(h)));
37 g.len() - 1
38}
39
40fn with_handle<F, T>(id: usize, f: F) -> Result<T, c_int>
42where
43 F: FnOnce(&mut Handle) -> Result<T, c_int>,
44{
45 let mut g = HANDLES.lock().unwrap();
46 let slot = g.get_mut(id).ok_or(RKRDB_NULL)?;
47 let h = slot.as_mut().ok_or(RKRDB_NULL)?;
48 f(h)
49}
50
51fn corpus_arc(id: usize) -> Result<Arc<ConCorpus>, c_int> {
52 let g = HANDLES.lock().unwrap();
53 let slot = g.get(id).ok_or(RKRDB_NULL)?;
54 let h = slot.as_ref().ok_or(RKRDB_NULL)?;
55 Ok(Arc::clone(&h.corpus))
56}
57
58fn set_err_id(id: usize, e: impl ToString) {
59 let mut g = HANDLES.lock().unwrap();
60 if let Some(Some(h)) = g.get_mut(id) {
61 h.last_error = e.to_string();
62 }
63}
64
65fn set_err(h: &mut Handle, e: impl ToString) {
66 h.last_error = e.to_string();
67}
68
69#[no_mangle]
72pub unsafe extern "C" fn rkrdb_open(path: *const c_char, out_id: *mut usize) -> c_int {
73 if path.is_null() || out_id.is_null() {
74 return RKRDB_NULL;
75 }
76 let cpath = unsafe { CStr::from_ptr(path) };
77 let path = match cpath.to_str() {
78 Ok(s) => s,
79 Err(_) => return RKRDB_ERR,
80 };
81 match ConCorpus::open(path) {
82 Ok(corpus) => {
83 let id = push_handle(Handle {
84 corpus: Arc::new(corpus),
85 last_keys: Vec::new(),
86 last_error: String::new(),
87 });
88 unsafe { *out_id = id };
89 RKRDB_OK
90 }
91 Err(_) => RKRDB_ERR,
92 }
93}
94
95#[no_mangle]
96pub unsafe extern "C" fn rkrdb_close(id: usize) -> c_int {
97 let mut g = HANDLES.lock().unwrap();
98 if let Some(slot) = g.get_mut(id) {
99 *slot = None;
100 RKRDB_OK
101 } else {
102 RKRDB_NULL
103 }
104}
105
106#[no_mangle]
109pub unsafe extern "C" fn rkrdb_last_error(id: usize, buf: *mut c_char, buflen: usize) -> c_int {
110 if buf.is_null() || buflen == 0 {
111 return RKRDB_NULL;
112 }
113 with_handle(id, |h| {
114 let bytes = h.last_error.as_bytes();
115 let n = (buflen - 1).min(bytes.len());
116 unsafe {
117 ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, n);
118 *buf.add(n) = 0;
119 }
120 Ok(n as c_int)
121 })
122 .unwrap_or(RKRDB_NULL)
123}
124
125#[no_mangle]
126pub unsafe extern "C" fn rkrdb_append_trajectory(
127 id: usize,
128 traj_id: u64,
129 path: *const c_char,
130 out_n_frames: *mut u32,
131) -> c_int {
132 if path.is_null() {
133 return RKRDB_NULL;
134 }
135 let cpath = unsafe { CStr::from_ptr(path) };
136 let path = match cpath.to_str() {
137 Ok(s) => s,
138 Err(_) => return RKRDB_ERR,
139 };
140 let corpus = match corpus_arc(id) {
142 Ok(c) => c,
143 Err(c) => return c,
144 };
145 match corpus.append_trajectory_path(traj_id, path) {
146 Ok(n) => {
147 if !out_n_frames.is_null() {
148 unsafe { *out_n_frames = n };
149 }
150 RKRDB_OK
151 }
152 Err(e) => {
153 set_err_id(id, e);
154 RKRDB_ERR
155 }
156 }
157}
158
159#[no_mangle]
162pub unsafe extern "C" fn rkrdb_select_basic(
163 id: usize,
164 traj_id: i64,
165 symbol: *const c_char,
166 natoms_min: u32,
167 natoms_max: u32,
168 limit: u32,
169) -> c_int {
170 with_handle(id, |h| {
171 let mut sel = Select::new().natoms_range(natoms_min, natoms_max);
172 if traj_id >= 0 {
173 sel = sel.trajectory(traj_id as u64);
174 }
175 if !symbol.is_null() {
176 let s = unsafe { CStr::from_ptr(symbol) };
177 if let Ok(sym) = s.to_str() {
178 if !sym.is_empty() {
179 sel = sel.require_symbol(sym);
180 }
181 }
182 }
183 if limit > 0 {
184 sel = sel.limit(limit as usize);
185 }
186 match h.corpus.select(&sel) {
187 Ok(keys) => {
188 h.last_keys = keys;
189 Ok(RKRDB_OK)
190 }
191 Err(e) => {
192 set_err(h, e);
193 Ok(RKRDB_ERR)
194 }
195 }
196 })
197 .unwrap_or(RKRDB_NULL)
198}
199
200#[no_mangle]
202pub unsafe extern "C" fn rkrdb_select_hash(id: usize, hash16: *const u8) -> c_int {
203 if hash16.is_null() {
204 return RKRDB_NULL;
205 }
206 let mut hb = [0u8; 16];
207 unsafe { ptr::copy_nonoverlapping(hash16, hb.as_mut_ptr(), 16) };
208 with_handle(id, |h| {
209 let sel = Select::new().exact_hash(hb);
210 match h.corpus.select(&sel) {
211 Ok(keys) => {
212 h.last_keys = keys;
213 Ok(RKRDB_OK)
214 }
215 Err(e) => {
216 set_err(h, e);
217 Ok(RKRDB_ERR)
218 }
219 }
220 })
221 .unwrap_or(RKRDB_NULL)
222}
223
224#[no_mangle]
227pub unsafe extern "C" fn rkrdb_select_meta(
228 id: usize,
229 traj_id: i64,
230 symbol: *const c_char,
231 natoms_min: u32,
232 natoms_max: u32,
233 energy_min: f64,
234 energy_max: f64,
235 use_energy_range: c_int,
236 flags: u32,
237 limit: u32,
238) -> c_int {
239 with_handle(id, |h| {
240 let mut sel = Select::new().natoms_range(natoms_min, natoms_max);
241 if traj_id >= 0 {
242 sel = sel.trajectory(traj_id as u64);
243 }
244 if !symbol.is_null() {
245 let s = unsafe { CStr::from_ptr(symbol) };
246 if let Ok(sym) = s.to_str() {
247 if !sym.is_empty() {
248 sel = sel.require_symbol(sym);
249 }
250 }
251 }
252 if use_energy_range != 0 {
253 sel = sel.energy_range(energy_min, energy_max);
254 }
255 if flags & 1 != 0 {
256 sel = sel.require_forces();
257 }
258 if flags & 2 != 0 {
259 sel = sel.require_velocities();
260 }
261 if flags & 4 != 0 {
262 sel = sel.require_energy();
263 }
264 if limit > 0 {
265 sel = sel.limit(limit as usize);
266 }
267 match h.corpus.select(&sel) {
268 Ok(keys) => {
269 h.last_keys = keys;
270 Ok(RKRDB_OK)
271 }
272 Err(e) => {
273 set_err(h, e);
274 Ok(RKRDB_ERR)
275 }
276 }
277 })
278 .unwrap_or(RKRDB_NULL)
279}
280
281
282#[no_mangle]
284pub unsafe extern "C" fn rkrdb_reindex(id: usize) -> c_int {
285 with_handle(id, |h| match h.corpus.reindex() {
286 Ok(_) => Ok(RKRDB_OK),
287 Err(e) => {
288 set_err(h, e);
289 Ok(RKRDB_ERR)
290 }
291 })
292 .unwrap_or(RKRDB_NULL)
293}
294
295#[no_mangle]
297pub unsafe extern "C" fn rkrdb_cook_frame(id: usize, traj_id: u64, frame_idx: u32) -> c_int {
298 with_handle(id, |h| {
299 match h.corpus.cook_frame(crate::keys::FrameKey {
300 traj_id,
301 frame_idx,
302 }) {
303 Ok(_) => Ok(RKRDB_OK),
304 Err(e) => {
305 set_err(h, e);
306 Ok(RKRDB_ERR)
307 }
308 }
309 })
310 .unwrap_or(RKRDB_NULL)
311}
312
313#[no_mangle]
315pub unsafe extern "C" fn rkrdb_delete_cooked(id: usize, traj_id: u64, frame_idx: u32) -> c_int {
316 with_handle(id, |h| {
317 match h.corpus.delete_cooked_soa(crate::keys::FrameKey {
318 traj_id,
319 frame_idx,
320 }) {
321 Ok(()) => Ok(RKRDB_OK),
322 Err(e) => {
323 set_err(h, e);
324 Ok(RKRDB_ERR)
325 }
326 }
327 })
328 .unwrap_or(RKRDB_NULL)
329}
330
331#[no_mangle]
333pub unsafe extern "C" fn rkrdb_has_valid_cooked(id: usize, traj_id: u64, frame_idx: u32) -> c_int {
334 with_handle(id, |h| {
335 match h.corpus.has_valid_cooked_soa(crate::keys::FrameKey {
336 traj_id,
337 frame_idx,
338 }) {
339 Ok(true) => Ok(1),
340 Ok(false) => Ok(0),
341 Err(e) => {
342 set_err(h, e);
343 Ok(RKRDB_ERR)
344 }
345 }
346 })
347 .unwrap_or(RKRDB_NULL)
348}
349
350#[no_mangle]
353pub unsafe extern "C" fn rkrdb_get_positions(
354 id: usize,
355 traj_id: u64,
356 frame_idx: u32,
357 out_xyz: *mut f64,
358 capacity_atoms: u32,
359 out_natoms: *mut u32,
360) -> c_int {
361 if out_xyz.is_null() || out_natoms.is_null() {
362 return RKRDB_NULL;
363 }
364 with_handle(id, |h| {
365 match h.corpus.get_positions(crate::keys::FrameKey {
366 traj_id,
367 frame_idx,
368 }) {
369 Ok(pos) => {
370 let n = pos.len() as u32;
371 if n > capacity_atoms {
372 set_err(
373 h,
374 crate::error::Error::Message("positions buffer too small".into()),
375 );
376 return Ok(RKRDB_ERR);
377 }
378 unsafe {
379 *out_natoms = n;
380 for (i, row) in pos.iter().enumerate() {
381 *out_xyz.add(i * 3) = row[0];
382 *out_xyz.add(i * 3 + 1) = row[1];
383 *out_xyz.add(i * 3 + 2) = row[2];
384 }
385 }
386 Ok(RKRDB_OK)
387 }
388 Err(e) => {
389 set_err(h, e);
390 Ok(RKRDB_ERR)
391 }
392 }
393 })
394 .unwrap_or(RKRDB_NULL)
395}
396
397#[no_mangle]
399pub unsafe extern "C" fn rkrdb_get_forces(
400 id: usize,
401 traj_id: u64,
402 frame_idx: u32,
403 out_xyz: *mut f64,
404 capacity_atoms: u32,
405 out_natoms: *mut u32,
406 out_has_forces: *mut u8,
407) -> c_int {
408 if out_xyz.is_null() || out_natoms.is_null() || out_has_forces.is_null() {
409 return RKRDB_NULL;
410 }
411 with_handle(id, |h| {
412 match h.corpus.get_forces(crate::keys::FrameKey {
413 traj_id,
414 frame_idx,
415 }) {
416 Ok(None) => {
417 unsafe {
418 *out_has_forces = 0;
419 *out_natoms = 0;
420 }
421 Ok(RKRDB_OK)
422 }
423 Ok(Some(frc)) => {
424 let n = frc.len() as u32;
425 if n > capacity_atoms {
426 set_err(
427 h,
428 crate::error::Error::Message("forces buffer too small".into()),
429 );
430 return Ok(RKRDB_ERR);
431 }
432 unsafe {
433 *out_has_forces = 1;
434 *out_natoms = n;
435 for (i, row) in frc.iter().enumerate() {
436 *out_xyz.add(i * 3) = row[0];
437 *out_xyz.add(i * 3 + 1) = row[1];
438 *out_xyz.add(i * 3 + 2) = row[2];
439 }
440 }
441 Ok(RKRDB_OK)
442 }
443 Err(e) => {
444 set_err(h, e);
445 Ok(RKRDB_ERR)
446 }
447 }
448 })
449 .unwrap_or(RKRDB_NULL)
450}
451
452#[no_mangle]
454pub unsafe extern "C" fn rkrdb_recook_all(id: usize) -> c_int {
455 with_handle(id, |h| match h.corpus.recook_all() {
456 Ok(_) => Ok(RKRDB_OK),
457 Err(e) => {
458 set_err(h, e);
459 Ok(RKRDB_ERR)
460 }
461 })
462 .unwrap_or(RKRDB_NULL)
463}
464
465#[no_mangle]
469pub unsafe extern "C" fn rkrdb_frame_formula(
470 id: usize,
471 traj_id: u64,
472 frame_idx: u32,
473 buf: *mut c_char,
474 buflen: usize,
475) -> c_int {
476 if buf.is_null() || buflen == 0 {
477 return RKRDB_NULL;
478 }
479 with_handle(id, |h| {
480 match h.corpus.frame_formula(crate::keys::FrameKey {
481 traj_id,
482 frame_idx,
483 }) {
484 Ok(s) => {
485 let bytes = s.as_bytes();
486 if bytes.len() + 1 > buflen {
487 set_err(h, crate::error::Error::Message("buffer too small".into()));
488 return Ok(RKRDB_ERR);
489 }
490 unsafe {
491 std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, bytes.len());
492 *buf.add(bytes.len()) = 0;
493 }
494 Ok(RKRDB_OK)
495 }
496 Err(e) => {
497 set_err(h, e);
498 Ok(RKRDB_ERR)
499 }
500 }
501 })
502 .unwrap_or(RKRDB_NULL)
503}
504
505#[no_mangle]
509pub unsafe extern "C" fn rkrdb_select_campaign(
510 id: usize,
511 traj_id: i64,
512 symbol: *const c_char,
513 natoms_min: u32,
514 natoms_max: u32,
515 formula: *const c_char,
516 energy_min: f64,
517 energy_max: f64,
518 use_energy_range: c_int,
519 fmax_min: f64,
520 fmax_max: f64,
521 use_fmax_range: c_int,
522 elem_sym: *const c_char,
523 elem_count: u32,
524 elem_exact: c_int,
525 flags: u32,
526 limit: u32,
527) -> c_int {
528 with_handle(id, |h| {
529 let mut sel = Select::new().natoms_range(natoms_min, natoms_max);
530 if traj_id >= 0 {
531 sel = sel.trajectory(traj_id as u64);
532 }
533 if !symbol.is_null() {
534 let s = unsafe { CStr::from_ptr(symbol) };
535 if let Ok(sym) = s.to_str() {
536 if !sym.is_empty() {
537 sel = sel.require_symbol(sym);
538 }
539 }
540 }
541 if !formula.is_null() {
542 let s = unsafe { CStr::from_ptr(formula) };
543 if let Ok(f) = s.to_str() {
544 if !f.is_empty() {
545 sel = sel.exact_composition(f);
546 }
547 }
548 }
549 if use_energy_range != 0 {
550 sel = sel.energy_range(energy_min, energy_max);
551 }
552 if use_fmax_range != 0 {
553 sel = sel.fmax_range(fmax_min, fmax_max);
554 }
555 if !elem_sym.is_null() {
556 let s = unsafe { CStr::from_ptr(elem_sym) };
557 if let Ok(sym) = s.to_str() {
558 if !sym.is_empty() {
559 if elem_exact != 0 {
560 sel = sel.element_exact(sym, elem_count);
561 } else {
562 sel = sel.element_min(sym, elem_count);
563 }
564 }
565 }
566 }
567 if flags & 1 != 0 {
568 sel = sel.require_forces();
569 }
570 if flags & 2 != 0 {
571 sel = sel.require_velocities();
572 }
573 if flags & 4 != 0 {
574 sel = sel.require_energy();
575 }
576 if limit > 0 {
577 sel = sel.limit(limit as usize);
578 }
579 match h.corpus.select(&sel) {
580 Ok(keys) => {
581 h.last_keys = keys;
582 Ok(RKRDB_OK)
583 }
584 Err(e) => {
585 set_err(h, e);
586 Ok(RKRDB_ERR)
587 }
588 }
589 })
590 .unwrap_or(RKRDB_NULL)
591}
592
593#[no_mangle]
594pub unsafe extern "C" fn rkrdb_result_count(id: usize) -> c_int {
595 with_handle(id, |h| Ok(h.last_keys.len() as c_int)).unwrap_or(RKRDB_NULL)
596}
597
598#[no_mangle]
600pub unsafe extern "C" fn rkrdb_result_key(
601 id: usize,
602 i: usize,
603 out_traj: *mut u64,
604 out_frame: *mut u32,
605) -> c_int {
606 if out_traj.is_null() || out_frame.is_null() {
607 return RKRDB_NULL;
608 }
609 with_handle(id, |h| {
610 let k = match h.last_keys.get(i) {
611 Some(k) => *k,
612 None => return Ok(RKRDB_NOT_FOUND),
613 };
614 unsafe {
615 *out_traj = k.traj_id;
616 *out_frame = k.frame_idx;
617 }
618 Ok(RKRDB_OK)
619 })
620 .unwrap_or(RKRDB_NULL)
621}
622
623#[no_mangle]
625pub unsafe extern "C" fn rkrdb_frame_hash(
626 id: usize,
627 traj_id: u64,
628 frame_idx: u32,
629 out_hash16: *mut u8,
630) -> c_int {
631 if out_hash16.is_null() {
632 return RKRDB_NULL;
633 }
634 let key = FrameKey {
635 traj_id,
636 frame_idx,
637 };
638 with_handle(id, |h| match h.corpus.frame_hash(key) {
639 Ok(hash) => {
640 let b = hash.to_bytes();
641 unsafe { ptr::copy_nonoverlapping(b.as_ptr(), out_hash16, 16) };
642 Ok(RKRDB_OK)
643 }
644 Err(e) => {
645 set_err(h, e);
646 Ok(RKRDB_ERR)
647 }
648 })
649 .unwrap_or(RKRDB_NULL)
650}
651
652#[no_mangle]
654pub unsafe extern "C" fn rkrdb_get_frame_text(
655 id: usize,
656 traj_id: u64,
657 frame_idx: u32,
658 buf: *mut c_char,
659 buflen: usize,
660) -> c_int {
661 if buf.is_null() || buflen == 0 {
662 return RKRDB_NULL;
663 }
664 let key = FrameKey {
665 traj_id,
666 frame_idx,
667 };
668 with_handle(id, |h| match h.corpus.get_frame_text(key) {
669 Ok(text) => {
670 let bytes = text.as_bytes();
671 if bytes.len() + 1 > buflen {
672 set_err(h, "buffer too small");
673 return Ok(RKRDB_ERR);
674 }
675 unsafe {
676 ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, bytes.len());
677 *buf.add(bytes.len()) = 0;
678 }
679 Ok(bytes.len() as c_int)
680 }
681 Err(e) => {
682 set_err(h, e);
683 Ok(RKRDB_ERR)
684 }
685 })
686 .unwrap_or(RKRDB_NULL)
687}
688
689#[no_mangle]
691pub unsafe extern "C" fn rkrdb_xxh3_128(data: *const u8, len: usize, out_hash16: *mut u8) -> c_int {
692 if data.is_null() || out_hash16.is_null() {
693 return RKRDB_NULL;
694 }
695 let slice = unsafe { std::slice::from_raw_parts(data, len) };
696 let h = hash_frame_bytes(slice);
697 let b = h.to_bytes();
698 unsafe { ptr::copy_nonoverlapping(b.as_ptr(), out_hash16, 16) };
699 RKRDB_OK
700}
701
702#[allow(dead_code)]
704fn _cs(s: &str) -> Result<CString, c_int> {
705 CString::new(s).map_err(|_| RKRDB_ERR)
706}
707
708#[allow(dead_code)]
710fn _ch(b: [u8; 16]) -> ContentHash {
711 ContentHash(b)
712}