1#![allow(non_camel_case_types)]
17
18use std::marker::{PhantomData, PhantomPinned};
19use std::os::raw::{c_char, c_int};
20
21pub const SEMQ_FILE_VERSION: u32 = 2;
27pub const SEMQ_RULE_REVISION: u32 = 0;
29pub const SEMQ_CONFIG_BYTES: u32 = 13;
31pub const SEMQ_DIGEST_BYTES: u32 = 32;
33pub const SEMQ_MAX_DIM: u32 = 65536;
35pub const SEMQ_ID_MAX_BYTES: u32 = 4096;
37pub const SEMQ_MANIFEST_MAX_PAIRS: u32 = 4096;
39pub const SEMQ_MANIFEST_KEY_MAX: u32 = 256;
40pub const SEMQ_MANIFEST_VALUE_MAX: u32 = 65536;
41pub const SEMQ_MANIFEST_SECTION_MAX: u32 = 16 * 1024 * 1024;
42pub const SEMQ_NONE: u64 = u64::MAX;
44
45pub type semq_operator_t = u32;
47pub const SEMQ_ORBIT: semq_operator_t = 0;
48pub const SEMQ_PHASE: semq_operator_t = 1;
49pub const SEMQ_QUANT: semq_operator_t = 2;
50
51pub type semq_id_kind_t = u32;
53pub const SEMQ_ID_U64: semq_id_kind_t = 0;
54pub const SEMQ_ID_UTF8: semq_id_kind_t = 1;
55
56pub type semq_status_t = u32;
58pub const SEMQ_OK: semq_status_t = 0;
59pub const SEMQ_ERR_INVALID_INPUT: semq_status_t = 1;
61pub const SEMQ_ERR_INCOMPATIBLE: semq_status_t = 2;
63pub const SEMQ_ERR_FORMAT: semq_status_t = 3;
65pub const SEMQ_ERR_INTEGRITY: semq_status_t = 4;
67pub const SEMQ_ERR_UNSUPPORTED: semq_status_t = 5;
69pub const SEMQ_ERR_NOMEM: semq_status_t = 6;
71pub const SEMQ_ERR_INTERNAL: semq_status_t = 7;
73
74pub type semq_which_t = u32;
76pub const SEMQ_WHICH_NONE: semq_which_t = 0;
77pub const SEMQ_WHICH_CONTENT: semq_which_t = 1;
78pub const SEMQ_WHICH_STATE: semq_which_t = 2;
79
80pub type semq_section_t = u32;
82pub const SEMQ_SECTION_FRAMING: semq_section_t = 0;
83pub const SEMQ_SECTION_CONFIG: semq_section_t = 1;
84pub const SEMQ_SECTION_SIZES: semq_section_t = 2;
85pub const SEMQ_SECTION_IDS: semq_section_t = 3;
86pub const SEMQ_SECTION_MANIFEST: semq_section_t = 4;
87pub const SEMQ_SECTION_FOOTER: semq_section_t = 5;
88pub const SEMQ_SECTION_ROWS: semq_section_t = 6;
89
90pub type semq_config_field_t = u32;
93pub const SEMQ_FIELD_OPERATOR: semq_config_field_t = 0;
94pub const SEMQ_FIELD_DIM: semq_config_field_t = 1;
95pub const SEMQ_FIELD_P1: semq_config_field_t = 2;
97pub const SEMQ_FIELD_P2: semq_config_field_t = 3;
98
99pub type semq_list_t = u32;
101pub const SEMQ_LIST_ADDED: semq_list_t = 0;
102pub const SEMQ_LIST_REMOVED: semq_list_t = 1;
103pub const SEMQ_LIST_CHANGED: semq_list_t = 2;
104
105pub type semq_check_t = u32;
108pub const SEMQ_CHECK_PER_ROW: semq_check_t = 1;
109
110pub type semq_reason_t = u32;
112pub const SEMQ_REASON_NO_COMMON_ROWS: semq_reason_t = 1;
113pub const SEMQ_REASON_REMOVED_ROWS: semq_reason_t = 2;
114pub const SEMQ_REASON_CHANGED_RATIO: semq_reason_t = 4;
115pub const SEMQ_REASON_HAMMING: semq_reason_t = 8;
116pub const SEMQ_REASON_ENCODER: semq_reason_t = 16;
117pub const SEMQ_REASON_ROW_ABOVE_MAX: semq_reason_t = 32;
118
119#[repr(C)]
128#[derive(Debug, Clone, Copy)]
129pub struct semq_error_t {
130 pub status: u32,
131 pub which: u32,
132 pub row: u64,
133 pub field: u64,
134 pub message: [c_char; 128],
135}
136
137#[repr(C)]
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
141pub struct semq_config_t {
142 pub op: u32,
143 pub dim: u32,
144 pub p1: u32,
145 pub p2: u32,
146}
147
148#[repr(C)]
150#[derive(Debug, Clone, Copy)]
151pub struct semq_pair_t {
152 pub key: *const u8,
153 pub key_len: u32,
154 pub value: *const u8,
155 pub value_len: u32,
156}
157
158#[repr(C)]
163#[derive(Debug, Clone, Copy)]
164pub struct semq_ids_t {
165 pub kind: u32,
166 pub n: u64,
167 pub u64: *const u64,
168 pub utf8_offsets: *const u64,
169 pub utf8_bytes: *const u8,
170}
171
172#[repr(C)]
174#[derive(Debug, Clone, Copy)]
175pub struct semq_part_t {
176 pub ptr: *const u8,
177 pub len: u64,
178}
179
180#[repr(C)]
183#[derive(Debug, Clone, Copy)]
184pub struct semq_manifest_change_t {
185 pub key: *const u8,
186 pub key_len: u32,
187 pub has_before: c_int,
188 pub before: *const u8,
189 pub before_len: u32,
190 pub has_after: c_int,
191 pub after: *const u8,
192 pub after_len: u32,
193}
194
195#[repr(C)]
201pub struct semq_codec_t {
202 _data: [u8; 0],
203 _marker: PhantomData<(*mut u8, PhantomPinned)>,
204}
205
206#[repr(C)]
208pub struct semq_encoding_t {
209 _data: [u8; 0],
210 _marker: PhantomData<(*mut u8, PhantomPinned)>,
211}
212
213#[repr(C)]
215pub struct semq_diff_t {
216 _data: [u8; 0],
217 _marker: PhantomData<(*mut u8, PhantomPinned)>,
218}
219
220#[repr(C)]
222pub struct semq_floor_t {
223 _data: [u8; 0],
224 _marker: PhantomData<(*mut u8, PhantomPinned)>,
225}
226
227#[repr(C)]
229pub struct semq_verdict_t {
230 _data: [u8; 0],
231 _marker: PhantomData<(*mut u8, PhantomPinned)>,
232}
233
234extern "C" {
239 pub fn semq_config_orbit(
241 dim: u32,
242 scale: u32,
243 out: *mut semq_config_t,
244 err: *mut semq_error_t,
245 ) -> semq_status_t;
246 pub fn semq_config_phase(
247 dim: u32,
248 sectors: u32,
249 out: *mut semq_config_t,
250 err: *mut semq_error_t,
251 ) -> semq_status_t;
252 pub fn semq_config_quant(
253 dim: u32,
254 bins: u32,
255 out: *mut semq_config_t,
256 err: *mut semq_error_t,
257 ) -> semq_status_t;
258 pub fn semq_config_validate(cfg: *const semq_config_t, err: *mut semq_error_t)
259 -> semq_status_t;
260 pub fn semq_config_to_bytes(cfg: *const semq_config_t, out: *mut u8);
262 pub fn semq_config_from_bytes(
264 input: *const u8,
265 out: *mut semq_config_t,
266 err: *mut semq_error_t,
267 ) -> semq_status_t;
268 pub fn semq_config_bytes_per_vector(cfg: *const semq_config_t) -> u32;
269 pub fn semq_config_units_per_row(cfg: *const semq_config_t) -> u32;
270 pub fn semq_config_max_magnitude(cfg: *const semq_config_t) -> f32;
272 pub fn semq_config_equal(a: *const semq_config_t, b: *const semq_config_t) -> c_int;
274
275 pub fn semq_codec_create(
277 cfg: *const semq_config_t,
278 out: *mut *mut semq_codec_t,
279 err: *mut semq_error_t,
280 ) -> semq_status_t;
281 pub fn semq_codec_free(codec: *mut semq_codec_t);
282 pub fn semq_codec_config(codec: *const semq_codec_t) -> *const semq_config_t;
283 pub fn semq_codec_backend(codec: *const semq_codec_t) -> *const c_char;
285 pub fn semq_codec_encode(
286 codec: *const semq_codec_t,
287 ids: *const semq_ids_t,
288 vectors: *const f32,
289 manifest: *const semq_pair_t,
290 n_pairs: u32,
291 out: *mut *mut semq_encoding_t,
292 err: *mut semq_error_t,
293 ) -> semq_status_t;
294 pub fn semq_codec_decode(
296 codec: *const semq_codec_t,
297 enc: *const semq_encoding_t,
298 out: *mut f32,
299 err: *mut semq_error_t,
300 ) -> semq_status_t;
301 pub fn semq_codec_unpack(
303 codec: *const semq_codec_t,
304 enc: *const semq_encoding_t,
305 out: *mut u8,
306 err: *mut semq_error_t,
307 ) -> semq_status_t;
308
309 pub fn semq_encoding_create(
311 cfg: *const semq_config_t,
312 ids: *const semq_ids_t,
313 rows: *const u8,
314 manifest: *const semq_pair_t,
315 n_pairs: u32,
316 out: *mut *mut semq_encoding_t,
317 err: *mut semq_error_t,
318 ) -> semq_status_t;
319 pub fn semq_encoding_free(enc: *mut semq_encoding_t);
320 pub fn semq_encoding_config(enc: *const semq_encoding_t) -> *const semq_config_t;
321 pub fn semq_encoding_id_kind(enc: *const semq_encoding_t) -> u32;
322 pub fn semq_encoding_len(enc: *const semq_encoding_t) -> u64;
323 pub fn semq_encoding_rows(enc: *const semq_encoding_t, len: *mut u64) -> *const u8;
325 pub fn semq_encoding_ids_u64(enc: *const semq_encoding_t) -> *const u64;
327 pub fn semq_encoding_ids_utf8_offsets(enc: *const semq_encoding_t) -> *const u64;
329 pub fn semq_encoding_ids_utf8_bytes(enc: *const semq_encoding_t, len: *mut u64) -> *const u8;
331 pub fn semq_encoding_manifest_len(enc: *const semq_encoding_t) -> u32;
333 pub fn semq_encoding_manifest_pair(
334 enc: *const semq_encoding_t,
335 i: u32,
336 out: *mut semq_pair_t,
337 err: *mut semq_error_t,
338 ) -> semq_status_t;
339 pub fn semq_encoding_content_digest(enc: *const semq_encoding_t, out: *mut u8);
340 pub fn semq_encoding_state_id(enc: *const semq_encoding_t, out: *mut u8);
341 pub fn semq_encoding_find_u64(
343 enc: *const semq_encoding_t,
344 id: u64,
345 index: *mut u64,
346 err: *mut semq_error_t,
347 ) -> semq_status_t;
348 pub fn semq_encoding_find_utf8(
349 enc: *const semq_encoding_t,
350 id: *const u8,
351 len: u64,
352 index: *mut u64,
353 err: *mut semq_error_t,
354 ) -> semq_status_t;
355 pub fn semq_encoding_file_size(enc: *const semq_encoding_t) -> u64;
357 pub fn semq_encoding_save_parts(enc: *const semq_encoding_t, parts: *mut semq_part_t) -> u32;
359 pub fn semq_encoding_save(
361 enc: *const semq_encoding_t,
362 out: *mut u8,
363 cap: u64,
364 err: *mut semq_error_t,
365 ) -> semq_status_t;
366 pub fn semq_encoding_load(
368 buf: *const u8,
369 len: u64,
370 out: *mut *mut semq_encoding_t,
371 err: *mut semq_error_t,
372 ) -> semq_status_t;
373 pub fn semq_encoding_concat(
376 parts: *const *const semq_encoding_t,
377 k: u32,
378 out: *mut *mut semq_encoding_t,
379 err: *mut semq_error_t,
380 ) -> semq_status_t;
381 pub fn semq_encoding_diff(
383 reference: *const semq_encoding_t,
384 candidate: *const semq_encoding_t,
385 out: *mut *mut semq_diff_t,
386 err: *mut semq_error_t,
387 ) -> semq_status_t;
388
389 pub fn semq_diff_free(diff: *mut semq_diff_t);
392 pub fn semq_diff_config(diff: *const semq_diff_t) -> *const semq_config_t;
393 pub fn semq_diff_id_kind(diff: *const semq_diff_t) -> u32;
394 pub fn semq_diff_reference_id(diff: *const semq_diff_t, out: *mut u8);
395 pub fn semq_diff_candidate_id(diff: *const semq_diff_t, out: *mut u8);
396 pub fn semq_diff_count(diff: *const semq_diff_t, list: u32) -> u64;
397 pub fn semq_diff_n_unchanged(diff: *const semq_diff_t) -> u64;
398 pub fn semq_diff_id(
401 diff: *const semq_diff_t,
402 list: u32,
403 i: u64,
404 out_u64: *mut u64,
405 bytes: *mut *const u8,
406 len: *mut u64,
407 err: *mut semq_error_t,
408 ) -> semq_status_t;
409 pub fn semq_diff_hamming(
411 diff: *const semq_diff_t,
412 i: u64,
413 out: *mut u64,
414 err: *mut semq_error_t,
415 ) -> semq_status_t;
416 pub fn semq_diff_manifest_changes(diff: *const semq_diff_t) -> u32;
417 pub fn semq_diff_manifest_change(
418 diff: *const semq_diff_t,
419 i: u32,
420 out: *mut semq_manifest_change_t,
421 err: *mut semq_error_t,
422 ) -> semq_status_t;
423 pub fn semq_diff_units_u64(
426 diff: *const semq_diff_t,
427 id: u64,
428 units: *mut u32,
429 ref_symbols: *mut u8,
430 cand_symbols: *mut u8,
431 cap: u64,
432 count: *mut u64,
433 err: *mut semq_error_t,
434 ) -> semq_status_t;
435 pub fn semq_diff_units_utf8(
436 diff: *const semq_diff_t,
437 id: *const u8,
438 len: u64,
439 units: *mut u32,
440 ref_symbols: *mut u8,
441 cand_symbols: *mut u8,
442 cap: u64,
443 count: *mut u64,
444 err: *mut semq_error_t,
445 ) -> semq_status_t;
446
447 pub fn semq_floor_create(
453 config: *const semq_config_t,
454 id_kind: u32,
455 reference_id: *const u8,
456 nulls: u64,
457 changed_rows: u64,
458 total_rows: u64,
459 hamming: u64,
460 out: *mut *mut semq_floor_t,
461 err: *mut semq_error_t,
462 ) -> semq_status_t;
463 pub fn semq_floor_free(floor: *mut semq_floor_t);
464 pub fn semq_floor_measure(
469 nulls: *const *const semq_diff_t,
470 k: u32,
471 out: *mut *mut semq_floor_t,
472 err: *mut semq_error_t,
473 ) -> semq_status_t;
474 pub fn semq_floor_measure_for(
478 nulls: *const *const semq_diff_t,
479 k: u32,
480 checks: u32,
481 out: *mut *mut semq_floor_t,
482 err: *mut semq_error_t,
483 ) -> semq_status_t;
484 pub fn semq_floor_config(floor: *const semq_floor_t) -> *const semq_config_t;
486 pub fn semq_floor_id_kind(floor: *const semq_floor_t) -> u32;
488 pub fn semq_floor_reference_id(floor: *const semq_floor_t, out: *mut u8);
490 pub fn semq_floor_nulls(floor: *const semq_floor_t) -> u64;
491 pub fn semq_floor_changed_rows(floor: *const semq_floor_t) -> u64;
492 pub fn semq_floor_total_rows(floor: *const semq_floor_t) -> u64;
493 pub fn semq_floor_hamming(floor: *const semq_floor_t) -> u64;
494 pub fn semq_floor_max_hamming(floor: *const semq_floor_t) -> u64;
496 pub fn semq_floor_distinct_nulls(floor: *const semq_floor_t) -> u64;
498 pub fn semq_floor_json_size(floor: *const semq_floor_t) -> u64;
500 pub fn semq_floor_save(
502 floor: *const semq_floor_t,
503 out: *mut u8,
504 cap: u64,
505 err: *mut semq_error_t,
506 ) -> semq_status_t;
507 pub fn semq_floor_load(
510 buf: *const u8,
511 len: u64,
512 out: *mut *mut semq_floor_t,
513 err: *mut semq_error_t,
514 ) -> semq_status_t;
515 pub fn semq_diff_within(
519 diff: *const semq_diff_t,
520 floor: *const semq_floor_t,
521 out: *mut c_int,
522 err: *mut semq_error_t,
523 ) -> semq_status_t;
524
525 pub fn semq_diff_evaluate(
531 diff: *const semq_diff_t,
532 floor: *const semq_floor_t,
533 checks: u32,
534 out: *mut *mut semq_verdict_t,
535 err: *mut semq_error_t,
536 ) -> semq_status_t;
537 pub fn semq_verdict_free(verdict: *mut semq_verdict_t);
538 pub fn semq_verdict_passed(verdict: *const semq_verdict_t) -> c_int;
539 pub fn semq_verdict_reasons(verdict: *const semq_verdict_t) -> u32;
541 pub fn semq_verdict_row_count(verdict: *const semq_verdict_t) -> u64;
542 pub fn semq_verdict_row(verdict: *const semq_verdict_t, i: u64) -> u64;
544
545 pub fn semq_core_version() -> *const c_char;
548 pub fn semq_build_id() -> *const c_char;
550 pub fn semq_backend_name(op: u32) -> *const c_char;
552 pub fn semq_status_name(status: u32) -> *const c_char;
554 pub fn semq_sha256(data: *const u8, len: u64, out: *mut u8);
556}
557
558#[cfg(test)]
559mod tests {
560 use super::*;
561 use std::ffi::CStr;
562 use std::mem::{align_of, offset_of, size_of};
563
564 #[test]
566 fn error_layout_matches_the_abi() {
567 assert_eq!(size_of::<semq_error_t>(), 152);
568 assert_eq!(align_of::<semq_error_t>(), 8);
569 assert_eq!(offset_of!(semq_error_t, status), 0);
570 assert_eq!(offset_of!(semq_error_t, which), 4);
571 assert_eq!(offset_of!(semq_error_t, row), 8);
572 assert_eq!(offset_of!(semq_error_t, field), 16);
573 assert_eq!(offset_of!(semq_error_t, message), 24);
574 }
575
576 #[test]
578 fn config_layout_matches_the_abi() {
579 assert_eq!(size_of::<semq_config_t>(), 16);
580 assert_eq!(align_of::<semq_config_t>(), 4);
581 assert_eq!(offset_of!(semq_config_t, op), 0);
582 assert_eq!(offset_of!(semq_config_t, dim), 4);
583 assert_eq!(offset_of!(semq_config_t, p1), 8);
584 assert_eq!(offset_of!(semq_config_t, p2), 12);
585 }
586
587 #[cfg(target_pointer_width = "64")]
589 #[test]
590 fn pointer_struct_layouts_match_the_abi() {
591 assert_eq!(size_of::<semq_pair_t>(), 32);
592 assert_eq!(offset_of!(semq_pair_t, key), 0);
593 assert_eq!(offset_of!(semq_pair_t, key_len), 8);
594 assert_eq!(offset_of!(semq_pair_t, value), 16);
595 assert_eq!(offset_of!(semq_pair_t, value_len), 24);
596
597 assert_eq!(size_of::<semq_ids_t>(), 40);
598 assert_eq!(offset_of!(semq_ids_t, kind), 0);
599 assert_eq!(offset_of!(semq_ids_t, n), 8);
600 assert_eq!(offset_of!(semq_ids_t, u64), 16);
601 assert_eq!(offset_of!(semq_ids_t, utf8_offsets), 24);
602 assert_eq!(offset_of!(semq_ids_t, utf8_bytes), 32);
603
604 assert_eq!(size_of::<semq_part_t>(), 16);
605 assert_eq!(offset_of!(semq_part_t, ptr), 0);
606 assert_eq!(offset_of!(semq_part_t, len), 8);
607
608 assert_eq!(size_of::<semq_manifest_change_t>(), 48);
609 assert_eq!(offset_of!(semq_manifest_change_t, key), 0);
610 assert_eq!(offset_of!(semq_manifest_change_t, key_len), 8);
611 assert_eq!(offset_of!(semq_manifest_change_t, has_before), 12);
612 assert_eq!(offset_of!(semq_manifest_change_t, before), 16);
613 assert_eq!(offset_of!(semq_manifest_change_t, before_len), 24);
614 assert_eq!(offset_of!(semq_manifest_change_t, has_after), 28);
615 assert_eq!(offset_of!(semq_manifest_change_t, after), 32);
616 assert_eq!(offset_of!(semq_manifest_change_t, after_len), 40);
617 }
618
619 #[test]
622 fn core_links_and_answers() {
623 let version = unsafe { CStr::from_ptr(semq_core_version()) };
624 assert!(!version.to_bytes().is_empty());
625
626 let mut cfg = semq_config_t {
627 op: 0,
628 dim: 0,
629 p1: 0,
630 p2: 0,
631 };
632 let status = unsafe { semq_config_quant(4, 4, &mut cfg, std::ptr::null_mut()) };
633 assert_eq!(status, SEMQ_OK);
634 assert_eq!(
635 cfg,
636 semq_config_t {
637 op: SEMQ_QUANT,
638 dim: 4,
639 p1: 4,
640 p2: SEMQ_RULE_REVISION
641 }
642 );
643 let mut bytes = [0u8; 13];
644 unsafe { semq_config_to_bytes(&cfg, bytes.as_mut_ptr()) };
645 assert_eq!(bytes, [2, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0]);
646
647 let name = unsafe { CStr::from_ptr(semq_status_name(SEMQ_ERR_INVALID_INPUT)) };
648 assert_eq!(name.to_str().unwrap(), "invalid_input");
649 }
650}