diff --git a/Cargo.toml b/Cargo.toml index cffbc94..3a715a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,3 +3,7 @@ name = "rocksdb" version = "0.0.1" authors = ["Tyler Neely "] + +[dependencies.rocksdb-sys] +path = "rocksdb-sys" +version = "*" diff --git a/rocksdb-sys/Cargo.toml b/rocksdb-sys/Cargo.toml new file mode 100644 index 0000000..c3a6af2 --- /dev/null +++ b/rocksdb-sys/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rocksdb-sys" +version = "0.0.1" +authors = [] +links = "rocksdb" +build = "build.rs" + +[build-dependencies.pkg-config] +git = "https://github.com/alexcrichton/pkg-config-rs" + +[lib] +name = "rocksdb-sys" +path = "lib.rs" diff --git a/rocksdb-sys/build.rs b/rocksdb-sys/build.rs new file mode 100644 index 0000000..35e106e --- /dev/null +++ b/rocksdb-sys/build.rs @@ -0,0 +1,55 @@ +extern crate "pkg-config" as pkg_config; + +use std::os; +use std::io::{mod, fs, Command}; +use std::io::process::InheritFd; + +//TODO windows + osx support + +fn main() { + // Next, fall back and try to use pkg-config if its available. + match pkg_config::find_library("librocksdb") { + Ok(()) => return, + Err(..) => {} + } + + let src = os::getcwd().unwrap(); + let dst = Path::new(os::getenv("OUT_DIR").unwrap()); + + let _ = fs::mkdir(&dst.join("build"), io::USER_DIR); + + let mut config_opts = Vec::new(); + config_opts.push("--enable-static=yes".to_string()); + config_opts.push(format!("--prefix={}", dst.display())); + config_opts.push("--disable-manual".to_string()); + + println!("cwd: {}", src.as_str()); + run(Command::new(make()) + .arg("shared_lib") + .arg(format!("-j{}", os::getenv("NUM_JOBS").unwrap())) + .cwd(&src.join("rocksdb"))); + + // Don't run `make install` because apparently it's a little buggy on mingw + // for windows. + fs::mkdir_recursive(&dst.join("lib/pkgconfig"), io::USER_DIR).unwrap(); + + fs::rename(&src.join("rocksdb/librocksdb.so"), &dst.join("lib/librocksdb.so")).unwrap(); + + println!("cargo:rustc-flags=-L {}/lib -l rocksdb:dylib", dst.display()); + println!("cargo:root={}", dst.display()); + println!("cargo:include={}/include", src.join("rocksdb").display()); +} + +fn run(cmd: &mut Command) { + println!("running: {}", cmd); + assert!(cmd.stdout(InheritFd(1)) + .stderr(InheritFd(2)) + .status() + .unwrap() + .success()); + +} + +fn make() -> &'static str { + if cfg!(target_os = "freebsd") {"gmake"} else {"make"} +} diff --git a/src/ffi.rs b/rocksdb-sys/lib.rs similarity index 94% rename from src/ffi.rs rename to rocksdb-sys/lib.rs index 23f356e..9dbbdb5 100644 --- a/src/ffi.rs +++ b/rocksdb-sys/lib.rs @@ -1,8 +1,5 @@ extern crate libc; use self::libc::{c_int, c_void, size_t}; -use std::io::{IoResult, IoError}; -use std::c_vec::CVec; -use std::c_str::CString; #[repr(C)] pub struct RocksdbOptions(pub *const c_void); @@ -19,9 +16,8 @@ pub struct RocksdbMergeOperator(pub *const c_void); #[repr(C)] pub struct RocksdbFilterPolicy(pub *const c_void); -#[allow(dead_code)] #[repr(C)] -enum RocksdbCompressionType { +pub enum RocksdbCompressionType { RocksdbNoCompression = 0, RocksdbSnappyCompression = 1, RocksdbZlibCompression = 2, @@ -30,22 +26,19 @@ enum RocksdbCompressionType { RocksdbLz4hcCompression = 5 } -#[allow(dead_code)] #[repr(C)] -enum RocksdbCompactionStyle { +pub enum RocksdbCompactionStyle { RocksdbLevelCompaction = 0, RocksdbUniversalCompaction = 1, RocksdbFifoCompaction = 2 } -#[allow(dead_code)] #[repr(C)] -enum RocksdbUniversalCompactionStyle { +pub enum RocksdbUniversalCompactionStyle { rocksdb_similar_size_compaction_stop_style = 0, rocksdb_total_size_compaction_stop_style = 1 } -#[allow(dead_code)] #[link(name = "rocksdb")] extern { pub fn rocksdb_options_create() -> RocksdbOptions; @@ -114,8 +107,15 @@ extern { k: *const u8, kLen: size_t, valLen: *const size_t, err: *mut i8) -> *mut u8; pub fn rocksdb_close(db: RocksdbInstance); + pub fn rocksdb_destroy_db( + options: RocksdbOptions, path: *const i8, err: *mut i8); + pub fn rocksdb_repair_db( + options: RocksdbOptions, path: *const i8, err: *mut i8); + + } +#[allow(dead_code)] #[test] fn internal() { unsafe { @@ -127,11 +127,10 @@ fn internal() { rocksdb_options_optimize_level_style_compaction(opts, 0); rocksdb_options_set_create_if_missing(opts, 1); - let rustpath = "datadir"; + let rustpath = "internaltest"; let cpath = rustpath.to_c_str(); let cpath_ptr = cpath.as_ptr(); - //TODO this will SIGSEGV let err = 0 as *mut i8; let db = rocksdb_open(opts, cpath_ptr, err); assert!(err.is_null()); @@ -153,7 +152,7 @@ fn internal() { assert!(read_opts_ptr.is_not_null()); libc::free(err as *mut c_void); - let mut val_len: size_t = 0; + let val_len: size_t = 0; let val_len_ptr = &val_len as *const size_t; rocksdb_get(db, readopts, key.as_ptr(), 4, val_len_ptr, err); assert!(err.is_null()); diff --git a/src/lib.rs b/src/lib.rs index 78f75aa..ab01d18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ #![crate_id = "rocksdb"] #![crate_type = "lib"] -#![feature(globs)] +#![allow(dead_code)] + +extern crate "rocksdb-sys" as rocksdb_ffi; pub use rocksdb::{ open, @@ -8,4 +10,3 @@ pub use rocksdb::{ RocksdbResult, }; pub mod rocksdb; -mod ffi; diff --git a/src/main.rs b/src/main.rs index ea68da7..ea464d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,13 +3,15 @@ extern crate test; use rocksdb::open; use test::Bencher; +#[allow(dead_code)] fn main() { let db = open("testdb".to_string(), true).unwrap(); - db.put(b"hey", b"v1111"); - db.get(b"hey").map(|v| { println!("value: {}", v.as_slice()); }); + 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(); } +#[allow(dead_code)] #[bench] fn writes(b: &mut Bencher) { let db = open("testdb".to_string(), true).unwrap(); @@ -21,7 +23,8 @@ fn writes(b: &mut Bencher) { db.close(); } -#[bbench] +#[allow(dead_code)] +#[bench] fn reads(b: &mut Bencher) { let db = open("testdb".to_string(), true).unwrap(); let mut i = 0 as u64; diff --git a/src/rocksdb.rs b/src/rocksdb.rs index ca5bf64..d5996a1 100644 --- a/src/rocksdb.rs +++ b/src/rocksdb.rs @@ -1,10 +1,10 @@ extern crate libc; -use self::libc::{c_int, c_void, size_t}; -use std::io::{IoResult, IoError}; +use self::libc::{c_void, size_t}; +use std::io::{IoError}; use std::c_vec::CVec; use std::c_str::CString; -use ffi; +use rocksdb_ffi; // TODO learn more about lifetimes and determine if it's appropriate to keep // inner on the stack, instead. @@ -15,12 +15,13 @@ pub struct RocksdbVector { impl RocksdbVector { pub fn from_c(val: *mut u8, val_len: size_t) -> RocksdbVector { unsafe { - RocksdbVector{ + RocksdbVector { inner: box CVec::new_with_dtor(val, val_len as uint, proc(){ libc::free(val as *mut c_void); - })} + }) + } } } @@ -29,7 +30,8 @@ impl RocksdbVector { } } - +// 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 { Some(T), @@ -37,6 +39,27 @@ pub enum RocksdbResult { 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 { #[unstable = "waiting for unboxed closures"] pub fn map(self, f: |T| -> U) -> RocksdbResult { @@ -70,38 +93,37 @@ impl RocksdbResult { pub fn is_some(self) -> bool { match self { - RocksdbResult::Some(T) => true, + RocksdbResult::Some(_) => true, RocksdbResult::None => false, - RocksdbResult::Error(E) => false, + RocksdbResult::Error(_) => false, } } pub fn is_none(self) -> bool { match self { - RocksdbResult::Some(T) => false, + RocksdbResult::Some(_) => false, RocksdbResult::None => true, - RocksdbResult::Error(E) => false, + RocksdbResult::Error(_) => false, } } pub fn is_error(self) -> bool { match self { - RocksdbResult::Some(T) => false, + RocksdbResult::Some(_) => false, RocksdbResult::None => false, - RocksdbResult::Error(E) => true, + RocksdbResult::Error(_) => true, } } } pub struct Rocksdb { - inner: ffi::RocksdbInstance, - path: String, + inner: rocksdb_ffi::RocksdbInstance, } impl Rocksdb { pub fn put(&self, key: &[u8], value: &[u8]) -> Result { - unsafe { - let writeopts = ffi::rocksdb_writeoptions_create(); + unsafe { + let writeopts = rocksdb_ffi::rocksdb_writeoptions_create(); let err = 0 as *mut i8; - ffi::rocksdb_put(self.inner, writeopts, key.as_ptr(), + 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() { @@ -112,7 +134,7 @@ impl Rocksdb { None => { let ie = IoError::last_error(); return Err(format!( - "ERROR: desc:{}, details:{}", + "ERROR: desc:{}, details:{}", ie.desc, ie.detail.unwrap_or_else( || {"none provided by OS".to_string()}))) @@ -125,8 +147,8 @@ impl Rocksdb { pub fn get(&self, key: &[u8]) -> RocksdbResult { unsafe { - let readopts = ffi::rocksdb_readoptions_create(); - let ffi::RocksdbReadOptions(read_opts_ptr) = readopts; + 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 \ @@ -137,7 +159,7 @@ impl Rocksdb { let val_len: size_t = 0; let val_len_ptr = &val_len as *const size_t; let err = 0 as *mut i8; - let val = ffi::rocksdb_get(self.inner, readopts, key.as_ptr(), + 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); @@ -160,33 +182,33 @@ impl Rocksdb { } pub fn close(&self) { - unsafe { ffi::rocksdb_close(self.inner); } + unsafe { rocksdb_ffi::rocksdb_close(self.inner); } } } pub fn open(path: String, create_if_missing: bool) -> Result { unsafe { - let opts = ffi::rocksdb_options_create(); - let ffi::RocksdbOptions(opt_ptr) = opts; + let opts = rocksdb_ffi::rocksdb_options_create(); + let rocksdb_ffi::RocksdbOptions(opt_ptr) = opts; if opt_ptr.is_null() { return Err("Could not create options".to_string()); } - - ffi::rocksdb_options_increase_parallelism(opts, 2); - //ffi::rocksdb_options_optimize_level_style_compaction(opts, 0); + + //rocksdb_ffi::rocksdb_options_increase_parallelism(opts, 2); + //rocksdb_ffi::rocksdb_options_optimize_level_style_compaction(opts, 0); match create_if_missing { - true => ffi::rocksdb_options_set_create_if_missing(opts, 1), - false => ffi::rocksdb_options_set_create_if_missing(opts, 0), + true => rocksdb_ffi::rocksdb_options_set_create_if_missing(opts, 1), + false => rocksdb_ffi::rocksdb_options_set_create_if_missing(opts, 0), } let cpath = path.to_c_str(); let cpath_ptr = cpath.as_ptr(); - + let err = 0 as *mut i8; - let db = ffi::rocksdb_open(opts, cpath_ptr, err); - let ffi::RocksdbInstance(db_ptr) = db; + let db = rocksdb_ffi::rocksdb_open(opts, cpath_ptr, err); + let rocksdb_ffi::RocksdbInstance(db_ptr) = db; if err.is_not_null() { let cs = CString::new(err as *const i8, true); match cs.as_str() { @@ -199,14 +221,16 @@ pub fn open(path: String, create_if_missing: bool) -> Result { if db_ptr.is_null() { return Err("Could not initialize database.".to_string()); } - Ok(Rocksdb{inner: db, path: path}) + Ok(Rocksdb{inner: db}) } } +#[allow(dead_code)] #[test] fn external() { - let db = open("testdb".to_string(), true).unwrap(); - db.put(b"k1", b"v1111"); + let db = open("externaltest".to_string(), true).unwrap(); + let p = db.put(b"k1", b"v1111"); + assert!(p.is_ok()); let r: RocksdbResult = db.get(b"k1"); //assert!(r.is_some()); r.map(|v| { assert!(v.as_slice().len() == 5); } );