|
|
|
@ -21,18 +21,20 @@ use lmdb::{ |
|
|
|
|
DatabaseFlags, |
|
|
|
|
Environment, |
|
|
|
|
EnvironmentBuilder, |
|
|
|
|
RoTransaction, |
|
|
|
|
RwTransaction, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
use error::StoreError; |
|
|
|
|
|
|
|
|
|
use integer::{ |
|
|
|
|
IntegerStore, |
|
|
|
|
PrimitiveInt, |
|
|
|
|
}; |
|
|
|
|
// use integer::{
|
|
|
|
|
// IntegerStore,
|
|
|
|
|
// PrimitiveInt,
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
use readwrite::Store; |
|
|
|
|
use readwrite::{ |
|
|
|
|
Reader, |
|
|
|
|
Store, |
|
|
|
|
Writer, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
pub static DEFAULT_MAX_DBS: c_uint = 5; |
|
|
|
|
|
|
|
|
@ -85,37 +87,34 @@ impl Rkv { |
|
|
|
|
|
|
|
|
|
/// Store creation methods.
|
|
|
|
|
impl Rkv { |
|
|
|
|
pub fn open_or_create_default(&self) -> Result<Store<&str>, StoreError> { |
|
|
|
|
pub fn open_or_create_default(&self) -> Result<Store, StoreError> { |
|
|
|
|
self.open_or_create(None) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn open_or_create<'s, T, K>(&self, name: T) -> Result<Store<K>, StoreError> |
|
|
|
|
pub fn open_or_create<'s, T>(&self, name: T) -> Result<Store, StoreError> |
|
|
|
|
where |
|
|
|
|
T: Into<Option<&'s str>>, |
|
|
|
|
K: AsRef<[u8]>, |
|
|
|
|
{ |
|
|
|
|
let flags = DatabaseFlags::empty(); |
|
|
|
|
self.open_or_create_with_flags(name, flags) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn open_or_create_integer<'s, T, K>(&self, name: T) -> Result<IntegerStore<K>, StoreError> |
|
|
|
|
where |
|
|
|
|
T: Into<Option<&'s str>>, |
|
|
|
|
K: PrimitiveInt, |
|
|
|
|
{ |
|
|
|
|
let mut flags = DatabaseFlags::empty(); |
|
|
|
|
flags.toggle(DatabaseFlags::INTEGER_KEY); |
|
|
|
|
let db = self.env.create_db(name.into(), flags).map_err(|e| match e { |
|
|
|
|
lmdb::Error::BadRslot => StoreError::open_during_transaction(), |
|
|
|
|
_ => e.into(), |
|
|
|
|
})?; |
|
|
|
|
Ok(IntegerStore::new(db)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn open_or_create_with_flags<'s, T, K>(&self, name: T, flags: DatabaseFlags) -> Result<Store<K>, StoreError> |
|
|
|
|
// pub fn open_or_create_integer<'s, T>(&self, name: T) -> Result<IntegerStore, StoreError>
|
|
|
|
|
// where
|
|
|
|
|
// T: Into<Option<&'s str>>,
|
|
|
|
|
// {
|
|
|
|
|
// let mut flags = DatabaseFlags::empty();
|
|
|
|
|
// flags.toggle(DatabaseFlags::INTEGER_KEY);
|
|
|
|
|
// let db = self.env.create_db(name.into(), flags).map_err(|e| match e {
|
|
|
|
|
// lmdb::Error::BadRslot => StoreError::open_during_transaction(),
|
|
|
|
|
// _ => e.into(),
|
|
|
|
|
// })?;
|
|
|
|
|
// Ok(IntegerStore::new(db))
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
pub fn open_or_create_with_flags<'s, T>(&self, name: T, flags: DatabaseFlags) -> Result<Store, StoreError> |
|
|
|
|
where |
|
|
|
|
T: Into<Option<&'s str>>, |
|
|
|
|
K: AsRef<[u8]>, |
|
|
|
|
{ |
|
|
|
|
let db = self.env.create_db(name.into(), flags).map_err(|e| match e { |
|
|
|
|
lmdb::Error::BadRslot => StoreError::open_during_transaction(), |
|
|
|
@ -143,12 +142,20 @@ impl Rkv { |
|
|
|
|
|
|
|
|
|
/// Read and write accessors.
|
|
|
|
|
impl Rkv { |
|
|
|
|
pub fn read(&self) -> Result<RoTransaction, lmdb::Error> { |
|
|
|
|
self.env.begin_ro_txn() |
|
|
|
|
pub fn read<K>(&self) -> Result<Reader<K>, StoreError> |
|
|
|
|
where |
|
|
|
|
K: AsRef<[u8]>, |
|
|
|
|
{ |
|
|
|
|
let txn = self.env.begin_ro_txn()?; |
|
|
|
|
Ok(Reader::new(txn)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn write(&self) -> Result<RwTransaction, lmdb::Error> { |
|
|
|
|
self.env.begin_rw_txn() |
|
|
|
|
pub fn write<K>(&self) -> Result<Writer<K>, StoreError> |
|
|
|
|
where |
|
|
|
|
K: AsRef<[u8]>, |
|
|
|
|
{ |
|
|
|
|
let txn = self.env.begin_rw_txn()?; |
|
|
|
|
Ok(Writer::new(txn)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -200,10 +207,10 @@ mod tests { |
|
|
|
|
let k = Rkv::new(root.path()).expect("new succeeded"); |
|
|
|
|
let _ = k.open_or_create_default().expect("created default"); |
|
|
|
|
|
|
|
|
|
let yyy: Store<&str> = k.open_or_create("yyy").expect("opened"); |
|
|
|
|
let reader = yyy.read(&k).expect("reader"); |
|
|
|
|
let yyy = k.open_or_create("yyy").expect("opened"); |
|
|
|
|
let reader = k.read::<&str>().expect("reader"); |
|
|
|
|
|
|
|
|
|
let result = reader.get("foo"); |
|
|
|
|
let result = reader.get(&yyy, "foo"); |
|
|
|
|
assert_eq!(None, result.expect("success but no value")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -213,98 +220,98 @@ mod tests { |
|
|
|
|
fs::create_dir_all(root.path()).expect("dir created"); |
|
|
|
|
let k = Rkv::new(root.path()).expect("new succeeded"); |
|
|
|
|
|
|
|
|
|
let sk: Store<&str> = k.open_or_create("sk").expect("opened"); |
|
|
|
|
let sk: Store = k.open_or_create("sk").expect("opened"); |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
let mut writer = sk.write(&k).expect("writer"); |
|
|
|
|
writer.put("foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
writer.put("noo", &Value::F64(1234.0.into())).expect("wrote"); |
|
|
|
|
writer.put("bar", &Value::Bool(true)).expect("wrote"); |
|
|
|
|
writer.put("baz", &Value::Str("héllo, yöu")).expect("wrote"); |
|
|
|
|
assert_eq!(writer.get("foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
assert_eq!(writer.get("noo").expect("read"), Some(Value::F64(1234.0.into()))); |
|
|
|
|
assert_eq!(writer.get("bar").expect("read"), Some(Value::Bool(true))); |
|
|
|
|
assert_eq!(writer.get("baz").expect("read"), Some(Value::Str("héllo, yöu"))); |
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
writer.put(&sk, "foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
writer.put(&sk, "noo", &Value::F64(1234.0.into())).expect("wrote"); |
|
|
|
|
writer.put(&sk, "bar", &Value::Bool(true)).expect("wrote"); |
|
|
|
|
writer.put(&sk, "baz", &Value::Str("héllo, yöu")).expect("wrote"); |
|
|
|
|
assert_eq!(writer.get(&sk, "foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
assert_eq!(writer.get(&sk, "noo").expect("read"), Some(Value::F64(1234.0.into()))); |
|
|
|
|
assert_eq!(writer.get(&sk, "bar").expect("read"), Some(Value::Bool(true))); |
|
|
|
|
assert_eq!(writer.get(&sk, "baz").expect("read"), Some(Value::Str("héllo, yöu"))); |
|
|
|
|
|
|
|
|
|
// Isolation. Reads won't return values.
|
|
|
|
|
let r = &k.read().unwrap(); |
|
|
|
|
assert_eq!(sk.get(r, "foo").expect("read"), None); |
|
|
|
|
assert_eq!(sk.get(r, "bar").expect("read"), None); |
|
|
|
|
assert_eq!(sk.get(r, "baz").expect("read"), None); |
|
|
|
|
let r = &k.read::<&str>().unwrap(); |
|
|
|
|
assert_eq!(r.get(&sk, "foo").expect("read"), None); |
|
|
|
|
assert_eq!(r.get(&sk, "bar").expect("read"), None); |
|
|
|
|
assert_eq!(r.get(&sk, "baz").expect("read"), None); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Dropped: tx rollback. Reads will still return nothing.
|
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
let r = &k.read().unwrap(); |
|
|
|
|
assert_eq!(sk.get(r, "foo").expect("read"), None); |
|
|
|
|
assert_eq!(sk.get(r, "bar").expect("read"), None); |
|
|
|
|
assert_eq!(sk.get(r, "baz").expect("read"), None); |
|
|
|
|
let r = &k.read::<&str>().unwrap(); |
|
|
|
|
assert_eq!(r.get(&sk, "foo").expect("read"), None); |
|
|
|
|
assert_eq!(r.get(&sk, "bar").expect("read"), None); |
|
|
|
|
assert_eq!(r.get(&sk, "baz").expect("read"), None); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
let mut writer = sk.write(&k).expect("writer"); |
|
|
|
|
writer.put("foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
writer.put("bar", &Value::Bool(true)).expect("wrote"); |
|
|
|
|
writer.put("baz", &Value::Str("héllo, yöu")).expect("wrote"); |
|
|
|
|
assert_eq!(writer.get("foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
assert_eq!(writer.get("bar").expect("read"), Some(Value::Bool(true))); |
|
|
|
|
assert_eq!(writer.get("baz").expect("read"), Some(Value::Str("héllo, yöu"))); |
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
writer.put(&sk, "foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
writer.put(&sk, "bar", &Value::Bool(true)).expect("wrote"); |
|
|
|
|
writer.put(&sk, "baz", &Value::Str("héllo, yöu")).expect("wrote"); |
|
|
|
|
assert_eq!(writer.get(&sk, "foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
assert_eq!(writer.get(&sk, "bar").expect("read"), Some(Value::Bool(true))); |
|
|
|
|
assert_eq!(writer.get(&sk, "baz").expect("read"), Some(Value::Str("héllo, yöu"))); |
|
|
|
|
|
|
|
|
|
writer.commit().expect("committed"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Committed. Reads will succeed.
|
|
|
|
|
{ |
|
|
|
|
let r = &k.read().unwrap(); |
|
|
|
|
assert_eq!(sk.get(r, "foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
assert_eq!(sk.get(r, "bar").expect("read"), Some(Value::Bool(true))); |
|
|
|
|
assert_eq!(sk.get(r, "baz").expect("read"), Some(Value::Str("héllo, yöu"))); |
|
|
|
|
let r = k.read::<&str>().unwrap(); |
|
|
|
|
assert_eq!(r.get(&sk, "foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
assert_eq!(r.get(&sk, "bar").expect("read"), Some(Value::Bool(true))); |
|
|
|
|
assert_eq!(r.get(&sk, "baz").expect("read"), Some(Value::Str("héllo, yöu"))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
let mut writer = sk.write(&k).expect("writer"); |
|
|
|
|
writer.delete("foo").expect("deleted"); |
|
|
|
|
writer.delete("bar").expect("deleted"); |
|
|
|
|
writer.delete("baz").expect("deleted"); |
|
|
|
|
assert_eq!(writer.get("foo").expect("read"), None); |
|
|
|
|
assert_eq!(writer.get("bar").expect("read"), None); |
|
|
|
|
assert_eq!(writer.get("baz").expect("read"), None); |
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
writer.delete(&sk, "foo").expect("deleted"); |
|
|
|
|
writer.delete(&sk, "bar").expect("deleted"); |
|
|
|
|
writer.delete(&sk, "baz").expect("deleted"); |
|
|
|
|
assert_eq!(writer.get(&sk, "foo").expect("read"), None); |
|
|
|
|
assert_eq!(writer.get(&sk, "bar").expect("read"), None); |
|
|
|
|
assert_eq!(writer.get(&sk, "baz").expect("read"), None); |
|
|
|
|
|
|
|
|
|
// Isolation. Reads still return values.
|
|
|
|
|
let r = &k.read().unwrap(); |
|
|
|
|
assert_eq!(sk.get(r, "foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
assert_eq!(sk.get(r, "bar").expect("read"), Some(Value::Bool(true))); |
|
|
|
|
assert_eq!(sk.get(r, "baz").expect("read"), Some(Value::Str("héllo, yöu"))); |
|
|
|
|
let r = k.read::<&str>().unwrap(); |
|
|
|
|
assert_eq!(r.get(&sk, "foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
assert_eq!(r.get(&sk, "bar").expect("read"), Some(Value::Bool(true))); |
|
|
|
|
assert_eq!(r.get(&sk, "baz").expect("read"), Some(Value::Str("héllo, yöu"))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Dropped: tx rollback. Reads will still return values.
|
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
let r = &k.read().unwrap(); |
|
|
|
|
assert_eq!(sk.get(r, "foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
assert_eq!(sk.get(r, "bar").expect("read"), Some(Value::Bool(true))); |
|
|
|
|
assert_eq!(sk.get(r, "baz").expect("read"), Some(Value::Str("héllo, yöu"))); |
|
|
|
|
let r = k.read::<&str>().unwrap(); |
|
|
|
|
assert_eq!(r.get(&sk, "foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
assert_eq!(r.get(&sk, "bar").expect("read"), Some(Value::Bool(true))); |
|
|
|
|
assert_eq!(r.get(&sk, "baz").expect("read"), Some(Value::Str("héllo, yöu"))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
let mut writer = sk.write(&k).expect("writer"); |
|
|
|
|
writer.delete("foo").expect("deleted"); |
|
|
|
|
writer.delete("bar").expect("deleted"); |
|
|
|
|
writer.delete("baz").expect("deleted"); |
|
|
|
|
assert_eq!(writer.get("foo").expect("read"), None); |
|
|
|
|
assert_eq!(writer.get("bar").expect("read"), None); |
|
|
|
|
assert_eq!(writer.get("baz").expect("read"), None); |
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
writer.delete(&sk, "foo").expect("deleted"); |
|
|
|
|
writer.delete(&sk, "bar").expect("deleted"); |
|
|
|
|
writer.delete(&sk, "baz").expect("deleted"); |
|
|
|
|
assert_eq!(writer.get(&sk, "foo").expect("read"), None); |
|
|
|
|
assert_eq!(writer.get(&sk, "bar").expect("read"), None); |
|
|
|
|
assert_eq!(writer.get(&sk, "baz").expect("read"), None); |
|
|
|
|
|
|
|
|
|
writer.commit().expect("committed"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Committed. Reads will succeed but return None to indicate a missing value.
|
|
|
|
|
{ |
|
|
|
|
let r = &k.read().unwrap(); |
|
|
|
|
assert_eq!(sk.get(r, "foo").expect("read"), None); |
|
|
|
|
assert_eq!(sk.get(r, "bar").expect("read"), None); |
|
|
|
|
assert_eq!(sk.get(r, "baz").expect("read"), None); |
|
|
|
|
let r = k.read::<&str>().unwrap(); |
|
|
|
|
assert_eq!(r.get(&sk, "foo").expect("read"), None); |
|
|
|
|
assert_eq!(r.get(&sk, "bar").expect("read"), None); |
|
|
|
|
assert_eq!(r.get(&sk, "baz").expect("read"), None); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -358,26 +365,26 @@ mod tests { |
|
|
|
|
let root = Builder::new().prefix("test_read_before_write_num").tempdir().expect("tempdir"); |
|
|
|
|
fs::create_dir_all(root.path()).expect("dir created"); |
|
|
|
|
let k = Rkv::new(root.path()).expect("new succeeded"); |
|
|
|
|
let sk: Store<&str> = k.open_or_create("sk").expect("opened"); |
|
|
|
|
let sk: Store = k.open_or_create("sk").expect("opened"); |
|
|
|
|
|
|
|
|
|
// Test reading a number, modifying it, and then writing it back.
|
|
|
|
|
// We have to be done with the Value::I64 before calling Writer::put,
|
|
|
|
|
// as the Value::I64 borrows an immutable reference to the Writer.
|
|
|
|
|
// So we extract and copy its primitive value.
|
|
|
|
|
|
|
|
|
|
fn get_existing_foo(writer: &Writer<&str>) -> Option<i64> { |
|
|
|
|
match writer.get("foo").expect("read") { |
|
|
|
|
fn get_existing_foo(writer: &Writer<&str>, store: &Store) -> Option<i64> { |
|
|
|
|
match writer.get(store, "foo").expect("read") { |
|
|
|
|
Some(Value::I64(val)) => Some(val), |
|
|
|
|
_ => None, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let mut writer = sk.write(&k).expect("writer"); |
|
|
|
|
let mut existing = get_existing_foo(&writer).unwrap_or(99); |
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
let mut existing = get_existing_foo(&writer, &sk).unwrap_or(99); |
|
|
|
|
existing += 1; |
|
|
|
|
writer.put("foo", &Value::I64(existing)).expect("success"); |
|
|
|
|
writer.put(&sk, "foo", &Value::I64(existing)).expect("success"); |
|
|
|
|
|
|
|
|
|
let updated = get_existing_foo(&writer).unwrap_or(99); |
|
|
|
|
let updated = get_existing_foo(&writer, &sk).unwrap_or(99); |
|
|
|
|
assert_eq!(updated, 100); |
|
|
|
|
writer.commit().expect("commit"); |
|
|
|
|
} |
|
|
|
@ -387,20 +394,20 @@ mod tests { |
|
|
|
|
let root = Builder::new().prefix("test_read_before_write_str").tempdir().expect("tempdir"); |
|
|
|
|
fs::create_dir_all(root.path()).expect("dir created"); |
|
|
|
|
let k = Rkv::new(root.path()).expect("new succeeded"); |
|
|
|
|
let sk: Store<&str> = k.open_or_create("sk").expect("opened"); |
|
|
|
|
let sk: Store = k.open_or_create("sk").expect("opened"); |
|
|
|
|
|
|
|
|
|
// Test reading a string, modifying it, and then writing it back.
|
|
|
|
|
// We have to be done with the Value::Str before calling Writer::put,
|
|
|
|
|
// as the Value::Str (and its underlying &str) borrows an immutable
|
|
|
|
|
// reference to the Writer. So we copy it to a String.
|
|
|
|
|
|
|
|
|
|
let mut writer = sk.write(&k).expect("writer"); |
|
|
|
|
let mut existing = match writer.get("foo").expect("read") { |
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
let mut existing = match writer.get(&sk, "foo").expect("read") { |
|
|
|
|
Some(Value::Str(val)) => val, |
|
|
|
|
_ => "", |
|
|
|
|
}.to_string(); |
|
|
|
|
existing.push('…'); |
|
|
|
|
writer.put("foo", &Value::Str(&existing)).expect("write"); |
|
|
|
|
writer.put(&sk, "foo", &Value::Str(&existing)).expect("write"); |
|
|
|
|
writer.commit().expect("commit"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -409,10 +416,9 @@ mod tests { |
|
|
|
|
let root = Builder::new().prefix("test_concurrent_reads_prohibited").tempdir().expect("tempdir"); |
|
|
|
|
fs::create_dir_all(root.path()).expect("dir created"); |
|
|
|
|
let k = Rkv::new(root.path()).expect("new succeeded"); |
|
|
|
|
let s: Store<&str> = k.open_or_create("s").expect("opened"); |
|
|
|
|
|
|
|
|
|
let _first = s.read(&k).expect("reader"); |
|
|
|
|
let second = s.read(&k); |
|
|
|
|
let _first = k.read::<&str>().expect("reader"); |
|
|
|
|
let second = k.read::<&str>(); |
|
|
|
|
|
|
|
|
|
match second { |
|
|
|
|
Err(StoreError::ReadTransactionAlreadyExists(t)) => { |
|
|
|
@ -429,46 +435,41 @@ mod tests { |
|
|
|
|
let root = Builder::new().prefix("test_isolation").tempdir().expect("tempdir"); |
|
|
|
|
fs::create_dir_all(root.path()).expect("dir created"); |
|
|
|
|
let k = Rkv::new(root.path()).expect("new succeeded"); |
|
|
|
|
let s: Store<&str> = k.open_or_create("s").expect("opened"); |
|
|
|
|
let s: Store = k.open_or_create("s").expect("opened"); |
|
|
|
|
|
|
|
|
|
// Add one field.
|
|
|
|
|
{ |
|
|
|
|
let mut writer = s.write(&k).expect("writer"); |
|
|
|
|
writer.put("foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
writer.put(&s, "foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
writer.commit().expect("committed"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Both ways of reading see the value.
|
|
|
|
|
{ |
|
|
|
|
let reader = &k.read().unwrap(); |
|
|
|
|
assert_eq!(s.get(reader, "foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
let reader = s.read(&k).unwrap(); |
|
|
|
|
assert_eq!(reader.get("foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
let reader = k.read::<&str>().unwrap(); |
|
|
|
|
assert_eq!(reader.get(&s, "foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Establish a long-lived reader that outlasts a writer.
|
|
|
|
|
let reader = s.read(&k).expect("reader"); |
|
|
|
|
assert_eq!(reader.get("foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
let reader = k.read::<&str>().expect("reader"); |
|
|
|
|
assert_eq!(reader.get(&s, "foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
|
|
|
|
|
// Start a write transaction.
|
|
|
|
|
let mut writer = s.write(&k).expect("writer"); |
|
|
|
|
writer.put("foo", &Value::I64(999)).expect("wrote"); |
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
writer.put(&s, "foo", &Value::I64(999)).expect("wrote"); |
|
|
|
|
|
|
|
|
|
// The reader and writer are isolated.
|
|
|
|
|
assert_eq!(reader.get("foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
assert_eq!(writer.get("foo").expect("read"), Some(Value::I64(999))); |
|
|
|
|
assert_eq!(reader.get(&s, "foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
assert_eq!(writer.get(&s, "foo").expect("read"), Some(Value::I64(999))); |
|
|
|
|
|
|
|
|
|
// If we commit the writer, we still have isolation.
|
|
|
|
|
writer.commit().expect("committed"); |
|
|
|
|
assert_eq!(reader.get("foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
assert_eq!(reader.get(&s, "foo").expect("read"), Some(Value::I64(1234))); |
|
|
|
|
|
|
|
|
|
// A new reader sees the committed value. Note that LMDB doesn't allow two
|
|
|
|
|
// read transactions to exist in the same thread, so we abort the previous one.
|
|
|
|
|
reader.abort(); |
|
|
|
|
let reader = s.read(&k).expect("reader"); |
|
|
|
|
assert_eq!(reader.get("foo").expect("read"), Some(Value::I64(999))); |
|
|
|
|
let reader = k.read::<&str>().expect("reader"); |
|
|
|
|
assert_eq!(reader.get(&s, "foo").expect("read"), Some(Value::I64(999))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
@ -476,12 +477,12 @@ mod tests { |
|
|
|
|
let root = Builder::new().prefix("test_round_trip_blob").tempdir().expect("tempdir"); |
|
|
|
|
fs::create_dir_all(root.path()).expect("dir created"); |
|
|
|
|
let k = Rkv::new(root.path()).expect("new succeeded"); |
|
|
|
|
let sk: Store<&str> = k.open_or_create("sk").expect("opened"); |
|
|
|
|
let mut writer = sk.write(&k).expect("writer"); |
|
|
|
|
let sk: Store = k.open_or_create("sk").expect("opened"); |
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
|
|
|
|
|
assert_eq!(writer.get("foo").expect("read"), None); |
|
|
|
|
writer.put("foo", &Value::Blob(&[1, 2, 3, 4])).expect("wrote"); |
|
|
|
|
assert_eq!(writer.get("foo").expect("read"), Some(Value::Blob(&[1, 2, 3, 4]))); |
|
|
|
|
assert_eq!(writer.get(&sk, "foo").expect("read"), None); |
|
|
|
|
writer.put(&sk, "foo", &Value::Blob(&[1, 2, 3, 4])).expect("wrote"); |
|
|
|
|
assert_eq!(writer.get(&sk, "foo").expect("read"), Some(Value::Blob(&[1, 2, 3, 4]))); |
|
|
|
|
|
|
|
|
|
fn u16_to_u8(src: &[u16]) -> Vec<u8> { |
|
|
|
|
let mut dst = vec![0; 2 * src.len()]; |
|
|
|
@ -499,9 +500,9 @@ mod tests { |
|
|
|
|
// their [u16] backing storage to [u8]. Test that converting, writing,
|
|
|
|
|
// reading, and converting back works as expected.
|
|
|
|
|
let u16_array = [1000, 10000, 54321, 65535]; |
|
|
|
|
assert_eq!(writer.get("bar").expect("read"), None); |
|
|
|
|
writer.put("bar", &Value::Blob(&u16_to_u8(&u16_array))).expect("wrote"); |
|
|
|
|
let u8_array = match writer.get("bar").expect("read") { |
|
|
|
|
assert_eq!(writer.get(&sk, "bar").expect("read"), None); |
|
|
|
|
writer.put(&sk, "bar", &Value::Blob(&u16_to_u8(&u16_array))).expect("wrote"); |
|
|
|
|
let u8_array = match writer.get(&sk, "bar").expect("read") { |
|
|
|
|
Some(Value::Blob(val)) => val, |
|
|
|
|
_ => &[], |
|
|
|
|
}; |
|
|
|
@ -514,12 +515,12 @@ mod tests { |
|
|
|
|
let root = Builder::new().prefix("test_delete_value").tempdir().expect("tempdir"); |
|
|
|
|
fs::create_dir_all(root.path()).expect("dir created"); |
|
|
|
|
let k = Rkv::new(root.path()).expect("new succeeded"); |
|
|
|
|
let sk: Store<&str> = k.open_or_create_with_flags("sk", DatabaseFlags::DUP_SORT).expect("opened"); |
|
|
|
|
let sk: Store = k.open_or_create_with_flags("sk", DatabaseFlags::DUP_SORT).expect("opened"); |
|
|
|
|
|
|
|
|
|
let mut writer = sk.write(&k).expect("writer"); |
|
|
|
|
writer.put("foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
writer.put("foo", &Value::I64(1235)).expect("wrote"); |
|
|
|
|
writer.delete_value("foo", &Value::I64(1234)).expect("deleted"); |
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
writer.put(&sk, "foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
writer.put(&sk, "foo", &Value::I64(1235)).expect("wrote"); |
|
|
|
|
writer.delete_value(&sk, "foo", &Value::I64(1234)).expect("deleted"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
@ -527,28 +528,28 @@ mod tests { |
|
|
|
|
let root = Builder::new().prefix("test_iter").tempdir().expect("tempdir"); |
|
|
|
|
fs::create_dir_all(root.path()).expect("dir created"); |
|
|
|
|
let k = Rkv::new(root.path()).expect("new succeeded"); |
|
|
|
|
let sk: Store<&str> = k.open_or_create("sk").expect("opened"); |
|
|
|
|
let sk: Store = k.open_or_create("sk").expect("opened"); |
|
|
|
|
|
|
|
|
|
// An iterator over an empty store returns no values.
|
|
|
|
|
{ |
|
|
|
|
let reader = sk.read(&k).unwrap(); |
|
|
|
|
let mut iter = reader.iter_start().unwrap(); |
|
|
|
|
let reader = k.read::<&str>().unwrap(); |
|
|
|
|
let mut iter = reader.iter_start(&sk).unwrap(); |
|
|
|
|
assert!(iter.next().is_none()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let mut writer = sk.write(&k).expect("writer"); |
|
|
|
|
writer.put("foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
writer.put("noo", &Value::F64(1234.0.into())).expect("wrote"); |
|
|
|
|
writer.put("bar", &Value::Bool(true)).expect("wrote"); |
|
|
|
|
writer.put("baz", &Value::Str("héllo, yöu")).expect("wrote"); |
|
|
|
|
writer.put("héllò, töűrîst", &Value::Str("Emil.RuleZ!")).expect("wrote"); |
|
|
|
|
writer.put("你好,遊客", &Value::Str("米克規則")).expect("wrote"); |
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
writer.put(&sk, "foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
writer.put(&sk, "noo", &Value::F64(1234.0.into())).expect("wrote"); |
|
|
|
|
writer.put(&sk, "bar", &Value::Bool(true)).expect("wrote"); |
|
|
|
|
writer.put(&sk, "baz", &Value::Str("héllo, yöu")).expect("wrote"); |
|
|
|
|
writer.put(&sk, "héllò, töűrîst", &Value::Str("Emil.RuleZ!")).expect("wrote"); |
|
|
|
|
writer.put(&sk, "你好,遊客", &Value::Str("米克規則")).expect("wrote"); |
|
|
|
|
writer.commit().expect("committed"); |
|
|
|
|
|
|
|
|
|
let reader = sk.read(&k).unwrap(); |
|
|
|
|
let reader = k.read::<&str>().unwrap(); |
|
|
|
|
|
|
|
|
|
// Reader.iter() returns (key, value) tuples ordered by key.
|
|
|
|
|
let mut iter = reader.iter_start().unwrap(); |
|
|
|
|
let mut iter = reader.iter_start(&sk).unwrap(); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "bar"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::Bool(true))); |
|
|
|
@ -575,7 +576,7 @@ mod tests { |
|
|
|
|
|
|
|
|
|
// Reader.iter_from() begins iteration at the first key equal to
|
|
|
|
|
// or greater than the given key.
|
|
|
|
|
let mut iter = reader.iter_from("moo").unwrap(); |
|
|
|
|
let mut iter = reader.iter_from(&sk, "moo").unwrap(); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "noo"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into()))); |
|
|
|
@ -586,7 +587,7 @@ mod tests { |
|
|
|
|
|
|
|
|
|
// Reader.iter_from() works as expected when the given key is a prefix
|
|
|
|
|
// of a key in the store.
|
|
|
|
|
let mut iter = reader.iter_from("no").unwrap(); |
|
|
|
|
let mut iter = reader.iter_from(&sk, "no").unwrap(); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "noo"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into()))); |
|
|
|
@ -602,16 +603,16 @@ mod tests { |
|
|
|
|
let root = Builder::new().prefix("test_iter_from_key_greater_than_existing").tempdir().expect("tempdir"); |
|
|
|
|
fs::create_dir_all(root.path()).expect("dir created"); |
|
|
|
|
let k = Rkv::new(root.path()).expect("new succeeded"); |
|
|
|
|
let sk: Store<&str> = k.open_or_create("sk").expect("opened"); |
|
|
|
|
let sk: Store = k.open_or_create("sk").expect("opened"); |
|
|
|
|
|
|
|
|
|
let mut writer = sk.write(&k).expect("writer"); |
|
|
|
|
writer.put("foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
writer.put("noo", &Value::F64(1234.0.into())).expect("wrote"); |
|
|
|
|
writer.put("bar", &Value::Bool(true)).expect("wrote"); |
|
|
|
|
writer.put("baz", &Value::Str("héllo, yöu")).expect("wrote"); |
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
writer.put(&sk, "foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
writer.put(&sk, "noo", &Value::F64(1234.0.into())).expect("wrote"); |
|
|
|
|
writer.put(&sk, "bar", &Value::Bool(true)).expect("wrote"); |
|
|
|
|
writer.put(&sk, "baz", &Value::Str("héllo, yöu")).expect("wrote"); |
|
|
|
|
writer.commit().expect("committed"); |
|
|
|
|
|
|
|
|
|
let reader = sk.read(&k).unwrap(); |
|
|
|
|
let reader = k.read::<&str>().unwrap(); |
|
|
|
|
|
|
|
|
|
// There is no key greater than "nuu", so the underlying LMDB API panics
|
|
|
|
|
// when calling iter_from. This is unfortunate, and I've requested
|
|
|
|
@ -620,6 +621,158 @@ mod tests { |
|
|
|
|
//
|
|
|
|
|
// Also see alternative https://github.com/danburkert/lmdb-rs/pull/30.
|
|
|
|
|
//
|
|
|
|
|
reader.iter_from("nuu").unwrap(); |
|
|
|
|
reader.iter_from(&sk, "nuu").unwrap(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_mutilpe_store_read_write() { |
|
|
|
|
let root = Builder::new().prefix("test_mutilpe_store_read_write").tempdir().expect("tempdir"); |
|
|
|
|
fs::create_dir_all(root.path()).expect("dir created"); |
|
|
|
|
let k = Rkv::new(root.path()).expect("new succeeded"); |
|
|
|
|
|
|
|
|
|
let s1: Store = k.open_or_create("store_1").expect("opened"); |
|
|
|
|
let s2: Store = k.open_or_create("store_2").expect("opened"); |
|
|
|
|
let s3: Store = k.open_or_create("store_3").expect("opened"); |
|
|
|
|
|
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
writer.put(&s1, "foo", &Value::Str("bar")).expect("wrote"); |
|
|
|
|
writer.put(&s2, "foo", &Value::I64(123)).expect("wrote"); |
|
|
|
|
writer.put(&s3, "foo", &Value::Bool(true)).expect("wrote"); |
|
|
|
|
|
|
|
|
|
assert_eq!(writer.get(&s1, "foo").expect("read"), Some(Value::Str("bar"))); |
|
|
|
|
assert_eq!(writer.get(&s2, "foo").expect("read"), Some(Value::I64(123))); |
|
|
|
|
assert_eq!(writer.get(&s3, "foo").expect("read"), Some(Value::Bool(true))); |
|
|
|
|
|
|
|
|
|
writer.commit().expect("committed"); |
|
|
|
|
|
|
|
|
|
let reader = k.read::<&str>().expect("unbound_reader"); |
|
|
|
|
assert_eq!(reader.get(&s1, "foo").expect("read"), Some(Value::Str("bar"))); |
|
|
|
|
assert_eq!(reader.get(&s2, "foo").expect("read"), Some(Value::I64(123))); |
|
|
|
|
assert_eq!(reader.get(&s3, "foo").expect("read"), Some(Value::Bool(true))); |
|
|
|
|
reader.abort(); |
|
|
|
|
|
|
|
|
|
// test delete across multiple stores
|
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
writer.delete(&s1, "foo").expect("deleted"); |
|
|
|
|
writer.delete(&s2, "foo").expect("deleted"); |
|
|
|
|
writer.delete(&s3, "foo").expect("deleted"); |
|
|
|
|
writer.commit().expect("committed"); |
|
|
|
|
|
|
|
|
|
let reader = k.read::<&str>().expect("reader"); |
|
|
|
|
assert_eq!(reader.get(&s1, "key").expect("value"), None); |
|
|
|
|
assert_eq!(reader.get(&s2, "key").expect("value"), None); |
|
|
|
|
assert_eq!(reader.get(&s3, "key").expect("value"), None); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_multiple_store_iter() { |
|
|
|
|
let root = Builder::new().prefix("test_multiple_store_iter").tempdir().expect("tempdir"); |
|
|
|
|
fs::create_dir_all(root.path()).expect("dir created"); |
|
|
|
|
let k = Rkv::new(root.path()).expect("new succeeded"); |
|
|
|
|
let s1: Store = k.open_or_create("store_1").expect("opened"); |
|
|
|
|
let s2: Store = k.open_or_create("store_2").expect("opened"); |
|
|
|
|
|
|
|
|
|
let mut writer = k.write::<&str>().expect("writer"); |
|
|
|
|
// Write to "s1"
|
|
|
|
|
writer.put(&s1, "foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
writer.put(&s1, "noo", &Value::F64(1234.0.into())).expect("wrote"); |
|
|
|
|
writer.put(&s1, "bar", &Value::Bool(true)).expect("wrote"); |
|
|
|
|
writer.put(&s1, "baz", &Value::Str("héllo, yöu")).expect("wrote"); |
|
|
|
|
writer.put(&s1, "héllò, töűrîst", &Value::Str("Emil.RuleZ!")).expect("wrote"); |
|
|
|
|
writer.put(&s1, "你好,遊客", &Value::Str("米克規則")).expect("wrote"); |
|
|
|
|
// Writer to "s2"
|
|
|
|
|
writer.put(&s2, "foo", &Value::I64(1234)).expect("wrote"); |
|
|
|
|
writer.put(&s2, "noo", &Value::F64(1234.0.into())).expect("wrote"); |
|
|
|
|
writer.put(&s2, "bar", &Value::Bool(true)).expect("wrote"); |
|
|
|
|
writer.put(&s2, "baz", &Value::Str("héllo, yöu")).expect("wrote"); |
|
|
|
|
writer.put(&s2, "héllò, töűrîst", &Value::Str("Emil.RuleZ!")).expect("wrote"); |
|
|
|
|
writer.put(&s2, "你好,遊客", &Value::Str("米克規則")).expect("wrote"); |
|
|
|
|
writer.commit().expect("committed"); |
|
|
|
|
|
|
|
|
|
let reader = k.read::<&str>().unwrap(); |
|
|
|
|
|
|
|
|
|
// Iterate through the whole store in "s1"
|
|
|
|
|
let mut iter = reader.iter_start(&s1).unwrap(); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "bar"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::Bool(true))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "baz"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::Str("héllo, yöu"))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "foo"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::I64(1234))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "héllò, töűrîst"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::Str("Emil.RuleZ!"))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "noo"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into()))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::Str("米克規則"))); |
|
|
|
|
assert!(iter.next().is_none()); |
|
|
|
|
|
|
|
|
|
// Iterate through the whole store in "s2"
|
|
|
|
|
let mut iter = reader.iter_start(&s2).unwrap(); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "bar"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::Bool(true))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "baz"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::Str("héllo, yöu"))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "foo"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::I64(1234))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "héllò, töűrîst"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::Str("Emil.RuleZ!"))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "noo"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into()))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::Str("米克規則"))); |
|
|
|
|
assert!(iter.next().is_none()); |
|
|
|
|
|
|
|
|
|
// Iterate from a given key in "s1"
|
|
|
|
|
let mut iter = reader.iter_from(&s1, "moo").unwrap(); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "noo"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into()))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::Str("米克規則"))); |
|
|
|
|
assert!(iter.next().is_none()); |
|
|
|
|
|
|
|
|
|
// Iterate from a given key in "s2"
|
|
|
|
|
let mut iter = reader.iter_from(&s2, "moo").unwrap(); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "noo"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into()))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::Str("米克規則"))); |
|
|
|
|
assert!(iter.next().is_none()); |
|
|
|
|
|
|
|
|
|
// Iterate from a given prefix in "s1"
|
|
|
|
|
let mut iter = reader.iter_from(&s1, "no").unwrap(); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "noo"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into()))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::Str("米克規則"))); |
|
|
|
|
assert!(iter.next().is_none()); |
|
|
|
|
|
|
|
|
|
// Iterate from a given prefix in "s2"
|
|
|
|
|
let mut iter = reader.iter_from(&s2, "no").unwrap(); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "noo"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::F64(1234.0.into()))); |
|
|
|
|
let (key, val) = iter.next().unwrap(); |
|
|
|
|
assert_eq!(str::from_utf8(key).expect("key"), "你好,遊客"); |
|
|
|
|
assert_eq!(val.expect("value"), Some(Value::Str("米克規則"))); |
|
|
|
|
assert!(iter.next().is_none()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|