automerge/rust/automerge/src/columnar/encoding/boolean.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

131 lines
3.2 KiB
Rust

use std::borrow::Cow;
use super::{raw, Encodable, RawDecoder, Sink};
/// Encodes booleans by storing the count of the same value.
///
/// The sequence of numbers describes the count of false values on even indices (0-indexed) and the
/// count of true values on odd indices (0-indexed).
///
/// Counts are encoded as usize.
pub(crate) struct BooleanEncoder<S> {
written: usize,
//buf: &'a mut Vec<u8>,
buf: S,
last: bool,
count: usize,
}
impl BooleanEncoder<Vec<u8>> {
pub(crate) fn new() -> BooleanEncoder<Vec<u8>> {
BooleanEncoder::from_sink(Vec::new())
}
}
impl<S: Sink> BooleanEncoder<S> {
pub(crate) fn from_sink(sink: S) -> Self {
BooleanEncoder {
written: 0,
buf: sink,
last: false,
count: 0,
}
}
pub(crate) fn append(&mut self, value: bool) {
if value == self.last {
self.count += 1;
} else {
self.written += self.count.encode(&mut self.buf);
self.last = value;
self.count = 1;
}
}
pub(crate) fn finish(mut self) -> (S, usize) {
if self.count > 0 {
self.written += self.count.encode(&mut self.buf);
}
(self.buf, self.written)
}
}
impl<S: Sink> From<S> for BooleanEncoder<S> {
fn from(output: S) -> Self {
BooleanEncoder::from_sink(output)
}
}
/// See the discussion of [`BooleanEncoder`] for details on this encoding
#[derive(Clone, Debug)]
pub(crate) struct BooleanDecoder<'a> {
decoder: RawDecoder<'a>,
last_value: bool,
count: usize,
}
impl<'a> From<Cow<'a, [u8]>> for BooleanDecoder<'a> {
fn from(bytes: Cow<'a, [u8]>) -> Self {
BooleanDecoder {
decoder: RawDecoder::from(bytes),
last_value: true,
count: 0,
}
}
}
impl<'a> From<&'a [u8]> for BooleanDecoder<'a> {
fn from(d: &'a [u8]) -> Self {
Cow::Borrowed(d).into()
}
}
// this is an endless iterator that returns false after input is exhausted
impl<'a> Iterator for BooleanDecoder<'a> {
type Item = Result<bool, raw::Error>;
fn next(&mut self) -> Option<Self::Item> {
while self.count == 0 {
if self.decoder.done() && self.count == 0 {
return None;
}
self.count = match self.decoder.read() {
Ok(c) => c,
Err(e) => return Some(Err(e)),
};
self.last_value = !self.last_value;
}
self.count -= 1;
Some(Ok(self.last_value))
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
fn encode(vals: &[bool]) -> Vec<u8> {
let mut buf = Vec::new();
let mut encoder = BooleanEncoder::from_sink(&mut buf);
for val in vals {
encoder.append(*val);
}
encoder.finish();
buf
}
fn decode(buf: &[u8]) -> Vec<bool> {
BooleanDecoder::from(buf)
.collect::<Result<Vec<_>, _>>()
.unwrap()
}
proptest! {
#[test]
fn encode_decode_bools(vals in proptest::collection::vec(any::<bool>(), 0..100)) {
assert_eq!(vals, decode(&encode(&vals)))
}
}
}