Merge pull request #39 from petehunt/iterator

More idiomatic iterators?
master
Tyler Neely 9 years ago
commit 8dffaa356a
  1. 25
      README.md
  2. 2
      src/lib.rs
  3. 139
      src/rocksdb.rs
  4. 38
      test/test_iterator.rs

@ -49,7 +49,7 @@ fn main() {
match db.get(b"my key") { match db.get(b"my key") {
Ok(Some(value)) => println!("retrieved value {}", value.to_utf8().unwrap()), Ok(Some(value)) => println!("retrieved value {}", value.to_utf8().unwrap()),
Ok(None) => println!("value not found"), Ok(None) => println!("value not found"),
Err(e) => println!("operational problem encountered: {}", e), Err(e) => println!("operational problem encountered: {}", e),
} }
db.delete(b"my key"); db.delete(b"my key");
@ -77,25 +77,33 @@ fn main() {
###### Getting an Iterator ###### Getting an Iterator
```rust ```rust
extern crate rocksdb; extern crate rocksdb;
use rocksdb::{DB, Direction}; use rocksdb::{DB, Direction, IteratorMode};
fn main() { fn main() {
// NB: db is automatically freed at end of lifetime // NB: db is automatically freed at end of lifetime
let mut db = DB::open_default("/path/for/rocksdb/storage").unwrap(); let mut db = DB::open_default("/path/for/rocksdb/storage").unwrap();
let mut iter = db.iterator(); let mut iter = db.iterator(IteratorMode::Start); // Always iterates forward
for (key, value) in iter.from_start() { // Always iterates forward for (key, value) in iter {
println!("Saw {} {}", key, value); //actually, need to convert [u8] keys into Strings println!("Saw {} {}", key, value); //actually, need to convert [u8] keys into Strings
} }
for (key, value) in iter.from_end() { //Always iterates backward iter = db.iterator(IteratorMode::End); // Always iterates backward
for (key, value) in iter {
println!("Saw {} {}", key, value);
}
iter = db.iterator(IteratorMode::From(b"my key", Direction::forward)); // From a key in Direction::{forward,reverse}
for (key, value) in iter {
println!("Saw {} {}", key, value); println!("Saw {} {}", key, value);
} }
for (key, value) in iter.from(b"my key", Direction::forward) { // From a key in Direction::{forward,reverse}
// You can seek with an existing Iterator instance, too
iter.set_mode(IteratorMode::From(b"another key", Direction::reverse));
for (key, value) in iter {
println!("Saw {} {}", key, value); println!("Saw {} {}", key, value);
} }
} }
``` ```
###### Getting an Iterator ###### Getting an Iterator from a Snapshot
```rust ```rust
extern crate rocksdb; extern crate rocksdb;
use rocksdb::{DB, Direction}; use rocksdb::{DB, Direction};
@ -104,7 +112,7 @@ fn main() {
// NB: db is automatically freed at end of lifetime // NB: db is automatically freed at end of lifetime
let mut db = DB::open_default("/path/for/rocksdb/storage").unwrap(); let mut db = DB::open_default("/path/for/rocksdb/storage").unwrap();
let snapshot = db.snapshot(); // Creates a longer-term snapshot of the DB, but freed when goes out of scope let snapshot = db.snapshot(); // Creates a longer-term snapshot of the DB, but freed when goes out of scope
let mut iter = snapshot.iterator(); // Make as many iterators as you'd like from one snapshot let mut iter = snapshot.iterator(IteratorMode::Start); // Make as many iterators as you'd like from one snapshot
} }
``` ```
@ -176,4 +184,3 @@ fn badly_tuned_for_somebody_elses_disk() -> DB {
DB::open(&opts, path).unwrap() DB::open(&opts, path).unwrap()
} }
``` ```

