Skip to content

Commit ae98ddd

Browse files
authored
Merge pull request #20 from Kerollmops/improve-intro-doc
Improve the introduction documentation
2 parents d87bba5 + 6f8de49 commit ae98ddd

File tree

7 files changed

+53
-43
lines changed

7 files changed

+53
-43
lines changed

.github/workflows/rust.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
- uses: actions-rs/cargo@v1
3333
with:
3434
command: test
35-
args: ${{ matrix.features }}
35+
args: --all-features
3636

3737
lint:
3838
runs-on: ubuntu-latest

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "grenad"
3-
description = "Simple sorted string table"
3+
description = "Tools to sort, merge, write, and read immutable key-value pairs."
44
version = "0.3.1"
55
authors = ["Kerollmops <clement@meilisearch.com>"]
66
repository = "https://github.com/Kerollmops/grenad"

README.md

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
This library provides different ways to sort, merge, write,
2-
and read key-value pairs efficiently.
1+
This library provides tools to sort, merge, write, and read immutable key-value pairs.
2+
The entries in the grenad files are _immutable_ and the only way to modify them is by _creating
3+
a new file_ with the changes.
34

4-
# Example: use the Writer and Reader structs
5+
# Example: Use the `Writer` and `Reader` structs
56

6-
You can use the [`Writer`] struct to store key-value pairs
7-
into the specified [`std::io::Write`] type. The [`Reader`] type
8-
can then be used to read the entries.
7+
You can use the [`Writer`] struct to store key-value pairs into the specified
8+
[`std::io::Write`] type. The [`Reader`] type can then be used to read the entries.
9+
10+
The entries provided to the [`Writer`] struct must be given in lexicographic order.
911

1012
```rust
1113
use std::io::Cursor;
@@ -14,24 +16,34 @@ use grenad::{Reader, Writer};
1416

1517
# fn main() -> Result<(), Box<dyn std::error::Error>> {
1618
let mut writer = Writer::memory();
19+
20+
// We insert our key-value pairs in lexicographic order.
1721
writer.insert("first-counter", 119_u32.to_ne_bytes())?;
1822
writer.insert("second-counter", 384_u32.to_ne_bytes())?;
1923

2024
// We create a reader from our writer.
2125
let cursor = writer.into_inner().map(Cursor::new)?;
22-
let mut reader = Reader::new(cursor)?.into_cursor()?;
26+
let mut cursor = Reader::new(cursor)?.into_cursor()?;
2327

2428
// We can see that the sum of u32s is valid here.
25-
assert_eq!(reader.move_on_next()?, Some((&b"first-counter"[..], &119_u32.to_ne_bytes()[..])));
26-
assert_eq!(reader.move_on_next()?, Some((&b"second-counter"[..], &384_u32.to_ne_bytes()[..])));
27-
assert_eq!(reader.move_on_next()?, None);
29+
assert_eq!(cursor.move_on_next()?, Some((&b"first-counter"[..], &119_u32.to_ne_bytes()[..])));
30+
assert_eq!(cursor.move_on_next()?, Some((&b"second-counter"[..], &384_u32.to_ne_bytes()[..])));
31+
assert_eq!(cursor.move_on_next()?, None);
32+
33+
// We can also jum on any given entry.
34+
assert_eq!(cursor.move_on_key_greater_than_or_equal_to("first")?, Some((&b"first-counter"[..], &119_u32.to_ne_bytes()[..])));
35+
assert_eq!(cursor.move_on_key_equal_to("second-counter")?, Some((&b"second-counter"[..], &384_u32.to_ne_bytes()[..])));
36+
assert_eq!(cursor.move_on_key_lower_than_or_equal_to("abracadabra")?, None);
2837
# Ok(()) }
2938
```
3039

31-
# Example: use the Merger struct
40+
# Example: Use the `Merger` struct
3241

3342
In this example we show how you can merge multiple [`Reader`]s
34-
by using a merge function when a conflict is encountered.
43+
by using a _merge function_ when a conflict is encountered.
44+
45+
The entries yielded by the [`Merger`] struct are returned in lexicographic order,
46+
a good way to write them back into a new [`Writer`].
3547

3648
```rust
3749
use std::array::TryFromSliceError;
@@ -64,8 +76,8 @@ let mut writera = Writer::memory();
6476
let mut writerb = Writer::memory();
6577
let mut writerc = Writer::memory();
6678

67-
// We insert our key-value pairs in order and
68-
// mix them between our writers.
79+
// We insert our key-value pairs in lexicographic order
80+
// and mix them between our writers.
6981
writera.insert("first-counter", 32_u32.to_ne_bytes())?;
7082
writera.insert("second-counter", 64_u32.to_ne_bytes())?;
7183
writerb.insert("first-counter", 23_u32.to_ne_bytes())?;
@@ -95,11 +107,14 @@ assert_eq!(iter.next()?, None);
95107
# Ok(()) }
96108
```
97109

98-
# Example: use the Sorter struct
110+
# Example: Use the `Sorter` struct
99111

100-
In this example we show how by defining a merge function,
101-
you can insert multiple entries with the same key and output
102-
them in key-order.
112+
In this example we show how by defining a _merge function_, we can insert
113+
multiple entries with the same key and output them in lexicographic order.
114+
115+
The [`Sorter`] accepts the entries in any given order, will reorder them in-memory and
116+
merge them with the _merge function_ when required. It is authorized to have a memory budget
117+
during its construction and will try to follow it as closely as possible.
103118

