use std::cmp::Ordering;
use std::mem;
use std::os::raw::{c_char, c_int};
use std::slice;
use std::str;
use rocks_sys as ll;
pub trait Comparator {
fn compare(&self, a: &[u8], b: &[u8]) -> Ordering;
fn equal(&self, a: &[u8], b: &[u8]) -> bool {
self.compare(a, b) == Ordering::Equal
}
fn name(&self) -> &str {
"rust-rocks.Comparator\0"
}
fn find_shortest_separator(&self, _start: &[u8], _limit: &[u8]) -> Option<&[u8]> {
None
}
fn find_short_successor(&self, _key: &[u8]) -> Option<&[u8]> {
None
}
}
#[doc(hidden)]
pub mod rust_export {
use super::*;
#[no_mangle]
pub unsafe extern "C" fn rust_comparator_compare(cp: *mut (), a: *const &[u8], b: *const &[u8]) -> c_int {
let comparator = cp as *mut &dyn Comparator;
mem::transmute::<_, i8>((*comparator).compare(*a, *b)) as c_int
}
#[no_mangle]
pub unsafe extern "C" fn rust_comparator_equal(cp: *mut (), a: *const &[u8], b: *const &[u8]) -> c_char {
let comparator = cp as *mut &dyn Comparator;
((*comparator).equal(*a, *b)) as c_char
}
#[no_mangle]
pub unsafe extern "C" fn rust_comparator_name(cp: *mut ()) -> *const c_char {
let comparator = cp as *mut &dyn Comparator;
(*comparator).name().as_ptr() as *const _
}
#[no_mangle]
pub unsafe extern "C" fn rust_comparator_find_shortest_separator(
cp: *mut (),
start: *mut (), limit: *const &[u8],
) {
let comparator = cp as *mut &dyn Comparator;
let start_ptr = ll::cxx_string_data(start as *const _);
let start_len = ll::cxx_string_size(start as *const _);
let ret =
(*comparator).find_shortest_separator(slice::from_raw_parts(start_ptr as *const _, start_len as _), *limit);
if let Some(new_start) = ret {
ll::cxx_string_assign(start as *mut _, new_start.as_ptr() as *const _, new_start.len() as _)
}
}
#[no_mangle]
pub unsafe extern "C" fn rust_comparator_find_short_successor(cp: *mut (), key: *mut ()) {
let comparator = cp as *mut &dyn Comparator;
let key_ptr = ll::cxx_string_data(key as *const _);
let key_len = ll::cxx_string_size(key as *const _);
let ret = (*comparator).find_short_successor(slice::from_raw_parts(key_ptr as *const _, key_len as _));
if let Some(new_key) = ret {
ll::cxx_string_assign(key as *mut _, new_key.as_ptr() as *const _, new_key.len() as _);
}
}
#[no_mangle]
pub unsafe extern "C" fn rust_comparator_drop(op: *mut ()) {
assert!(!op.is_null());
let operator = op as *mut &dyn Comparator;
Box::from_raw(operator);
}
}
#[cfg(test)]
mod tests {
use lazy_static::lazy_static;
use std::str;
use super::super::rocksdb::*;
use super::*;
#[test]
fn bitwise_comparator_normal() {
let tmp_dir = ::tempdir::TempDir::new_in(".", "rocks").unwrap();
let db = DB::open(
Options::default()
.map_db_options(|db| db.create_if_missing(true))
.map_cf_options(|cf| cf.bitwise_comparator_reversed(false)),
tmp_dir,
)
.unwrap();
assert!(db.put(&WriteOptions::default(), b"key1", b"val2").is_ok());
assert!(db.put(&WriteOptions::default(), b"key2", b"val2").is_ok());
let mut it = db.new_iterator(&ReadOptions::default().pin_data(true));
it.seek_to_first();
assert!(it.is_valid());
let first = it.key().to_vec();
it.next();
assert!(it.is_valid());
let second = it.key().to_vec();
assert!(first < second);
}
#[test]
fn bitwise_comparator_reversed() {
let tmp_dir = ::tempdir::TempDir::new_in(".", "rocks").unwrap();
let db = DB::open(
Options::default()
.map_db_options(|db| db.create_if_missing(true))
.map_cf_options(|cf| cf.bitwise_comparator_reversed(true)),
tmp_dir,
)
.unwrap();
assert!(db.put(&WriteOptions::default(), b"key1", b"").is_ok());
assert!(db.put(&WriteOptions::default(), b"key2", b"").is_ok());
let mut it = db.new_iterator(&ReadOptions::default().pin_data(true));
it.seek_to_first();
assert!(it.is_valid());
let first = it.key().to_vec();
it.next();
assert!(it.is_valid());
let second = it.key().to_vec();
assert!(first > second);
}
pub struct MyComparator;
impl Comparator for MyComparator {
fn compare(&self, a: &[u8], b: &[u8]) -> ::std::cmp::Ordering {
let sa = unsafe { str::from_utf8_unchecked(a) };
let sb = unsafe { str::from_utf8_unchecked(b) };
sa.to_lowercase().cmp(&sb.to_lowercase())
}
}
lazy_static! {
static ref CMP: MyComparator = MyComparator;
}
#[test]
fn custom_lowercase_comparator() {
let tmp_dir = ::tempdir::TempDir::new_in(".", "rocks").unwrap();
let opts = Options::default()
.map_db_options(|db| db.create_if_missing(true))
.map_cf_options(|cf| cf.comparator(&*CMP));
let db = DB::open(opts, tmp_dir).unwrap();
assert!(db.put(&WriteOptions::default(), b"Key1", b"").is_ok());
assert!(db.put(&WriteOptions::default(), b"kEY3", b"").is_ok());
assert!(db.put(&WriteOptions::default(), b"key4", b"").is_ok());
assert!(db.put(&WriteOptions::default(), b"kEy2", b"").is_ok());
let ks = db
.new_iterator(&ReadOptions::default().pin_data(true))
.keys()
.map(|key| String::from_utf8_lossy(key))
.collect::<Vec<_>>();
assert_eq!(ks, vec!["Key1", "kEy2", "kEY3", "key4"]);
}
}