use super::meta::MetaCursor;
use super::op::{AsChangeOp, OpBuilder};
use super::types::{ActionCursor, ActorCursor, ActorIdx};
use crate::change_graph::ChangeGraph;
use crate::storage::change::{ChangeOpsColumns as ChangeOpsColumns2, Verified};
use crate::storage::{Change, ChunkType, Header};
use crate::types::{ActorId, ChangeHash};
use hexane::{BooleanCursor, ColumnCursor, DeltaCursor, RawCursor, StrCursor, UIntCursor};
use std::borrow::Cow;
use std::cmp::Ordering;
use std::io::Write;
use std::marker::PhantomData;
use std::num::NonZero;
use std::ops::Range;
pub(crate) mod batch;
pub(crate) mod collector;
pub(crate) use collector::{BuildChangeMetadata, ChangeCollector, CollectedChanges, OutOfMemory};
pub(crate) trait GetHash {
fn get_hash(&self, index: usize) -> Option<ChangeHash>;
}
impl GetHash for Vec<crate::Change> {
fn get_hash(&self, index: usize) -> Option<ChangeHash> {
Some(self.get(index)?.hash())
}
}
impl GetHash for ChangeGraph {
fn get_hash(&self, index: usize) -> Option<ChangeHash> {
self.index_to_hash(index).copied()
}
}
pub(crate) fn build_change<T, G>(
ops: &[T],
meta: &BuildChangeMetadata<'_>,
graph: &G,
actors: &[ActorId],
) -> Change<'static, Verified>
where
T: AsChangeOp,
G: GetHash,
{
let mut mapper = ActorMapper::new(actors);
build_change_inner(ops, meta, graph, &mut mapper)
}
pub(crate) fn build_change_inner<T, G>(
ops: &[T],
meta: &BuildChangeMetadata<'_>,
graph: &G,
mapper: &mut ActorMapper<'_>,
) -> Change<'static, Verified>
where
T: AsChangeOp,
G: GetHash,
{
let num_ops = ops.len();
let mut col_data = Vec::new();
let actor = mapper.actors[meta.actor].clone();
let start_op = ops.first().map(T::op_id_ctr).unwrap_or(meta.max_op + 1);
let ops_meta = write_change_ops(ops, meta.actor, &mut col_data, mapper);
let other_actors: Vec<_> = mapper.iter().collect();
let mut data = Vec::with_capacity(col_data.len());
leb128::write::unsigned(&mut data, meta.deps.len() as u64).unwrap();
let deps: Vec<_> = meta
.deps
.iter()
.map(|i| graph.get_hash(*i as usize).unwrap())
.collect();
for hash in &deps {
data.write_all(hash.as_bytes()).unwrap();
}
length_prefixed_bytes(&actor, &mut data);
leb128::write::unsigned(&mut data, meta.seq).unwrap();
leb128::write::unsigned(&mut data, start_op).unwrap();
leb128::write::signed(&mut data, meta.timestamp).unwrap();
length_prefixed_bytes(meta.message_str(), &mut data);
leb128::write::unsigned(&mut data, other_actors.len() as u64).unwrap();
for actor in other_actors.iter() {
length_prefixed_bytes(actor, &mut data);
}
ops_meta.raw_columns().write(&mut data);
let ops_data_start = data.len();
let ops_data = ops_data_start..(ops_data_start + col_data.len());
data.extend(col_data);
let extra_bytes = data.len()..(data.len() + meta.extra.len());
if !meta.extra.is_empty() {
data.extend(meta.extra.as_ref());
}
let header = Header::new(ChunkType::Change, &data);
let mut bytes = Vec::with_capacity(header.len() + data.len());
header.write(&mut bytes);
bytes.extend(data);
let ops_data = shift_range(ops_data, header.len());
let extra_bytes = shift_range(extra_bytes, header.len());
Change {
bytes: Cow::Owned(bytes),
header,
dependencies: deps,
actor,
other_actors,
seq: meta.seq,
start_op: NonZero::new(start_op).unwrap(),
timestamp: meta.timestamp,
message: meta.message.as_ref().map(|s| s.to_string()),
ops_meta,
ops_data,
extra_bytes,
num_ops,
_phantom: PhantomData,
}
}
impl PartialOrd for OpBuilder<'_> {
fn partial_cmp(&self, other: &OpBuilder<'_>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for OpBuilder<'_> {
fn cmp(&self, other: &Self) -> Ordering {
self.id.cmp(&other.id)
}
}
#[derive(Default)]
pub(crate) struct ChangeOpsColumns {
pub(crate) obj_actor: Range<usize>,
pub(crate) obj_ctr: Range<usize>,
pub(crate) key_actor: Range<usize>,
pub(crate) key_ctr: Range<usize>,
pub(crate) key_str: Range<usize>,
pub(crate) insert: Range<usize>,
pub(crate) action: Range<usize>,
pub(crate) value_meta: Range<usize>,
pub(crate) value: Range<usize>,
pub(crate) pred_count: Range<usize>,
pub(crate) pred_actor: Range<usize>,
pub(crate) pred_ctr: Range<usize>,
pub(crate) expand: Range<usize>,
pub(crate) mark_name: Range<usize>,
}
pub(crate) fn shift_range(range: Range<usize>, by: usize) -> Range<usize> {
range.start + by..range.end + by
}
pub(crate) fn length_prefixed_bytes<B: AsRef<[u8]>>(b: B, out: &mut Vec<u8>) -> usize {
let prefix_len = leb128::write::unsigned(out, b.as_ref().len() as u64).unwrap();
out.write_all(b.as_ref()).unwrap();
prefix_len + b.as_ref().len()
}
impl<'a> PartialEq for OpBuilder<'a> {
fn eq(&self, other: &OpBuilder<'a>) -> bool {
self.id == other.id
}
}
impl Eq for OpBuilder<'_> {}
fn write_change_ops<T>(
ops: &[T],
change_actor: usize,
data: &mut Vec<u8>,
mapper: &mut ActorMapper<'_>,
) -> ChangeOpsColumns2
where
T: AsChangeOp,
{
if ops.is_empty() {
return ChangeOpsColumns::default().into();
}
mapper.remap_actors(ops, change_actor);
let remap = move |actor: Option<Cow<'_, ActorIdx>>| {
actor.map(|a| Cow::Owned(mapper.mapping[usize::from(*a)].unwrap()))
};
let obj_actor =
ActorCursor::encode_unless_empty(data, ops.iter().map(T::obj_actor).map(&remap));
let obj_ctr = UIntCursor::encode_unless_empty(data, ops.iter().map(T::obj_ctr));
let key_actor =
ActorCursor::encode_unless_empty(data, ops.iter().map(T::key_actor).map(&remap));
let key_ctr = DeltaCursor::encode_unless_empty(data, ops.iter().map(T::key_ctr));
let key_str = StrCursor::encode_unless_empty(data, ops.iter().map(T::key_str));
let insert = BooleanCursor::encode(data, ops.iter().map(T::insert));
let action = ActionCursor::encode_unless_empty(data, ops.iter().map(T::action));
let value_meta = MetaCursor::encode_unless_empty(data, ops.iter().map(T::value_meta));
let value = RawCursor::encode_unless_empty(data, ops.iter().map(T::value));
let pred_count = UIntCursor::encode_unless_empty(data, ops.iter().map(T::pred_count));
let pred_iter = ops.iter().map(T::pred).flat_map(|id| id.iter());
let pred_actor_iter = pred_iter.clone().map(T::id_actor).map(&remap);
let pred_actor = ActorCursor::encode_unless_empty(data, pred_actor_iter);
let pred_ctr_iter = pred_iter.map(T::id_ctr);
let pred_ctr = DeltaCursor::encode_unless_empty(data, pred_ctr_iter);
let expand = BooleanCursor::encode_unless_empty(data, ops.iter().map(T::expand));
let mark_name = StrCursor::encode_unless_empty(data, ops.iter().map(T::mark_name));
let cols = ChangeOpsColumns {
obj_actor,
obj_ctr,
key_actor,
key_ctr,
key_str,
insert,
action,
value_meta,
value,
pred_count,
pred_actor,
pred_ctr,
expand,
mark_name,
};
cols.into()
}
#[derive(Debug, PartialEq)]
pub(crate) struct ActorMapper<'a> {
seen_actors: Vec<bool>,
pub(crate) mapping: Vec<Option<ActorIdx>>,
actors: &'a [ActorId],
other_actors: Vec<usize>,
}
impl<'a> ActorMapper<'a> {
pub(crate) fn iter(&self) -> impl ExactSizeIterator<Item = ActorId> + '_ {
self.other_actors.iter().map(|i| self.actors[*i].clone())
}
pub(crate) fn new(actors: &'a [ActorId]) -> ActorMapper<'a> {
let len = actors.len();
ActorMapper {
seen_actors: vec![false; len],
mapping: vec![None; len],
actors,
other_actors: vec![],
}
}
fn reset(&mut self) {
let len = self.actors.len();
self.seen_actors.truncate(0);
self.mapping.truncate(0);
self.other_actors.truncate(0);
self.seen_actors.resize(len, false);
self.mapping.resize(len, None);
}
pub(crate) fn process_actor(&mut self, actor: usize) {
self.seen_actors[actor] = true;
}
pub(crate) fn process_op<C>(&mut self, op: &C)
where
C: AsChangeOp,
{
if let Some(actor) = C::obj_actor(op).as_deref() {
self.process_actor(usize::from(*actor))
}
if let Some(actor) = C::key_actor(op).as_deref() {
self.process_actor(usize::from(*actor));
}
for id in C::pred(op) {
self.process_actor(id.actor());
}
}
pub(crate) fn build_mapping(&mut self, default_actor: Option<usize>) {
let mut seen_index = 0;
if let Some(actor) = default_actor {
self.seen_actors[actor] = false;
self.mapping[actor] = Some(ActorIdx(0));
seen_index = 1;
}
for (index, seen) in self.seen_actors.iter().enumerate() {
if *seen {
self.other_actors.push(index);
self.mapping[index] = Some(ActorIdx(seen_index));
seen_index += 1;
}
}
}
fn remap_actors<C>(&mut self, ops: &[C], change_actor: usize)
where
C: AsChangeOp,
{
self.reset();
for op in ops {
self.process_op(op);
}
self.build_mapping(Some(change_actor));
}
}