12个版本
0.4.7 | 2024年7月2日 |
---|---|
0.4.5 | 2023年11月1日 |
0.4.4 | 2022年11月16日 |
0.4.2 | 2022年6月1日 |
0.2.0 | 2021年7月28日 |
#68 in 压缩
3,846次每月下载
在grenad中使用
1MB
3K SLoC
Grenad
排序、合并、写入和读取不可变键值对的工具。
lib.rs
:
这个库提供了排序、合并、写入和读取不可变键值对的工具。grenad文件中的条目是不可变的,唯一修改它们的方式是创建一个包含更改的新文件。
功能
您可以选择支持哪些压缩方案,目前有几种可供选择,这些决定了上面模块中可用的类型
如果您需要更好的性能,可以启用 rayon
功能,这将启用一系列新设置,例如使 Sorter
能够并行排序。
示例
使用 Writer
和 Reader
结构体
您可以使用 Writer
结构体将键值对存储到指定的 std::io::Write
类型。然后可以使用 Reader
类型来读取条目。
提供给 Writer
结构体的条目必须以字典序给出。
use std::io::Cursor;
use grenad::{Reader, Writer};
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 jum 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);
使用 Merger
结构体
在这个示例中,我们展示了如何使用合并函数在遇到冲突时合并多个 Reader
。
Merger
结构体生成的条目以字典序返回,将它们写回一个新的 Writer
是一个好方法。
use std::array::TryFromSliceError;
use std::borrow::Cow;
use std::convert::TryInto;
use std::io::Cursor;
use grenad::{MergerBuilder, Reader, Writer};
// This merge function:
// - parses u32s from native-endian bytes,
// - wrapping sums them and,
// - outputs the result as native-endian bytes.
fn wrapping_sum_u32s<'a>(
_key: &[u8],
values: &[Cow<'a, [u8]>],
) -> Result<Cow<'a, [u8]>, TryFromSliceError>
{
let mut output: u32 = 0;
for bytes in values.iter().map(AsRef::as_ref) {
let num = bytes.try_into().map(u32::from_ne_bytes)?;
output = output.wrapping_add(num);
}
Ok(Cow::Owned(output.to_ne_bytes().to_vec()))
}
// We create our writers in memory to insert our key-value pairs.
let mut writera = Writer::memory();
let mut writerb = Writer::memory();
let mut writerc = Writer::memory();
// We insert our key-value pairs in lexicographic order
// and mix them between our writers.
writera.insert("first-counter", 32_u32.to_ne_bytes())?;
writera.insert("second-counter", 64_u32.to_ne_bytes())?;
writerb.insert("first-counter", 23_u32.to_ne_bytes())?;
writerb.insert("second-counter", 320_u32.to_ne_bytes())?;
writerc.insert("first-counter", 64_u32.to_ne_bytes())?;
// We create readers from our writers.
let cursora = writera.into_inner().map(Cursor::new)?;
let cursorb = writerb.into_inner().map(Cursor::new)?;
let cursorc = writerc.into_inner().map(Cursor::new)?;
let readera = Reader::new(cursora)?.into_cursor()?;
let readerb = Reader::new(cursorb)?.into_cursor()?;
let readerc = Reader::new(cursorc)?.into_cursor()?;
// We create a merger that will sum our u32s when necessary,
// and we add our readers to the list of readers to merge.
let merger_builder = MergerBuilder::new(wrapping_sum_u32s);
let merger = merger_builder.add(readera).add(readerb).add(readerc).build();
// We can iterate over the entries in key-order.
let mut iter = merger.into_stream_merger_iter()?;
// We can see that the sum of u32s is valid here.
assert_eq!(iter.next()?, Some((&b"first-counter"[..], &119_u32.to_ne_bytes()[..])));
assert_eq!(iter.next()?, Some((&b"second-counter"[..], &384_u32.to_ne_bytes()[..])));
assert_eq!(iter.next()?, None);
使用 Sorter
结构体
在这个例子中,我们展示了如何通过定义一个合并函数,我们可以插入具有相同键的多个条目,并以字典顺序输出它们。
Sorter
接受任何给定顺序的条目,将在内存中重新排序它们,并在需要时使用合并函数合并它们。它在构建过程中有权拥有内存预算,并会尽可能遵循该预算。
use std::array::TryFromSliceError;
use std::borrow::Cow;
use std::convert::TryInto;
use grenad::{CursorVec, SorterBuilder};
// This merge function:
// - parses u32s from native-endian bytes,
// - wrapping sums them and,
// - outputs the result as native-endian bytes.
fn wrapping_sum_u32s<'a>(
_key: &[u8],
values: &[Cow<'a, [u8]>],
) -> Result<Cow<'a, [u8]>, TryFromSliceError>
{
let mut output: u32 = 0;
for bytes in values.iter().map(AsRef::as_ref) {
let num = bytes.try_into().map(u32::from_ne_bytes)?;
output = output.wrapping_add(num);
}
Ok(Cow::Owned(output.to_ne_bytes().to_vec()))
}
// We create a sorter that will sum our u32s when necessary.
let mut sorter = SorterBuilder::new(wrapping_sum_u32s).chunk_creator(CursorVec).build();
// We insert multiple entries with the same key but different values
// in arbitrary order, the sorter will take care of merging them for us.
sorter.insert("first-counter", 32_u32.to_ne_bytes())?;
sorter.insert("first-counter", 23_u32.to_ne_bytes())?;
sorter.insert("first-counter", 64_u32.to_ne_bytes())?;
sorter.insert("second-counter", 320_u32.to_ne_bytes())?;
sorter.insert("second-counter", 64_u32.to_ne_bytes())?;
// We can iterate over the entries in key-order.
let mut iter = sorter.into_stream_merger_iter()?;
// We can see that the sum of u32s is valid here.
assert_eq!(iter.next()?, Some((&b"first-counter"[..], &119_u32.to_ne_bytes()[..])));
assert_eq!(iter.next()?, Some((&b"second-counter"[..], &384_u32.to_ne_bytes()[..])));
assert_eq!(iter.next()?, None);
依赖关系
~2–12MB
~148K SLoC