Crates.io | content-tree |
lib.rs | content-tree |
version | 0.2.0 |
source | src |
created_at | 2021-09-14 09:51:17.18526 |
updated_at | 2022-08-25 15:33:24.900461 |
description | An efficient data structure for compacted RLE data |
homepage | |
repository | https://github.com/josephg/diamond-types |
max_upload_size | |
id | 451159 |
size | 172,203 |
This is a fancy data structure for managing packed run-length encoded data. Its like:
This library is currently implemented as a b-tree, but I have plans to rewrite it using skip lists a la jumprope. I believe this will make the code smaller and improve performance.
TODO: This library does not get the tick of approval from MIRI. I will add that during the skip list oriented rewrite.
Lets say you want to store RLE runs of bits. We could make our own SplitableSpan RLE type for our data, but in this case we can use the builtin RleRun<bool>
type:
use content_tree::{ContentTree, RleRun};
fn main() {
let mut list = ContentTree::new();
list.push(RleRun { val: false, len: 10 });
// Insert in the middle (at offset 5) in the run of 10 items:
list.insert_at_offset(5, RleRun { val: true, len: 2 });
println!("List contains {:?}", list.iter().collect::<Vec<RleRun<bool>>>());
// List contains [
// RleRun { val: false, len: 5 },
// RleRun { val: true, len: 2 },
// RleRun { val: false, len: 5 }
// ]
}
But you aren't limited to simple runs of items. Lets suppose you want to store auto-compacting ranges of identifiers. We can make a custom struct for that, so long as it implements:
For a range type, our implementation would look something like this:
#[derive(Debug, Clone, Copy, Default)]
struct RleRange {
// Sadly we can't just embed a Range because it doesn't implement
// Copy. And Copy is needed for ContentTree.
start: usize,
end: usize,
}
impl SplitableSpan for RleRange {
fn len(&self) -> usize { self.end - self.start }
fn truncate(&mut self, at: usize) -> Self {
let old_end = self.end;
// Truncate self to [start..start+at)
self.end = self.start + at;
// And return the trimmed remainder
RleRange { start: self.end, end: old_end }
}
fn can_append(&self, other: &Self) -> bool {
self.end == other.start
}
fn append(&mut self, other: Self) {
self.end = other.end;
}
}
Then you can make a ContentTree using your type:
fn main() {
let mut list = ContentTree::new();
list.push(RleRange { start: 0, end: 15 });
list.push(RleRange { start: 15, end: 20 });
// Both items are automatically merged!
println!("List contains {:?}", list.iter().collect::<Vec<RleRange>>());
// List contains [RleRange { start: 0, end: 20 }]
}
See examples/custom_entry.rs for a fully worked example.
This code was implemented as part of diamond types.