diff --git a/src/db.rs b/src/db.rs index 56f709c..215fdff 100644 --- a/src/db.rs +++ b/src/db.rs @@ -14,7 +14,7 @@ // -use {DB, Error, Options, WriteOptions}; +use {DB, Error, Options, WriteOptions, ColumnFamily}; use ffi; use ffi_util::opt_bytes_to_ptr; @@ -220,12 +220,12 @@ impl DBIterator { } fn new_cf(db: &DB, - cf_handle: *mut ffi::rocksdb_column_family_handle_t, + cf_handle: ColumnFamily, readopts: &ReadOptions, mode: IteratorMode) -> Result { unsafe { - let iterator = ffi::rocksdb_create_iterator_cf(db.inner, readopts.inner, cf_handle); + let iterator = ffi::rocksdb_create_iterator_cf(db.inner, readopts.inner, cf_handle.inner); let mut rv = DBIterator { inner: iterator, @@ -262,7 +262,7 @@ impl<'a> Snapshot<'a> { } pub fn iterator_cf(&self, - cf_handle: *mut ffi::rocksdb_column_family_handle_t, + cf_handle: ColumnFamily, mode: IteratorMode) -> Result { let mut readopts = ReadOptions::default(); @@ -277,7 +277,7 @@ impl<'a> Snapshot<'a> { } pub fn get_cf(&self, - cf: *mut ffi::rocksdb_column_family_handle_t, + cf: ColumnFamily, key: &[u8]) -> Result, Error> { let mut readopts = ReadOptions::default(); @@ -379,7 +379,7 @@ impl DB { } for (n, h) in cfs_v.iter().zip(cfhandles) { - cf_map.insert(n.to_string(), h); + cf_map.insert(n.to_string(), ColumnFamily { inner: h }); } } @@ -462,7 +462,7 @@ impl DB { } pub fn get_cf_opt(&self, - cf: *mut ffi::rocksdb_column_family_handle_t, + cf: ColumnFamily, key: &[u8], readopts: &ReadOptions) -> Result, Error> { @@ -479,7 +479,7 @@ impl DB { let mut val_len: size_t = 0; let val = ffi_try!(ffi::rocksdb_get_cf(self.inner, readopts.inner, - cf, + cf.inner, key.as_ptr() as *const c_char, key.len() as size_t, &mut val_len)) as *mut u8; @@ -492,7 +492,7 @@ impl DB { } pub fn get_cf(&self, - cf: *mut ffi::rocksdb_column_family_handle_t, + cf: ColumnFamily, key: &[u8]) -> Result, Error> { self.get_cf_opt(cf, key, &ReadOptions::default()) @@ -501,7 +501,7 @@ impl DB { pub fn create_cf(&mut self, name: &str, opts: &Options) - -> Result<*mut ffi::rocksdb_column_family_handle_t, Error> { + -> Result { let cname = match CString::new(name.as_bytes()) { Ok(c) => c, Err(_) => { @@ -513,6 +513,7 @@ impl DB { let cf_handler = unsafe { let cf_handler = ffi_try!(ffi::rocksdb_create_column_family(self.inner, opts.inner, cname.as_ptr())); + let cf_handler = ColumnFamily { inner: cf_handler }; self.cfs.insert(name.to_string(), cf_handler); cf_handler }; @@ -525,14 +526,14 @@ impl DB { return Err(Error::new(format!("Invalid column family: {}", name).to_owned())); } unsafe { - ffi_try!(ffi::rocksdb_drop_column_family(self.inner, *cf.unwrap())); + ffi_try!(ffi::rocksdb_drop_column_family(self.inner, cf.unwrap().inner)); } Ok(()) } /// Return the underlying column family handle. - pub fn cf_handle(&self, name: &str) -> Option<&*mut ffi::rocksdb_column_family_handle_t> { - self.cfs.get(name) + pub fn cf_handle(&self, name: &str) -> Option { + self.cfs.get(name).map(|cf| *cf) } pub fn iterator(&self, mode: IteratorMode) -> DBIterator { @@ -541,7 +542,7 @@ impl DB { } pub fn iterator_cf(&self, - cf_handle: *mut ffi::rocksdb_column_family_handle_t, + cf_handle: ColumnFamily, mode: IteratorMode) -> Result { let opts = ReadOptions::default(); @@ -565,7 +566,7 @@ impl DB { } pub fn put_cf_opt(&self, - cf: *mut ffi::rocksdb_column_family_handle_t, + cf: ColumnFamily, key: &[u8], value: &[u8], writeopts: &WriteOptions) @@ -573,7 +574,7 @@ impl DB { unsafe { ffi_try!(ffi::rocksdb_put_cf(self.inner, writeopts.inner, - cf, + cf.inner, key.as_ptr() as *const c_char, key.len() as size_t, value.as_ptr() as *const c_char, @@ -599,7 +600,7 @@ impl DB { } pub fn merge_cf_opt(&self, - cf: *mut ffi::rocksdb_column_family_handle_t, + cf: ColumnFamily, key: &[u8], value: &[u8], writeopts: &WriteOptions) @@ -607,7 +608,7 @@ impl DB { unsafe { ffi_try!(ffi::rocksdb_merge_cf(self.inner, writeopts.inner, - cf, + cf.inner, key.as_ptr() as *const i8, key.len() as size_t, value.as_ptr() as *const i8, @@ -627,14 +628,14 @@ impl DB { } pub fn delete_cf_opt(&self, - cf: *mut ffi::rocksdb_column_family_handle_t, + cf: ColumnFamily, key: &[u8], writeopts: &WriteOptions) -> Result<(), Error> { unsafe { ffi_try!(ffi::rocksdb_delete_cf(self.inner, writeopts.inner, - cf, + cf.inner, key.as_ptr() as *const c_char, key.len() as size_t)); Ok(()) @@ -646,7 +647,7 @@ impl DB { } pub fn put_cf(&self, - cf: *mut ffi::rocksdb_column_family_handle_t, + cf: ColumnFamily, key: &[u8], value: &[u8]) -> Result<(), Error> { @@ -658,7 +659,7 @@ impl DB { } pub fn merge_cf(&self, - cf: *mut ffi::rocksdb_column_family_handle_t, + cf: ColumnFamily, key: &[u8], value: &[u8]) -> Result<(), Error> { @@ -670,7 +671,7 @@ impl DB { } pub fn delete_cf(&self, - cf: *mut ffi::rocksdb_column_family_handle_t, + cf: ColumnFamily, key: &[u8]) -> Result<(), Error> { self.delete_cf_opt(cf, key, &WriteOptions::default()) @@ -687,12 +688,12 @@ impl DB { } pub fn compact_range_cf(&self, - cf: *mut ffi::rocksdb_column_family_handle_t, + cf: ColumnFamily, start: Option<&[u8]>, end: Option<&[u8]>) { unsafe { ffi::rocksdb_compact_range_cf(self.inner, - cf, + cf.inner, opt_bytes_to_ptr(start), start.map_or(0, |s| s.len()) as size_t, opt_bytes_to_ptr(end), @@ -723,13 +724,13 @@ impl WriteBatch { } pub fn put_cf(&mut self, - cf: *mut ffi::rocksdb_column_family_handle_t, + cf: ColumnFamily, key: &[u8], value: &[u8]) -> Result<(), Error> { unsafe { ffi::rocksdb_writebatch_put_cf(self.inner, - cf, + cf.inner, key.as_ptr() as *const i8, key.len() as size_t, value.as_ptr() as *const i8, @@ -750,13 +751,13 @@ impl WriteBatch { } pub fn merge_cf(&mut self, - cf: *mut ffi::rocksdb_column_family_handle_t, + cf: ColumnFamily, key: &[u8], value: &[u8]) -> Result<(), Error> { unsafe { ffi::rocksdb_writebatch_merge_cf(self.inner, - cf, + cf.inner, key.as_ptr() as *const i8, key.len() as size_t, value.as_ptr() as *const i8, @@ -778,12 +779,12 @@ impl WriteBatch { } pub fn delete_cf(&mut self, - cf: *mut ffi::rocksdb_column_family_handle_t, + cf: ColumnFamily, key: &[u8]) -> Result<(), Error> { unsafe { ffi::rocksdb_writebatch_delete_cf(self.inner, - cf, + cf.inner, key.as_ptr() as *const i8, key.len() as size_t); Ok(()) @@ -807,7 +808,7 @@ impl Drop for DB { fn drop(&mut self) { unsafe { for cf in self.cfs.values() { - ffi::rocksdb_column_family_handle_destroy(*cf); + ffi::rocksdb_column_family_handle_destroy(cf.inner); } ffi::rocksdb_close(self.inner); } @@ -859,6 +860,10 @@ impl Default for ReadOptions { } /// Vector of bytes stored in the database. +/// +/// This is a `C` allocated byte array and a length value. +/// Normal usage would be to utilize the fact it implements `Deref<[u8]>` and use it as +/// a slice. pub struct DBVector { base: *mut u8, len: usize, @@ -881,18 +886,46 @@ impl Drop for DBVector { } impl DBVector { - pub fn from_c(val: *mut u8, val_len: size_t) -> DBVector { + /// Used internally to create a DBVector from a `C` memory block + /// + /// # Unsafe + /// Requires that the ponter be allocated by a `malloc` derivative (all C libraries), and + /// `val_len` be the length of the C array to be safe (since `sizeof(u8) = 1`). + /// + /// # Example + /// + /// ```ignore + /// let buf_len: libc::size_t = unsafe { mem::uninitialized() }; + /// // Assume the function fills buf_len with the length of the returned array + /// let buf: *mut u8 = unsafe { ffi_function_returning_byte_array(&buf_len) }; + /// DBVector::from_c(buf, buf_len) + /// ``` + pub unsafe fn from_c(val: *mut u8, val_len: size_t) -> DBVector { DBVector { base: val, len: val_len as usize, } } + /// Convenience function to attempt to reinterperet value as string. + /// + /// implemented as `str::from_utf8(&self[..])` pub fn to_utf8(&self) -> Option<&str> { str::from_utf8(self.deref()).ok() } } +#[test] +fn test_db_vector() { + use std::mem; + let len: size_t = 4; + let data: *mut u8 = unsafe { mem::transmute(libc::calloc(len, mem::size_of::())) }; + let v = unsafe { DBVector::from_c(data, len) }; + let ctrl = [0u8, 0, 0, 0]; + assert_eq!(&*v, &ctrl[..]); +} + + #[test] fn external() { let path = "_rust_rocksdb_externaltest"; diff --git a/src/lib.rs b/src/lib.rs index 102f95d..3f0f5f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,12 +55,16 @@ use std::fmt; use std::path::PathBuf; /// A RocksDB database. +/// +/// See crate level documentation for a simple usage example. pub struct DB { inner: *mut ffi::rocksdb_t, - cfs: BTreeMap, + cfs: BTreeMap, path: PathBuf, } +/// A simple wrapper round a string, used for errors reported from +/// ffi calls. #[derive(Debug, PartialEq)] pub struct Error { message: String, @@ -162,3 +166,10 @@ pub struct Options { pub struct WriteOptions { inner: *mut ffi::rocksdb_writeoptions_t, } + +/// An opaque type used to represent a column family. Returned from some functions, and used +/// in others +#[derive(Copy, Clone)] +pub struct ColumnFamily { + inner: *mut ffi::rocksdb_column_family_handle_t, +} diff --git a/test/test_column_family.rs b/test/test_column_family.rs index a85ea20..03b11fc 100644 --- a/test/test_column_family.rs +++ b/test/test_column_family.rs @@ -94,7 +94,7 @@ fn test_merge_operator() { } Err(e) => panic!("failed to open db with column family: {}", e), }; - let cf1 = *db.cf_handle("cf1").unwrap(); + let cf1 = db.cf_handle("cf1").unwrap(); assert!(db.put_cf(cf1, b"k1", b"v1").is_ok()); assert!(db.get_cf(cf1, b"k1").unwrap().unwrap().to_utf8().unwrap() == "v1"); let p = db.put_cf(cf1, b"k1", b"a");