@ -15,7 +15,7 @@
// //
pub use ffi as rocksdb_ffi; pub use ffi as rocksdb_ffi;
pub use ffi::{DBCompactionStyle, DBComparator, new_bloom_filter}; pub use ffi::{DBCompactionStyle, DBComparator, new_bloom_filter};
pub use rocksdb::{DB, DBIterator, DBVector, Direction, SubDBIterator, Writable, WriteBatch}; pub use rocksdb::{DB, DBIterator, DBVector, Direction, Writable, WriteBatch, IteratorMode};
pub use rocksdb_options::{BlockBasedOptions, Options}; pub use rocksdb_options::{BlockBasedOptions, Options};
pub use merge_operator::MergeOperands; pub use merge_operator::MergeOperands;
pub mod rocksdb; pub mod rocksdb;

@ -51,9 +51,8 @@ pub struct Snapshot<'a> {
inner: rocksdb_ffi::DBSnapshot, inner: rocksdb_ffi::DBSnapshot,
} }
pub struct DBIterator { pub struct DBIterator<'a> {
// TODO: should have a reference to DB to enforce scope, but it's trickier than I db: &'a DB,
// thought to add
inner: rocksdb_ffi::DBIterator, inner: rocksdb_ffi::DBIterator,
direction: Direction, direction: Direction,
just_seeked: bool, just_seeked: bool,
@ -64,23 +63,18 @@ pub enum Direction {
reverse, reverse,
} }
pub struct SubDBIterator<'a> { impl<'a> Iterator for DBIterator<'a> {
iter: &'a mut DBIterator,
direction: Direction,
}
impl <'a> Iterator for SubDBIterator<'a> {
type Item = (Box<[u8]>, Box<[u8]>); type Item = (Box<[u8]>, Box<[u8]>);
fn next(&mut self) -> Option<(Box<[u8]>, Box<[u8]>)> { fn next(&mut self) -> Option<(Box<[u8]>, Box<[u8]>)> {
let native_iter = self.iter.inner; let native_iter = self.inner;
if !self.iter.just_seeked { if !self.just_seeked {
match self.direction { match self.direction {
Direction::forward => unsafe { rocksdb_ffi::rocksdb_iter_next(native_iter) }, Direction::forward => unsafe { rocksdb_ffi::rocksdb_iter_next(native_iter) },
Direction::reverse => unsafe { rocksdb_ffi::rocksdb_iter_prev(native_iter) }, Direction::reverse => unsafe { rocksdb_ffi::rocksdb_iter_prev(native_iter) },
} }
} else { } else {
self.iter.just_seeked = false; self.just_seeked = false;
} }
if unsafe { rocksdb_ffi::rocksdb_iter_valid(native_iter) } { if unsafe { rocksdb_ffi::rocksdb_iter_valid(native_iter) } {
let mut key_len: size_t = 0; let mut key_len: size_t = 0;
@ -108,77 +102,80 @@ impl <'a> Iterator for SubDBIterator<'a> {
} }
} }
impl DBIterator { pub enum IteratorMode<'a> {
// TODO alias db & opts to different lifetimes, and DBIterator to the db's Start,
// lifetime End,
fn new(db: &DB, readopts: &ReadOptions) -> DBIterator { From(&'a [u8], Direction),
}
impl<'a> DBIterator<'a> {
fn new<'b>(db: &'a DB, readopts: &'b ReadOptions, mode: IteratorMode) -> DBIterator<'a> {
unsafe { unsafe {
let iterator = rocksdb_ffi::rocksdb_create_iterator(db.inner, let iterator = rocksdb_ffi::rocksdb_create_iterator(db.inner,
readopts.inner); readopts.inner);
rocksdb_ffi::rocksdb_iter_seek_to_first(iterator);
DBIterator { let mut rv = DBIterator {
db: db,
inner: iterator, inner: iterator,
direction: Direction::forward, direction: Direction::forward, // blown away by set_mode()
just_seeked: true, just_seeked: false,
} };
rv.set_mode(mode);
rv
}
}
pub fn set_mode(&mut self, mode: IteratorMode) {
unsafe {
match mode {
IteratorMode::Start => {
rocksdb_ffi::rocksdb_iter_seek_to_first(self.inner);
self.direction = Direction::forward;
},
IteratorMode::End => {
rocksdb_ffi::rocksdb_iter_seek_to_last(self.inner);
self.direction = Direction::reverse;
},
IteratorMode::From(key, dir) => {
rocksdb_ffi::rocksdb_iter_seek(self.inner,
key.as_ptr(),
key.len() as size_t);
self.direction = dir;
}
};
self.just_seeked = true;
} }
} }
fn new_cf(db: &DB, fn new_cf(db: &'a DB,
cf_handle: DBCFHandle, cf_handle: DBCFHandle,
readopts: &ReadOptions) readopts: &ReadOptions,
-> Result<DBIterator, String> { mode: IteratorMode)
-> Result<DBIterator<'a>, String> {
unsafe { unsafe {
let iterator = let iterator =
rocksdb_ffi::rocksdb_create_iterator_cf(db.inner, rocksdb_ffi::rocksdb_create_iterator_cf(db.inner,
readopts.inner, readopts.inner,
cf_handle); cf_handle);
rocksdb_ffi::rocksdb_iter_seek_to_first(iterator);
Ok(DBIterator {
inner: iterator,
direction: Direction::forward,
just_seeked: true,
})
}
}
pub fn from_start(&mut self) -> SubDBIterator { let mut rv = DBIterator {
self.just_seeked = true; db: db,
unsafe { inner: iterator,
rocksdb_ffi::rocksdb_iter_seek_to_first(self.inner); direction: Direction::forward, // blown away by set_mode()
} just_seeked: false,
SubDBIterator { };
iter: self,
direction: Direction::forward,
}
}
pub fn from_end(&mut self) -> SubDBIterator { rv.set_mode(mode);
self.just_seeked = true;
unsafe {
rocksdb_ffi::rocksdb_iter_seek_to_last(self.inner);
}
SubDBIterator {
iter: self,
direction: Direction::reverse,
}
}
pub fn from(&mut self, key: &[u8], dir: Direction) -> SubDBIterator { Ok(rv)
self.just_seeked = true;
unsafe {
rocksdb_ffi::rocksdb_iter_seek(self.inner,
key.as_ptr(),
key.len() as size_t);
}
SubDBIterator {
iter: self,
direction: dir,
} }
} }
} }
impl Drop for DBIterator { impl<'a> Drop for DBIterator<'a> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
rocksdb_ffi::rocksdb_iter_destroy(self.inner); rocksdb_ffi::rocksdb_iter_destroy(self.inner);
@ -197,10 +194,10 @@ impl <'a> Snapshot<'a> {
} }
} }
pub fn iterator(&self) -> DBIterator { pub fn iterator(&self, mode: IteratorMode) -> DBIterator {
let mut readopts = ReadOptions::new(); let mut readopts = ReadOptions::new();
readopts.set_snapshot(self); readopts.set_snapshot(self);
DBIterator::new(self.db, &readopts) DBIterator::new(self.db, &readopts, mode)
} }
} }
@ -512,14 +509,14 @@ impl DB {
self.cfs.get(name) self.cfs.get(name)
} }
pub fn iterator(&self) -> DBIterator { pub fn iterator(&self, mode: IteratorMode) -> DBIterator {
let opts = ReadOptions::new(); let opts = ReadOptions::new();
DBIterator::new(&self, &opts) DBIterator::new(&self, &opts, mode)
} }
pub fn iterator_cf(&self, cf_handle: DBCFHandle) -> Result<DBIterator, String> { pub fn iterator_cf(&self, cf_handle: DBCFHandle, mode: IteratorMode) -> Result<DBIterator, String> {
let opts = ReadOptions::new(); let opts = ReadOptions::new();
DBIterator::new_cf(&self, cf_handle, &opts) DBIterator::new_cf(&self, cf_handle, &opts, mode)
} }
pub fn snapshot(&self) -> Snapshot { pub fn snapshot(&self) -> Snapshot {
@ -891,8 +888,8 @@ fn iterator_test() {
assert!(p.is_ok()); assert!(p.is_ok());
let p = db.put(b"k3", b"v3333"); let p = db.put(b"k3", b"v3333");
assert!(p.is_ok()); assert!(p.is_ok());
let mut iter = db.iterator(); let mut iter = db.iterator(IteratorMode::Start);
for (k, v) in iter.from_start() { for (k, v) in iter {
println!("Hello {}: {}", println!("Hello {}: {}",
from_utf8(&*k).unwrap(), from_utf8(&*k).unwrap(),
from_utf8(&*v).unwrap()); from_utf8(&*v).unwrap());

@ -1,4 +1,4 @@
use rocksdb::{DB, Direction, Options, Writable}; use rocksdb::{DB, Direction, Options, Writable, IteratorMode};
fn cba(input: &Box<[u8]>) -> Box<[u8]> { fn cba(input: &Box<[u8]>) -> Box<[u8]> {
input.iter().cloned().collect::<Vec<_>>().into_boxed_slice() input.iter().cloned().collect::<Vec<_>>().into_boxed_slice()
@ -23,92 +23,90 @@ pub fn test_iterator() {
assert!(p.is_ok()); assert!(p.is_ok());
let p = db.put(&*k3, &*v3); let p = db.put(&*k3, &*v3);
assert!(p.is_ok()); assert!(p.is_ok());
let mut view1 = db.iterator();
let expected = vec![(cba(&k1), cba(&v1)), let expected = vec![(cba(&k1), cba(&v1)),
(cba(&k2), cba(&v2)), (cba(&k2), cba(&v2)),
(cba(&k3), cba(&v3))]; (cba(&k3), cba(&v3))];
{ {
let iterator1 = view1.from_start(); let iterator1 = db.iterator(IteratorMode::Start);
assert_eq!(iterator1.collect::<Vec<_>>(), expected); assert_eq!(iterator1.collect::<Vec<_>>(), expected);
} }
// Test that it's reusable a few times // Test that it's idempotent
{ {
let iterator1 = view1.from_start(); let iterator1 = db.iterator(IteratorMode::Start);
assert_eq!(iterator1.collect::<Vec<_>>(), expected); assert_eq!(iterator1.collect::<Vec<_>>(), expected);
} }
{ {
let iterator1 = view1.from_start(); let iterator1 = db.iterator(IteratorMode::Start);
assert_eq!(iterator1.collect::<Vec<_>>(), expected); assert_eq!(iterator1.collect::<Vec<_>>(), expected);
} }
{ {
let iterator1 = view1.from_start(); let iterator1 = db.iterator(IteratorMode::Start);
assert_eq!(iterator1.collect::<Vec<_>>(), expected); assert_eq!(iterator1.collect::<Vec<_>>(), expected);
} }
// Test it in reverse a few times // Test it in reverse a few times
{ {
let iterator1 = view1.from_end(); let iterator1 = db.iterator(IteratorMode::End);
let mut tmp_vec = iterator1.collect::<Vec<_>>(); let mut tmp_vec = iterator1.collect::<Vec<_>>();
tmp_vec.reverse(); tmp_vec.reverse();
assert_eq!(tmp_vec, expected); assert_eq!(tmp_vec, expected);
} }
{ {
let iterator1 = view1.from_end(); let iterator1 = db.iterator(IteratorMode::End);
let mut tmp_vec = iterator1.collect::<Vec<_>>(); let mut tmp_vec = iterator1.collect::<Vec<_>>();
tmp_vec.reverse(); tmp_vec.reverse();
assert_eq!(tmp_vec, expected); assert_eq!(tmp_vec, expected);
} }
{ {
let iterator1 = view1.from_end(); let iterator1 = db.iterator(IteratorMode::End);
let mut tmp_vec = iterator1.collect::<Vec<_>>(); let mut tmp_vec = iterator1.collect::<Vec<_>>();
tmp_vec.reverse(); tmp_vec.reverse();
assert_eq!(tmp_vec, expected); assert_eq!(tmp_vec, expected);
} }
{ {
let iterator1 = view1.from_end(); let iterator1 = db.iterator(IteratorMode::End);
let mut tmp_vec = iterator1.collect::<Vec<_>>(); let mut tmp_vec = iterator1.collect::<Vec<_>>();
tmp_vec.reverse(); tmp_vec.reverse();
assert_eq!(tmp_vec, expected); assert_eq!(tmp_vec, expected);
} }
{ {
let iterator1 = view1.from_end(); let iterator1 = db.iterator(IteratorMode::End);
let mut tmp_vec = iterator1.collect::<Vec<_>>(); let mut tmp_vec = iterator1.collect::<Vec<_>>();
tmp_vec.reverse(); tmp_vec.reverse();
assert_eq!(tmp_vec, expected); assert_eq!(tmp_vec, expected);
} }
// Try it forward again // Try it forward again
{ {
let iterator1 = view1.from_start(); let iterator1 = db.iterator(IteratorMode::Start);
assert_eq!(iterator1.collect::<Vec<_>>(), expected); assert_eq!(iterator1.collect::<Vec<_>>(), expected);
} }
{ {
let iterator1 = view1.from_start(); let iterator1 = db.iterator(IteratorMode::Start);
assert_eq!(iterator1.collect::<Vec<_>>(), expected); assert_eq!(iterator1.collect::<Vec<_>>(), expected);
} }
let old_iterator = db.iterator(IteratorMode::Start);
let p = db.put(&*k4, &*v4); let p = db.put(&*k4, &*v4);
assert!(p.is_ok()); assert!(p.is_ok());
let mut view3 = db.iterator();
let expected2 = vec![(cba(&k1), cba(&v1)), let expected2 = vec![(cba(&k1), cba(&v1)),
(cba(&k2), cba(&v2)), (cba(&k2), cba(&v2)),
(cba(&k3), cba(&v3)), (cba(&k3), cba(&v3)),
(cba(&k4), cba(&v4))]; (cba(&k4), cba(&v4))];
{ {
let iterator1 = view1.from_start(); assert_eq!(old_iterator.collect::<Vec<_>>(), expected);
assert_eq!(iterator1.collect::<Vec<_>>(), expected);
} }
{ {
let iterator1 = view3.from_start(); let iterator1 = db.iterator(IteratorMode::Start);
assert_eq!(iterator1.collect::<Vec<_>>(), expected2); assert_eq!(iterator1.collect::<Vec<_>>(), expected2);
} }
{ {
let iterator1 = view3.from(b"k2", Direction::forward); let iterator1 = db.iterator(IteratorMode::From(b"k2", Direction::forward));
let expected = vec![(cba(&k2), cba(&v2)), let expected = vec![(cba(&k2), cba(&v2)),
(cba(&k3), cba(&v3)), (cba(&k3), cba(&v3)),
(cba(&k4), cba(&v4))]; (cba(&k4), cba(&v4))];
assert_eq!(iterator1.collect::<Vec<_>>(), expected); assert_eq!(iterator1.collect::<Vec<_>>(), expected);
} }
{ {
let iterator1 = view3.from(b"k2", Direction::reverse); let iterator1 = db.iterator(IteratorMode::From(b"k2", Direction::reverse));
let expected = vec![(cba(&k2), cba(&v2)), (cba(&k1), cba(&v1))]; let expected = vec![(cba(&k2), cba(&v2)), (cba(&k1), cba(&v1))];
assert_eq!(iterator1.collect::<Vec<_>>(), expected); assert_eq!(iterator1.collect::<Vec<_>>(), expected);
} }

Loading…
Cancel
Save