The previous approach of using the key and insert columns of existing ops was leading to quite confusing code. There's no real cost to introducing new columns so I've switched the code to do that instead. Introduce an `expand` and a `mark_name` column. `expand` is a boolean column and `mark_name` is a RLE encoded string column. Neither of these columns are encoded if they are empty. Also move the `MarkData::name` property to use strings interned in `OpSetMetadata::props` rather than representing the string directly on the basis that we probably will have a lot of repeated mark names and we do a bunch of equality checks on them while searching so this will probably speed things up a bit. Introduced a new `MaybeBooleanEncoder` (and associated `MaybeBooleanDecoder` and `MaybeBooleanRange`) types to represent a boolean column which is entirely skipped if all it contains are `false` values. This allows us to omit encoding the `expand` column for groups of ops which only ever set it to `false` which in turn makes us backwards compatible when not using marks.
258 lines
7.5 KiB
Rust
258 lines
7.5 KiB
Rust
use std::{convert::Infallible, ops::Range};
|
|
|
|
use super::{DeltaRange, RleRange};
|
|
use crate::{
|
|
columnar::{
|
|
encoding::{
|
|
raw, DecodeColumnError, DeltaDecoder, DeltaEncoder, RleDecoder, RleEncoder, Sink,
|
|
},
|
|
SpliceError,
|
|
},
|
|
convert,
|
|
types::{ElemId, OpId},
|
|
};
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub(crate) enum Key {
|
|
Prop(smol_str::SmolStr),
|
|
Elem(ElemId),
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub(crate) struct KeyRange {
|
|
actor: RleRange<u64>,
|
|
counter: DeltaRange,
|
|
string: RleRange<smol_str::SmolStr>,
|
|
}
|
|
|
|
impl KeyRange {
|
|
pub(crate) fn new(
|
|
actor: RleRange<u64>,
|
|
counter: DeltaRange,
|
|
string: RleRange<smol_str::SmolStr>,
|
|
) -> Self {
|
|
Self {
|
|
actor,
|
|
counter,
|
|
string,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn actor_range(&self) -> &RleRange<u64> {
|
|
&self.actor
|
|
}
|
|
|
|
pub(crate) fn counter_range(&self) -> &DeltaRange {
|
|
&self.counter
|
|
}
|
|
|
|
pub(crate) fn string_range(&self) -> &RleRange<smol_str::SmolStr> {
|
|
&self.string
|
|
}
|
|
|
|
pub(crate) fn iter<'a>(&self, data: &'a [u8]) -> KeyIter<'a> {
|
|
KeyIter {
|
|
actor: self.actor.decoder(data),
|
|
counter: self.counter.decoder(data),
|
|
string: self.string.decoder(data),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn encode<'b, O, I: Iterator<Item = convert::Key<'b, O>> + Clone>(
|
|
items: I,
|
|
out: &mut Vec<u8>,
|
|
) -> Self
|
|
where
|
|
O: convert::OpId<usize>,
|
|
{
|
|
// SAFETY: The incoming iterator is infallible and there are no existing items
|
|
Self {
|
|
actor: (0..0).into(),
|
|
counter: (0..0).into(),
|
|
string: (0..0).into(),
|
|
}
|
|
.splice::<_, Infallible, _>(&[], 0..0, items.map(Ok), out)
|
|
.unwrap()
|
|
}
|
|
|
|
/// Splice new keys into this set of keys, encoding the resulting actor, counter, and str
|
|
/// columns in `out`.
|
|
pub(crate) fn splice<'b, O, E, I>(
|
|
&mut self,
|
|
data: &[u8],
|
|
replace: Range<usize>,
|
|
replace_with: I,
|
|
out: &mut Vec<u8>,
|
|
) -> Result<Self, SpliceError<raw::Error, E>>
|
|
where
|
|
O: convert::OpId<usize>,
|
|
E: std::error::Error,
|
|
I: Iterator<Item = Result<convert::Key<'b, O>, E>> + Clone,
|
|
{
|
|
let actor = self.actor.splice(
|
|
data,
|
|
replace.clone(),
|
|
replace_with.clone().map(|k| {
|
|
k.map(|k| match k {
|
|
convert::Key::Prop(_) => None,
|
|
convert::Key::Elem(convert::ElemId::Head) => None,
|
|
convert::Key::Elem(convert::ElemId::Op(o)) => Some(o.actor() as u64),
|
|
})
|
|
}),
|
|
out,
|
|
)?;
|
|
|
|
let counter = self.counter.splice(
|
|
data,
|
|
replace.clone(),
|
|
replace_with.clone().map(|k| {
|
|
k.map(|k| match k {
|
|
convert::Key::Prop(_) => None,
|
|
convert::Key::Elem(convert::ElemId::Head) => Some(0),
|
|
convert::Key::Elem(convert::ElemId::Op(o)) => Some(o.counter() as i64),
|
|
})
|
|
}),
|
|
out,
|
|
)?;
|
|
|
|
let string = self.string.splice(
|
|
data,
|
|
replace,
|
|
replace_with.map(|k| {
|
|
k.map(|k| match k {
|
|
convert::Key::Prop(s) => Some(s),
|
|
convert::Key::Elem(_) => None,
|
|
})
|
|
}),
|
|
out,
|
|
)?;
|
|
|
|
Ok(Self {
|
|
actor,
|
|
counter,
|
|
string,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub(crate) struct KeyIter<'a> {
|
|
actor: RleDecoder<'a, u64>,
|
|
counter: DeltaDecoder<'a>,
|
|
string: RleDecoder<'a, smol_str::SmolStr>,
|
|
}
|
|
|
|
impl<'a> KeyIter<'a> {
|
|
fn try_next(&mut self) -> Result<Option<Key>, DecodeColumnError> {
|
|
let actor = self
|
|
.actor
|
|
.next()
|
|
.transpose()
|
|
.map_err(|e| DecodeColumnError::decode_raw("actor", e))?;
|
|
let counter = self
|
|
.counter
|
|
.next()
|
|
.transpose()
|
|
.map_err(|e| DecodeColumnError::decode_raw("counter", e))?;
|
|
let string = self
|
|
.string
|
|
.next()
|
|
.transpose()
|
|
.map_err(|e| DecodeColumnError::decode_raw("string", e))?;
|
|
match (actor, counter, string) {
|
|
(Some(Some(_)), Some(Some(_)), Some(Some(_))) => {
|
|
Err(DecodeColumnError::invalid_value("key", "too many values"))
|
|
}
|
|
(Some(None) | None, Some(None) | None, Some(Some(string))) => {
|
|
Ok(Some(Key::Prop(string)))
|
|
}
|
|
(Some(None) | None, Some(Some(0)), Some(None) | None) => {
|
|
Ok(Some(Key::Elem(ElemId(OpId::new(0, 0)))))
|
|
}
|
|
(Some(Some(actor)), Some(Some(ctr)), Some(None) | None) => match ctr.try_into() {
|
|
//Ok(ctr) => Some(Ok(Key::Elem(ElemId(OpId(ctr, actor as usize))))),
|
|
Ok(ctr) => Ok(Some(Key::Elem(ElemId(OpId::new(ctr, actor as usize))))),
|
|
Err(_) => Err(DecodeColumnError::invalid_value(
|
|
"counter",
|
|
"negative value for counter",
|
|
)),
|
|
},
|
|
(None | Some(None), None | Some(None), None | Some(None)) => Ok(None),
|
|
(None | Some(None), k, _) => {
|
|
tracing::error!(key=?k, "unexpected null actor");
|
|
Err(DecodeColumnError::unexpected_null("actor"))
|
|
}
|
|
(_, None | Some(None), _) => Err(DecodeColumnError::unexpected_null("counter")),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Iterator for KeyIter<'a> {
|
|
type Item = Result<Key, DecodeColumnError>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
self.try_next().transpose()
|
|
}
|
|
}
|
|
|
|
pub(crate) struct KeyEncoder<S> {
|
|
actor: RleEncoder<S, u64>,
|
|
counter: DeltaEncoder<S>,
|
|
string: RleEncoder<S, smol_str::SmolStr>,
|
|
}
|
|
|
|
impl KeyEncoder<Vec<u8>> {
|
|
pub(crate) fn new() -> KeyEncoder<Vec<u8>> {
|
|
KeyEncoder {
|
|
actor: RleEncoder::new(Vec::new()),
|
|
counter: DeltaEncoder::new(Vec::new()),
|
|
string: RleEncoder::new(Vec::new()),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn finish(self, out: &mut Vec<u8>) -> KeyRange {
|
|
let actor_start = out.len();
|
|
let (actor, _) = self.actor.finish();
|
|
out.extend(actor);
|
|
let actor_end = out.len();
|
|
|
|
let (counter, _) = self.counter.finish();
|
|
out.extend(counter);
|
|
let counter_end = out.len();
|
|
|
|
let (string, _) = self.string.finish();
|
|
out.extend(string);
|
|
let string_end = out.len();
|
|
|
|
KeyRange {
|
|
actor: (actor_start..actor_end).into(),
|
|
counter: (actor_end..counter_end).into(),
|
|
string: (counter_end..string_end).into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S: Sink> KeyEncoder<S> {
|
|
pub(crate) fn append<O>(&mut self, key: convert::Key<'_, O>)
|
|
where
|
|
O: convert::OpId<usize>,
|
|
{
|
|
match key {
|
|
convert::Key::Prop(p) => {
|
|
self.string.append_value(p.clone());
|
|
self.actor.append_null();
|
|
self.counter.append_null();
|
|
}
|
|
convert::Key::Elem(convert::ElemId::Head) => {
|
|
self.string.append_null();
|
|
self.actor.append_null();
|
|
self.counter.append_value(0);
|
|
}
|
|
convert::Key::Elem(convert::ElemId::Op(o)) => {
|
|
self.string.append_null();
|
|
self.actor.append_value(o.actor() as u64);
|
|
self.counter.append_value(o.counter() as i64);
|
|
}
|
|
}
|
|
}
|
|
}
|