automerge/rust/automerge/src/columnar/column_range/deps.rs
Alex Good dd3c6d1303
Move rust workspace into ./rust
After some discussion with PVH I realise that the repo structure in the
last reorg was very rust-centric. In an attempt to put each language on
a level footing move the rust code and project files into ./rust
2022-10-16 19:55:51 +01:00

119 lines
3.5 KiB
Rust

use super::{DeltaRange, RleRange};
use crate::columnar::encoding::{DecodeColumnError, DeltaDecoder, RleDecoder};
/// A grouped column containing lists of u64s
#[derive(Clone, Debug)]
pub(crate) struct DepsRange {
num: RleRange<u64>,
deps: DeltaRange,
}
impl DepsRange {
pub(crate) fn new(num: RleRange<u64>, deps: DeltaRange) -> Self {
Self { num, deps }
}
pub(crate) fn num_range(&self) -> &RleRange<u64> {
&self.num
}
pub(crate) fn deps_range(&self) -> &DeltaRange {
&self.deps
}
pub(crate) fn encode<I, II>(deps: I, out: &mut Vec<u8>) -> DepsRange
where
I: Iterator<Item = II> + Clone,
II: IntoIterator<Item = u64> + ExactSizeIterator,
{
let num = RleRange::encode(deps.clone().map(|d| Some(d.len() as u64)), out);
let deps = DeltaRange::encode(
deps.flat_map(|d| d.into_iter().map(|d| Some(d as i64))),
out,
);
DepsRange { num, deps }
}
pub(crate) fn iter<'a>(&self, data: &'a [u8]) -> DepsIter<'a> {
DepsIter {
num: self.num.decoder(data),
deps: self.deps.decoder(data),
}
}
}
#[derive(Clone)]
pub(crate) struct DepsIter<'a> {
num: RleDecoder<'a, u64>,
deps: DeltaDecoder<'a>,
}
impl<'a> DepsIter<'a> {
fn try_next(&mut self) -> Result<Option<Vec<u64>>, DecodeColumnError> {
let num = match self
.num
.next()
.transpose()
.map_err(|e| DecodeColumnError::decode_raw("num", e))?
{
Some(Some(n)) => n as usize,
Some(None) => {
return Err(DecodeColumnError::unexpected_null("group"));
}
None => return Ok(None),
};
let mut result = Vec::with_capacity(num);
while result.len() < num {
match self
.deps
.next()
.transpose()
.map_err(|e| DecodeColumnError::decode_raw("deps", e))?
{
Some(Some(elem)) => {
let elem = match u64::try_from(elem) {
Ok(e) => e,
Err(e) => {
tracing::error!(err=?e, dep=elem, "error converting dep index to u64");
return Err(DecodeColumnError::invalid_value(
"deps",
"error converting dep index to u64",
));
}
};
result.push(elem);
}
_ => return Err(DecodeColumnError::unexpected_null("deps")),
}
}
Ok(Some(result))
}
}
impl<'a> Iterator for DepsIter<'a> {
type Item = Result<Vec<u64>, DecodeColumnError>;
fn next(&mut self) -> Option<Self::Item> {
self.try_next().transpose()
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::collection::vec as propvec;
use proptest::prelude::*;
fn encodable_u64() -> impl Strategy<Value = u64> + Clone {
0_u64..((i64::MAX / 2) as u64)
}
proptest! {
#[test]
fn encode_decode_deps(deps in propvec(propvec(encodable_u64(), 0..100), 0..100)) {
let mut out = Vec::new();
let range = DepsRange::encode(deps.iter().cloned().map(|d| d.into_iter()), &mut out);
let decoded = range.iter(&out).collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(deps, decoded);
}
}
}