From e1142f4f2f256bc0abfdb3b4cb9b60f399f06708 Mon Sep 17 00:00:00 2001 From: Tyler Neely Date: Fri, 28 Nov 2014 03:29:32 -0500 Subject: [PATCH] some cleanups and interface smoothing --- README.md | 34 +++++--- rocksdb-sys/lib.rs | 8 +- src/lib.rs | 1 + src/main.rs | 22 ++++- src/rocksdb.rs | 201 ++++++++++++++++++++++----------------------- 5 files changed, 143 insertions(+), 123 deletions(-) diff --git a/README.md b/README.md index fd8bd25..2d2d9a7 100644 --- a/README.md +++ b/README.md @@ -4,22 +4,32 @@ rust-rocksdb ### running - Cargo.toml ```rust -[dependencies.rocksdb] +[dependencies.rocksdb] git = "https://github.com/spacejam/rust-rocksdb" ``` - Code ```rust -extern crate rocksdb; - -fn main() { - let db = rocksdb::open("/path/to/db".to_string(), true).unwrap(); - assert!(db.put(b"hey", b"v1111").is_ok()); - db.get(b"hey").map( |raw| { - std::str::from_utf8(raw.as_slice()).map( |v| { - println!("value: {}", v); - }) - }); - db.close() +extern crate rocksdb; + +fn main() { + match rocksdb::create_or_open("/path/for/rocksdb/storage".to_string()) { + Ok(db) => { + db.put(b"my key", b"my value"); + + db.get(b"my key").map( |value| { + match value.to_utf8() { + Some(v) => + println!("retrieved utf8 value {}", v), + None => + println!("did not read valid utf-8 out of the db"), + }}); + + db.get(b"NOT my key").on_absent(|| { println!("value not found") }); + + db.close(); + }, + Err(e) => panic!(e), + } } ``` diff --git a/rocksdb-sys/lib.rs b/rocksdb-sys/lib.rs index 9dbbdb5..2c4d0e3 100644 --- a/rocksdb-sys/lib.rs +++ b/rocksdb-sys/lib.rs @@ -105,7 +105,7 @@ extern { pub fn rocksdb_readoptions_create() -> RocksdbReadOptions; pub fn rocksdb_get(db: RocksdbInstance, readopts: RocksdbReadOptions, k: *const u8, kLen: size_t, - valLen: *const size_t, err: *mut i8) -> *mut u8; + valLen: *const size_t, err: *mut i8) -> *mut c_void; pub fn rocksdb_close(db: RocksdbInstance); pub fn rocksdb_destroy_db( options: RocksdbOptions, path: *const i8, err: *mut i8); @@ -122,7 +122,7 @@ fn internal() { let opts = rocksdb_options_create(); let RocksdbOptions(opt_ptr) = opts; assert!(opt_ptr.is_not_null()); - + rocksdb_options_increase_parallelism(opts, 0); rocksdb_options_optimize_level_style_compaction(opts, 0); rocksdb_options_set_create_if_missing(opts, 1); @@ -130,9 +130,9 @@ fn internal() { let rustpath = "internaltest"; let cpath = rustpath.to_c_str(); let cpath_ptr = cpath.as_ptr(); - + let err = 0 as *mut i8; - let db = rocksdb_open(opts, cpath_ptr, err); + let db = rocksdb_open(opts, cpath_ptr, err); assert!(err.is_null()); libc::free(err as *mut c_void); diff --git a/src/lib.rs b/src/lib.rs index ab01d18..4bb7c1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ extern crate "rocksdb-sys" as rocksdb_ffi; pub use rocksdb::{ + create_or_open, open, Rocksdb, RocksdbResult, diff --git a/src/main.rs b/src/main.rs index ea464d9..f2a2512 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,10 +5,24 @@ use test::Bencher; #[allow(dead_code)] fn main() { - let db = open("testdb".to_string(), true).unwrap(); - assert!(db.put(b"hey", b"v1111").is_ok()); - db.get(b"hey").map(|raw| { std::str::from_utf8(raw.as_slice()).map(|v| {println!("value: {}", v); })}); - db.close(); + match rocksdb::create_or_open("/tmp/rust-rocksdb".to_string()) { + Ok(db) => { + db.put(b"my key", b"my value"); + + db.get(b"my key").map( |value| { + match value.to_utf8() { + Some(v) => + println!("retrieved utf8 value {}", v), + None => + println!("did not read valid utf-8 out of the db"), + }}); + + db.get(b"NOT my key").on_absent(|| { println!("value not found") }); + + db.close(); + }, + Err(e) => panic!(e), + } } #[allow(dead_code)] diff --git a/src/rocksdb.rs b/src/rocksdb.rs index d5996a1..3d830cc 100644 --- a/src/rocksdb.rs +++ b/src/rocksdb.rs @@ -3,13 +3,85 @@ use self::libc::{c_void, size_t}; use std::io::{IoError}; use std::c_vec::CVec; use std::c_str::CString; +use std::str::from_utf8; use rocksdb_ffi; -// TODO learn more about lifetimes and determine if it's appropriate to keep -// inner on the stack, instead. +pub struct Rocksdb { + inner: rocksdb_ffi::RocksdbInstance, +} + +impl Rocksdb { + pub fn put(&self, key: &[u8], value: &[u8]) -> Result<(), String> { + unsafe { + let writeopts = rocksdb_ffi::rocksdb_writeoptions_create(); + let err = 0 as *mut i8; + rocksdb_ffi::rocksdb_put(self.inner, writeopts, key.as_ptr(), + key.len() as size_t, value.as_ptr(), + value.len() as size_t, err); + if err.is_not_null() { + let cs = CString::new(err as *const i8, true); + match cs.as_str() { + Some(error_string) => + return Err(error_string.to_string()), + None => { + let ie = IoError::last_error(); + return Err(format!( + "ERROR: desc:{}, details:{}", + ie.desc, + ie.detail.unwrap_or_else( + || {"none provided by OS".to_string()}))) + } + } + } + return Ok(()) + } + } + + pub fn get<'a>(&self, key: &[u8]) -> RocksdbResult<'a, RocksdbVector, String> { + unsafe { + let readopts = rocksdb_ffi::rocksdb_readoptions_create(); + let rocksdb_ffi::RocksdbReadOptions(read_opts_ptr) = readopts; + if read_opts_ptr.is_null() { + return RocksdbResult::Error("Unable to create rocksdb read \ + options. This is a fairly trivial call, and its failure \ + may be indicative of a mis-compiled or mis-loaded rocksdb \ + library.".to_string()); + } + + let val_len: size_t = 0; + let val_len_ptr = &val_len as *const size_t; + let err = 0 as *mut i8; + let val = rocksdb_ffi::rocksdb_get(self.inner, readopts, key.as_ptr(), + key.len() as size_t, val_len_ptr, err) as *mut u8; + if err.is_not_null() { + let cs = CString::new(err as *const i8, true); + match cs.as_str() { + Some(error_string) => + return RocksdbResult::Error(error_string.to_string()), + None => + return RocksdbResult::Error("Unable to get value from \ + rocksdb. (non-utf8 error received from underlying \ + library)".to_string()), + } + } + match val.is_null() { + true => RocksdbResult::None, + false => { + RocksdbResult::Some(RocksdbVector::from_c(val, val_len)) + } + } + } + } + + pub fn close(&self) { + unsafe { rocksdb_ffi::rocksdb_close(self.inner); } + } + +} + pub struct RocksdbVector { - inner: Box>, + inner: CVec, } impl RocksdbVector { @@ -17,50 +89,31 @@ impl RocksdbVector { unsafe { RocksdbVector { inner: - box CVec::new_with_dtor(val, val_len as uint, - proc(){ - libc::free(val as *mut c_void); - }) + CVec::new_with_dtor(val, val_len as uint, + proc(){ libc::free(val as *mut c_void); }) } } } - pub fn as_slice(&self) -> &[u8] { + pub fn as_slice<'a>(&'a self) -> &'a [u8] { self.inner.as_slice() } + + pub fn to_utf8<'a>(&'a self) -> Option<&'a str> { + from_utf8(self.inner.as_slice()) + } } // RocksdbResult exists because of the inherent difference between // an operational failure and the absence of a possible result. #[deriving(Clone, PartialEq, PartialOrd, Eq, Ord, Show)] -pub enum RocksdbResult { +pub enum RocksdbResult<'a,T,E> { Some(T), None, Error(E), } -/* -impl RocksdbResult>, E> { - pub fn from_c(val: *mut u8, val_len: size_t) -> RocksdbResult>, E> { - unsafe { - RocksdbResult::Some( - box CVec::new_with_dtor(val, val_len as uint, - proc(){ - libc::free(val as *mut c_void); - })) - } - } - - pub fn as_slice<'a>(self) -> Option<&'a [u8]> { - match self { - RocksdbResult::Some(x) => Some(x.as_slice()), - RocksdbResult::None => None, - RocksdbResult::Error(e) => None, - } - } -} -*/ -impl RocksdbResult { +impl <'a,T,E> RocksdbResult<'a,T,E> { #[unstable = "waiting for unboxed closures"] pub fn map(self, f: |T| -> U) -> RocksdbResult { match self { @@ -70,6 +123,14 @@ impl RocksdbResult { } } + pub fn unwrap(self) -> T { + match self { + RocksdbResult::Some(x) => x, + RocksdbResult::None => panic!("Attempted unwrap on RocksdbResult::None"), + RocksdbResult::Error(_) => panic!("Attempted unwrap on RocksdbResult::Error"), + } + } + #[unstable = "waiting for unboxed closures"] pub fn on_error(self, f: |E| -> U) -> RocksdbResult { match self { @@ -80,7 +141,7 @@ impl RocksdbResult { } #[unstable = "waiting for unboxed closures"] - pub fn on_absent(self, f: || -> ()) -> RocksdbResult { + pub fn on_absent(self, f: || -> ()) -> RocksdbResult { match self { RocksdbResult::Some(x) => RocksdbResult::Some(x), RocksdbResult::None => { @@ -114,77 +175,8 @@ impl RocksdbResult { } } -pub struct Rocksdb { - inner: rocksdb_ffi::RocksdbInstance, -} - -impl Rocksdb { - pub fn put(&self, key: &[u8], value: &[u8]) -> Result { - unsafe { - let writeopts = rocksdb_ffi::rocksdb_writeoptions_create(); - let err = 0 as *mut i8; - rocksdb_ffi::rocksdb_put(self.inner, writeopts, key.as_ptr(), - key.len() as size_t, value.as_ptr(), - value.len() as size_t, err); - if err.is_not_null() { - let cs = CString::new(err as *const i8, true); - match cs.as_str() { - Some(error_string) => - return Err(error_string.to_string()), - None => { - let ie = IoError::last_error(); - return Err(format!( - "ERROR: desc:{}, details:{}", - ie.desc, - ie.detail.unwrap_or_else( - || {"none provided by OS".to_string()}))) - } - } - } - return Ok(true) - } - } - - pub fn get(&self, key: &[u8]) -> RocksdbResult { - unsafe { - let readopts = rocksdb_ffi::rocksdb_readoptions_create(); - let rocksdb_ffi::RocksdbReadOptions(read_opts_ptr) = readopts; - if read_opts_ptr.is_null() { - return RocksdbResult::Error("Unable to create rocksdb read \ - options. This is a fairly trivial call, and its failure \ - may be indicative of a mis-compiled or mis-loaded rocksdb \ - library.".to_string()); - } - - let val_len: size_t = 0; - let val_len_ptr = &val_len as *const size_t; - let err = 0 as *mut i8; - let val = rocksdb_ffi::rocksdb_get(self.inner, readopts, key.as_ptr(), - key.len() as size_t, val_len_ptr, err); - if err.is_not_null() { - let cs = CString::new(err as *const i8, true); - match cs.as_str() { - Some(error_string) => - return RocksdbResult::Error(error_string.to_string()), - None => - return RocksdbResult::Error("Unable to get value from \ - rocksdb. (non-utf8 error received from underlying \ - library)".to_string()), - } - } - match val.is_null() { - true => RocksdbResult::None, - false => { - RocksdbResult::Some(RocksdbVector::from_c(val, val_len)) - } - } - } - } - - pub fn close(&self) { - unsafe { rocksdb_ffi::rocksdb_close(self.inner); } - } - +pub fn create_or_open(path: String) -> Result { + open(path, true) } pub fn open(path: String, create_if_missing: bool) -> Result { @@ -206,6 +198,9 @@ pub fn open(path: String, create_if_missing: bool) -> Result { let cpath = path.to_c_str(); let cpath_ptr = cpath.as_ptr(); + //TODO test path here, as if rocksdb fails it will just crash the + // process currently + let err = 0 as *mut i8; let db = rocksdb_ffi::rocksdb_open(opts, cpath_ptr, err); let rocksdb_ffi::RocksdbInstance(db_ptr) = db;