104119
```rust
105120
use std::array::TryFromSliceError;
@@ -145,6 +160,5 @@ let mut iter = sorter.into_stream_merger_iter()?;
145160
assert_eq!(iter.next()?, Some((&b"first-counter"[..], &119_u32.to_ne_bytes()[..])));
146161
assert_eq!(iter.next()?, Some((&b"second-counter"[..], &384_u32.to_ne_bytes()[..])));
147162
assert_eq!(iter.next()?, None);
148-
149163
# Ok(()) }
150164
```

src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#[macro_use]
66
extern crate quickcheck;
77

8+
use std::mem;
9+
810
mod block;
911
mod block_writer;
1012
mod compression;
@@ -26,3 +28,11 @@ pub use self::reader::{PrefixIter, RangeIter, Reader, ReaderCursor, RevPrefixIte
2628
pub use self::sorter::TempFileChunk;
2729
pub use self::sorter::{ChunkCreator, CursorVec, DefaultChunkCreator, Sorter, SorterBuilder};
2830
pub use self::writer::{Writer, WriterBuilder};
31+
32+
/// Sometimes we need to use an unsafe trick to make the compiler happy.
33+
/// You can read more about the issue [on the Rust's Github issues].
34+
///
35+
/// [on the Rust's Github issues]: https://github.com/rust-lang/rust/issues/47680
36+
unsafe fn transmute_entry_to_static(key: &[u8], val: &[u8]) -> (&'static [u8], &'static [u8]) {
37+
(mem::transmute(key), mem::transmute(val))
38+
}

src/reader/range_iter.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
use std::io;
12
use std::ops::{Bound, RangeBounds};
2-
use std::{io, mem};
33

44
use crate::{Error, ReaderCursor};
55

@@ -46,10 +46,7 @@ impl<R: io::Read + io::Seek> RangeIter<R> {
4646

4747
match entry {
4848
Some((key, val)) if end_contains(self.range.end_bound(), key) => {
49-
// This is a trick to make the compiler happy...
50-
// https://github.com/rust-lang/rust/issues/47680
51-
let key: &'static _ = unsafe { mem::transmute(key) };
52-
let val: &'static _ = unsafe { mem::transmute(val) };
49+
let (key, val) = unsafe { crate::transmute_entry_to_static(key, val) };
5350
Ok(Some((key, val)))
5451
}
5552
_otherwise => Ok(None),
@@ -98,10 +95,7 @@ impl<R: io::Read + io::Seek> RevRangeIter<R> {
9895

9996
match entry {
10097
Some((key, val)) if start_contains(self.range.start_bound(), key) => {
101-
// This is a trick to make the compiler happy...
102-
// https://github.com/rust-lang/rust/issues/47680
103-
let key: &'static _ = unsafe { mem::transmute(key) };
104-
let val: &'static _ = unsafe { mem::transmute(val) };
98+
let (key, val) = unsafe { crate::transmute_entry_to_static(key, val) };
10599
Ok(Some((key, val)))
106100
}
107101
_otherwise => Ok(None),

src/reader/reader_cursor.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::convert::TryInto;
2+
use std::io;
23
use std::io::SeekFrom;
34
use std::ops::Deref;
4-
use std::{io, mem};
55

66
use crate::reader::{Block, BlockCursor};
77
use crate::{Error, Reader};
@@ -112,10 +112,7 @@ impl<R: io::Read + io::Seek> ReaderCursor<R> {
112112
pub fn move_on_next(&mut self) -> Result<Option<(&[u8], &[u8])>, Error> {
113113
match self.current_cursor.as_mut().map(BlockCursor::move_on_next) {
114114
Some(Some((key, val))) => {
115-
// This is a trick to make the compiler happy...
116-
// https://github.com/rust-lang/rust/issues/47680
117-
let key: &'static _ = unsafe { mem::transmute(key) };
118-
let val: &'static _ = unsafe { mem::transmute(val) };
115+
let (key, val) = unsafe { crate::transmute_entry_to_static(key, val) };
119116
Ok(Some((key, val)))
120117
}
121118
Some(None) => match self.next_block_from_index()?.map(Block::into_cursor) {
@@ -133,10 +130,7 @@ impl<R: io::Read + io::Seek> ReaderCursor<R> {
133130
pub fn move_on_prev(&mut self) -> Result<Option<(&[u8], &[u8])>, Error> {
134131
match self.current_cursor.as_mut().map(BlockCursor::move_on_prev) {
135132
Some(Some((key, val))) => {
136-
// This is a trick to make the compiler happy...
137-
// https://github.com/rust-lang/rust/issues/47680
138-
let key: &'static _ = unsafe { mem::transmute(key) };
139-
let val: &'static _ = unsafe { mem::transmute(val) };
133+
let (key, val) = unsafe { crate::transmute_entry_to_static(key, val) };
140134
Ok(Some((key, val)))
141135
}
142136
Some(None) => match self.prev_block_from_index()?.map(Block::into_cursor) {
@@ -159,10 +153,7 @@ impl<R: io::Read + io::Seek> ReaderCursor<R> {
159153
let target_key = target_key.as_ref();
160154
match self.move_on_key_greater_than_or_equal_to(target_key)? {
161155
Some((key, val)) if key == target_key => {
162-
// This is a trick to make the compiler happy...
163-
// https://github.com/rust-lang/rust/issues/47680
164-
let key: &'static _ = unsafe { mem::transmute(key) };
165-
let val: &'static _ = unsafe { mem::transmute(val) };
156+
let (key, val) = unsafe { crate::transmute_entry_to_static(key, val) };
166157
Ok(Some((key, val)))
167158
}
168159
Some(_) => self.move_on_prev(),

src/sorter.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::alloc::{alloc, dealloc, Layout};
22
use std::borrow::Cow;
33
use std::convert::Infallible;
4+
#[cfg(feature = "tempfile")]
45
use std::fs::File;
56
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
67
use std::mem::{align_of, size_of};

0 commit comments

Comments
 (0)