This version v0.4.0 brings a new era for the grenad crate: The ability to move on the keys of a grenad file.
Grenad is a library that provides tools to sort, merge, write, and read immutable key-value pairs. It is highly inspired by the SSTable format of RocksDB and by the MTBL c library.
let mut writer = Writer::memory();
// We insert our key-value pairs in lexicographic order.
writer.insert("first-counter", 119_u32.to_ne_bytes())?;
writer.insert("second-counter", 384_u32.to_ne_bytes())?;
// We create a reader from our writer.
let cursor = writer.into_inner().map(Cursor::new)?;
let mut cursor = Reader::new(cursor)?.into_cursor()?;
// We can see that the sum of u32s is valid here.
assert_eq!(cursor.move_on_next()?, Some((&b"first-counter"[..], &119_u32.to_ne_bytes()[..])));
assert_eq!(cursor.move_on_next()?, Some((&b"second-counter"[..], &384_u32.to_ne_bytes()[..])));
assert_eq!(cursor.move_on_next()?, None);
// We can also jump on any given entry.
assert_eq!(cursor.move_on_key_greater_than_or_equal_to("first")?, Some((&b"first-counter"[..], &119_u32.to_ne_bytes()[..])));
assert_eq!(cursor.move_on_key_equal_to("second-counter")?, Some((&b"second-counter"[..], &384_u32.to_ne_bytes()[..])));
assert_eq!(cursor.move_on_key_lower_than_or_equal_to("abracadabra")?, None);
It is now possible to use the ReaderCursor
methods:
move_on_first
: to move on the first entry of a grenad file.move_on_last
: to move on the last entry of a grenad file.move_on_next
: to move on the entry that is lying after the currently pointed one. Automatically moves on the first key when the cursor is uninitialized.move_on_prev
: to move on the entry that is lying before the currently pointed one. Automatically moves on the last key when the cursor is uninitialized.move_on_key_lower_than_or_equal_to
: to move on an entry that is either equal or lower than the given one. ReturnsNone
if no entry validates these predicates.move_on_key_greater_than_or_equal_to
: to move on an entry that is either equal or greater than the given one. ReturnsNone
if no entry validates these predicates.move_on_key_equal_to
: to move on an entry that is exactly equal to the given key. ReturnsNone
if no entry validates these predicates.
Along with these changes, we bring a small but, yet, useful method to get the exact number of entries in a grenad file.
let mut writer = Writer::memory();
// We insert our key-value pairs in lexicographic order.
writer.insert("aaa-cat-counter", 121_u32.to_ne_bytes())?;
writer.insert("aaa-cow-counter", 456_u32.to_ne_bytes())?;
writer.insert("aaa-dog-counter", 101_u32.to_ne_bytes())?;
writer.insert("aaa-horse-counter", 001_u32.to_ne_bytes())?;
writer.insert("bbb-turtoise-counter", 121_u32.to_ne_bytes())?;
// We create a reader from our writer.
let cursor = writer.into_inner().map(Cursor::new)?;
let mut prefix_iter = Reader::new(cursor)?.into_prefix_iter(b"aaa".to_vec()])?;
// We can see that the sum of u32s is valid here.
assert_eq!(cursor.next()?, Some((&b"aaa-cat-counter"[..], &121_u32.to_ne_bytes()[..])));
assert_eq!(cursor.next()?, Some((&b"aaa-cow-counter"[..], &456_u32.to_ne_bytes()[..])));
assert_eq!(cursor.next()?, Some((&b"aaa-dog-counter"[..], &101_u32.to_ne_bytes()[..])));
assert_eq!(cursor.next()?, Some((&b"aaa-horse-counter"[..], &001_u32.to_ne_bytes()[..])));
assert_eq!(cursor.next()?, None);
The ReaderCursor
struct is very useful to move freely between the entries of a grenad file,
but we also bring a set of iterators to help us get the job done:
PrefixIter
: let you iterate on entry where the keys start with a given prefix.RevPrefixIter
: same as thePrefixIter
but moves from the last key that starts with the given prefix to the first one.RangeIter
: let you iterate on keys in a given range defined by theRangebounds
trait.RevRangeIter
: same as theRangeIter
but in reverse order.
Non-idiomatic Iterators and Cursors
As grenad uses a compression and decompression system and we prefer keeping good performances we can't use the idiomatic Iterator
trait for the same reason that we can't declare a Lines
iterator that yields &str
and that this kind of iterator must return owned values.
The grenad ReaderCursor
returns &[u8]
for the key and values that are views into the internal buffer of the cursor. When the GAT's are released we will be able to maybe declare our iterators by using something like a StreamingIterator
trait or something similar.