1#![allow(non_camel_case_types)]
2#![allow(clippy::missing_safety_doc)]
3use std::{
4 ffi::CStr,
5 fs::File,
6 io::BufWriter,
7 os::raw::{c_char, c_int, c_uint, c_void},
8 ptr,
9};
10
11use cqdb::{CQDBWriter, Flag, CQDB};
12use libc::FILE;
13
14#[macro_use]
15mod macros;
16
17pub const CQDB_NONE: c_uint = 0;
19pub const CQDB_ONEWAY: c_uint = 1;
21
22pub const CQDB_SUCCESS: c_int = 0;
24pub const CQDB_ERROR_INVALIDID: c_int = -1018;
26pub const CQDB_ERROR_FILEWRITE: c_int = -1021;
28pub const CQDB_ERROR_NOTFOUND: c_int = -1023;
30
31#[derive(Debug, Clone, Copy)]
33#[repr(C)]
34pub struct tag_cqdb {
35 _unused: [u8; 0],
36}
37
38pub type cqdb_t = tag_cqdb;
39
40#[derive(Debug, Clone, Copy)]
41#[repr(C)]
42struct tag_cqdb_writer_inner {
43 _unused: [u8; 0],
44}
45
46#[derive(Debug, Clone, Copy)]
48#[repr(C)]
49pub struct tag_cqdb_writer {
50 file: *mut FILE,
51 inner: *mut tag_cqdb_writer_inner,
52}
53
54pub type cqdb_writer_t = tag_cqdb_writer;
55
56ffi_fn! {
57 fn cqdb_delete(db: *mut cqdb_t) {
59 if !db.is_null() {
60 unsafe { Box::from_raw(db as *mut CQDB) };
61 }
62 }
63}
64
65ffi_fn! {
66 fn cqdb_reader(buffer: *const c_void, size: usize) -> *mut cqdb_t {
68 let buf = unsafe { std::slice::from_raw_parts(buffer as *const u8, size) };
69 if let Ok(db) = CQDB::new(buf) {
70 Box::into_raw(Box::new(db)) as *mut cqdb_t
71 } else {
72 ptr::null_mut()
73 }
74 }
75}
76
77ffi_fn! {
78 fn cqdb_num(db: *mut cqdb_t) -> c_int {
80 let db = db as *mut CQDB;
81 unsafe {
82 (*db).num() as c_int
83 }
84 }
85}
86
87ffi_fn! {
88 fn cqdb_to_id(db: *mut cqdb_t, s: *const c_char) -> c_int {
92 let db = db as *mut CQDB;
93 unsafe {
94 let c_str = CStr::from_ptr(s).to_str().unwrap();
95 (*db).to_id(c_str).map(|id| id as c_int).unwrap_or(CQDB_ERROR_NOTFOUND)
96 }
97 }
98}
99
100ffi_fn! {
101 fn cqdb_to_string(db: *mut cqdb_t, id: c_int) -> *const c_char {
105 let db = db as *mut CQDB;
106 if let Some(s) = unsafe { (*db).to_str(id as u32) } {
107 s.as_ptr() as *const c_char
110 } else {
111 ptr::null_mut()
112 }
113 }
114}
115
116ffi_fn! {
117 fn cqdb_writer(fp: *mut FILE, flag: c_int) -> *mut cqdb_writer_t {
125 unsafe {
126 let file = new_file_from_libc(fp);
127 let buf_writer = BufWriter::new(file);
128 let flag = if flag as c_uint == CQDB_ONEWAY {
129 Flag::ONEWAY
130 } else {
131 Flag::NONE
132 };
133 let writer = match CQDBWriter::with_flag(buf_writer, flag) {
134 Ok(writer) => {
135 let inner = Box::into_raw(Box::new(writer)) as *mut tag_cqdb_writer_inner;
136 Box::into_raw(Box::new(cqdb_writer_t { file: fp, inner }))
137 }
138 Err(_) => ptr::null_mut(),
139 };
140 writer
141 }
142 }
143}
144
145#[cfg(unix)]
146unsafe fn new_file_from_libc(fp: *mut FILE) -> File {
147 use std::os::unix::io::FromRawFd;
148
149 libc::fflush(fp);
152 let fd = libc::dup(libc::fileno(fp));
154 File::from_raw_fd(fd)
155}
156
157#[cfg(windows)]
158unsafe fn new_file_from_libc(fp: *mut FILE) -> File {
159 use std::os::windows::io::{FromRawHandle, RawHandle};
160
161 libc::fflush(fp);
164 let fd = libc::dup(libc::fileno(fp));
165 let handle = libc::get_osfhandle(fd) as RawHandle;
166 File::from_raw_handle(handle)
168}
169
170ffi_fn! {
171 fn cqdb_writer_close(dbw: *mut cqdb_writer_t) -> c_int {
179 if !dbw.is_null() {
180 unsafe {
181 let inner = (*dbw).inner as *mut CQDBWriter<BufWriter<File>>;
182 Box::from_raw(inner);
184 let offset = libc::lseek(libc::fileno((*dbw).file), 0, libc::SEEK_CUR);
187 libc::fseek((*dbw).file, offset, libc::SEEK_SET);
188 Box::from_raw(dbw);
189 }
190 }
191 CQDB_SUCCESS
192 }
193}
194
195ffi_fn! {
196 fn cqdb_writer_put(
202 dbw: *mut cqdb_writer_t,
203 s: *const c_char,
204 id: c_int
205 ) -> c_int {
206 if id < 0 {
207 return CQDB_ERROR_INVALIDID;
208 }
209 unsafe {
210 let dbw = (*dbw).inner as *mut CQDBWriter<BufWriter<File>>;
211 let c_str = CStr::from_ptr(s).to_str().unwrap();
212 if let Err(_) = (*dbw).put(c_str, id as u32) {
213 return CQDB_ERROR_FILEWRITE;
214 }
215 }
216 CQDB_SUCCESS
217 }
218}
219
220#[cfg(test)]
221mod test {
222 use super::*;
223 use std::{ffi::CString, fs};
224
225 #[test]
226 fn test_cqdb_read_cqdb_ffi() {
227 let name = CString::new("../tests/output/cqdb-ffi-1.cqdb").unwrap();
228 let mode = CString::new("wb").unwrap();
229 unsafe {
230 let fp = libc::fopen(name.as_ptr(), mode.as_ptr());
231 assert!(!fp.is_null());
232 let writer = cqdb_writer(fp, 0);
233 assert!(!writer.is_null());
234 for i in 0..100 {
235 let s = CString::new(format!("{:08}", i)).unwrap();
236 assert_eq!(0, cqdb_writer_put(writer, s.as_ptr(), i));
237 }
238 let s = CString::new(format!("{:08}", 10)).unwrap();
239 assert_eq!(
240 CQDB_ERROR_INVALIDID,
241 cqdb_writer_put(writer, s.as_ptr(), -1)
242 );
243 assert_eq!(0, cqdb_writer_close(writer));
244 libc::fclose(fp);
245 }
246 let buf = std::fs::read("../tests/output/cqdb-ffi-1.cqdb").unwrap();
247 let db = CQDB::new(&buf).unwrap();
248 assert_eq!(100, db.num());
249 }
250
251 #[test]
252 fn test_cqdb_ffi_read_cqdb_writer() {
253 let file = fs::File::create("../tests/output/cqdb-ffi-2.cqdb").unwrap();
254 let mut writer = CQDBWriter::new(file).unwrap();
255 for id in 0..100 {
256 let key = format!("{:08}", id);
257 writer.put(&key, id).unwrap();
258 }
259 drop(writer);
260
261 let buf = fs::read("../tests/output/cqdb-ffi-2.cqdb").unwrap();
262 unsafe {
263 let db = cqdb_reader(buf.as_ptr() as _, buf.len());
264 assert!(!db.is_null());
265 let size = cqdb_num(db);
266 for id in 0..size {
268 let key = CString::new(format!("{:08}", id)).unwrap();
269 let j = cqdb_to_id(db, key.as_ptr());
270 assert_eq!(id, j);
271 }
272 let key = CString::new("non-exists-key").unwrap();
273 let j = cqdb_to_id(db, key.as_ptr());
274 assert!(j < 0);
275
276 for id in 0..size {
278 let ptr = cqdb_to_string(db, id);
279 assert!(!ptr.is_null());
280 let key = CStr::from_ptr(ptr).to_str().unwrap();
281 assert_eq!(key, format!("{:08}", id));
282 }
283 let ptr = cqdb_to_string(db, size + 100);
284 assert!(ptr.is_null());
285
286 cqdb_delete(db);
287 }
288 }
289}