use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::io::Write;
use std::marker::PhantomData;
use std::mem::size_of;
use std::num::NonZero;
use super::super::meta::MetaCursor;
use super::super::types::{ActionCursor, ActorCursor, ActorIdx};
use super::{length_prefixed_bytes, shift_range};
use super::{ActorMapper, ChangeOpsColumns};
use hexane::{BooleanCursor, DeltaCursor, Encoder, StrCursor, UIntCursor};
use crate::change_graph::{ChangeGraph, ChangeGraphCols};
use crate::error::AutomergeError;
use crate::op_set2::change::{write_change_ops, GetHash};
use crate::op_set2::op_set::IndexBuilder;
use crate::storage::bundle::BundleChange;
use crate::storage::change::{Change as StoredChange, Verified};
use crate::storage::load::change_collector::Error;
use crate::storage::{ChunkType, Header};
use crate::{
change::Change,
op_set2::{ChangeMetadata, KeyRef, Op, OpBuilder, OpSet, ReadOpError},
types::{ActorId, ChangeHash, ObjId, OpId},
};
#[derive(Debug, thiserror::Error)]
#[error("out of memory")]
pub(crate) struct OutOfMemory;
pub(crate) struct IndexedChangeCollector<'a> {
pub(crate) index: &'a mut IndexBuilder,
pub(crate) collector: ChangeCollector<'a>,
}
impl<'a> IndexedChangeCollector<'a> {
pub(crate) fn process_ops(&mut self, op_set: &'a OpSet) -> Result<(), ReadOpError> {
let mut iter = op_set.iter();
while let Some(op) = iter.try_next()? {
let op_id = op.id;
let op_is_counter = op.is_counter();
let op_succ = op.succ();
self.process_op(op);
for id in op_succ {
self.index.process_succ(op_is_counter, id);
self.collector.process_succ(op_id, id);
}
}
Ok(())
}
pub(crate) fn collect(self, op_set: &OpSet) -> Result<CollectedChanges, Error> {
self.collector.collect(op_set)
}
pub(crate) fn process_op(&mut self, op: Op<'a>) {
let next = Some((op.obj, op.elemid_or_key()));
let flush = self.collector.last != next;
if flush {
self.index.flush();
}
self.index.process_op(&op);
self.collector.process_op_internal(op, flush);
if flush {
self.collector.last = next;
}
}
}
pub(crate) struct ChangeCollector<'a> {
mapper: ActorMapper<'a>,
changes: Vec<BuildChangeMetadata<'a>>,
pub(crate) builders: Vec<ChangeBuilder<'a>>,
last: Option<(ObjId, KeyRef<'a>)>,
preds: HashMap<OpId, Vec<OpId>>,
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) struct BuildChangeMetadata<'a> {
pub(crate) actor: usize,
pub(crate) seq: u64,
pub(crate) max_op: u64,
pub(crate) timestamp: i64,
pub(crate) message: Option<Cow<'a, str>>,
pub(crate) deps: Vec<u64>,
pub(crate) extra: Cow<'a, [u8]>,
pub(crate) start_op: u64,
pub(crate) builder: usize,
}
impl BuildChangeMetadata<'_> {
pub(crate) fn num_ops(&self) -> usize {
(1 + self.max_op - self.start_op) as usize
}
pub(crate) fn message_str(&self) -> &str {
self.message.as_deref().unwrap_or_default()
}
}
#[derive(Debug)]
pub(crate) struct ChangeBuilder<'a> {
actor: usize,
seq: u64,
change: usize,
start_op: u64,
encoder: OpEncoderStrategy<'a>,
}
#[derive(Clone, Debug, Default)]
enum OpEncoderStrategy<'a> {
Ops(VecEncoder<'a>),
Enc(Box<ProgressiveEncoder<'a>>),
#[default]
Null, }
impl<'a> OpEncoderStrategy<'a> {
fn new(num_ops: usize) -> Self {
let ops_size = num_ops * size_of::<Option<OpBuilder<'_>>>();
let enc_size = size_of::<ProgressiveEncoder<'_>>();
if ops_size > enc_size {
Self::Enc(Box::new(ProgressiveEncoder::new(num_ops as u64)))
} else {
Self::Ops(VecEncoder::new(num_ops as u64))
}
}
fn try_new(num_ops: usize) -> Result<Self, OutOfMemory> {
if CAN_OOM {
Ok(Self::Ops(VecEncoder::try_new(num_ops as u64)?))
} else {
Ok(Self::new(num_ops))
}
}
fn num_ops(&self) -> u64 {
match self {
Self::Ops(v) => v.num_ops(),
Self::Enc(e) => e.num_ops,
Self::Null => 0,
}
}
fn add(&mut self, index: usize, op: OpBuilder<'a>) {
match self {
Self::Ops(v) => v.add(index, op),
Self::Enc(e) => e.add(index, op),
Self::Null => (),
}
}
fn into_change_cols(
self,
change: &BuildChangeMetadata<'_>,
mapper: &mut ActorMapper<'_>,
) -> Result<ChangeCols, Error> {
mapper.reset();
match self {
Self::Ops(mut v) => v.finish(change, mapper),
Self::Enc(e) => Ok(e.finish(change, mapper)),
Self::Null => Err(Error::InvalidState),
}
}
fn finish<G>(
self,
change: &BuildChangeMetadata<'_>,
graph: &G,
mapper: &mut ActorMapper<'_>,
) -> Result<StoredChange<'static, Verified>, Error>
where
G: GetHash,
{
let cols = self.into_change_cols(change, mapper)?;
let num_ops = cols.num_ops as usize;
let start_op = cols.start_op.unwrap_or(change.max_op + 1);
let ops_meta = cols.meta;
let col_data = cols.data;
let actor = cols.actor;
let other_actors = cols.other_actors;
let mut data = Vec::with_capacity(col_data.len());
leb128::write::unsigned(&mut data, change.deps.len() as u64).unwrap();
let deps: Vec<_> = change
.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, change.seq).unwrap();
leb128::write::unsigned(&mut data, start_op).unwrap();
leb128::write::signed(&mut data, change.timestamp).unwrap();
length_prefixed_bytes(change.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() + change.extra.len());
if !change.extra.is_empty() {
data.extend(change.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());
Ok(StoredChange {
bytes: Cow::Owned(bytes),
header,
dependencies: deps,
actor,
other_actors,
seq: change.seq,
start_op: NonZero::new(start_op).unwrap(),
timestamp: change.timestamp,
message: change.message.as_ref().map(|s| s.to_string()),
ops_meta,
ops_data,
extra_bytes,
num_ops,
_phantom: PhantomData,
})
}
}
#[derive(Clone, Debug, Default)]
pub(crate) struct VecEncoder<'a> {
data: Vec<Option<OpBuilder<'a>>>,
}
impl<'a> VecEncoder<'a> {
fn new(num_ops: u64) -> Self {
Self {
data: vec![None; num_ops as usize],
}
}
fn try_new(num_ops: u64) -> Result<Self, OutOfMemory> {
let num_ops = num_ops as usize;
let mut data = Vec::new();
data.try_reserve(num_ops).map_err(|_| OutOfMemory)?;
data.extend(std::iter::repeat_n(None, num_ops));
Ok(Self { data })
}
fn num_ops(&self) -> u64 {
self.data.len() as u64
}
fn add(&mut self, index: usize, op: OpBuilder<'a>) {
assert!(self.data[index].is_none());
self.data[index] = Some(op);
}
fn finish(
&mut self,
change: &BuildChangeMetadata<'_>,
mapper: &mut ActorMapper<'_>,
) -> Result<ChangeCols, Error> {
let start_pos = self.data.iter().position(|op| op.is_some()).unwrap_or(0);
let ops = &self.data[start_pos..];
if ops.iter().any(|o| o.is_none()) {
return Err(Error::MissingOps);
}
if let Some(Some(last)) = ops.last() {
assert_eq!(last.id.counter(), change.max_op);
}
let mut data = vec![];
let meta = write_change_ops(ops, change.actor, &mut data, mapper);
let actor = mapper.actors[change.actor].clone();
let other_actors = mapper.iter().collect();
Ok(ChangeCols {
actor,
other_actors,
start_op: ops
.first()
.and_then(|op| op.as_ref())
.map(|op| op.id.counter()),
num_ops: ops.len() as u64,
data,
meta,
})
}
}
#[derive(Clone, Debug, Default)]
pub(crate) struct ProgressiveEncoder<'a> {
pub(crate) len: usize,
pub(crate) start_op: Option<u64>,
pub(crate) num_ops: u64,
actors: Vec<bool>,
queue: BTreeMap<usize, OpBuilder<'a>>,
obj_actor: Encoder<'a, ActorCursor>,
obj_ctr: Encoder<'a, UIntCursor>,
key_actor: Encoder<'a, ActorCursor>,
key_ctr: Encoder<'a, DeltaCursor>,
key_str: Encoder<'a, StrCursor>,
insert: Encoder<'a, BooleanCursor>,
action: Encoder<'a, ActionCursor>,
value_meta: Encoder<'a, MetaCursor>,
value: Vec<u8>,
pred_count: Encoder<'a, UIntCursor>,
pred_actor: Encoder<'a, ActorCursor>,
pred_ctr: Encoder<'a, DeltaCursor>,
expand: Encoder<'a, BooleanCursor>,
mark_name: Encoder<'a, StrCursor>,
}
impl<'a> ProgressiveEncoder<'a> {
fn new(num_ops: u64) -> Self {
ProgressiveEncoder {
num_ops,
..Default::default()
}
}
fn process_actor(&mut self, actor: usize) {
if actor >= self.actors.len() {
self.actors.resize(actor + 1, false);
}
self.actors[actor] = true;
}
fn process_op(&mut self, op: &OpBuilder<'a>) {
if let Some(actor) = op.obj.actor() {
self.process_actor(usize::from(actor));
}
if let Some(actor) = op.key.actor() {
self.process_actor(usize::from(actor));
}
for id in &op.pred {
self.process_actor(id.actor());
}
}
fn add(&mut self, index: usize, op: OpBuilder<'a>) {
self.process_op(&op);
if index == self.len {
self.append(op);
self.len += 1;
while let Some(op) = self.queue.remove(&self.len) {
self.append(op);
self.len += 1;
}
} else {
self.queue.insert(index, op);
}
}
fn append(&mut self, op: OpBuilder<'a>) {
if self.start_op.is_none() {
self.start_op = Some(op.id.counter());
}
self.obj_actor.append(op.obj.actor());
self.obj_ctr.append(op.obj.counter());
self.key_actor.append(op.key.actor());
self.key_ctr.append(op.key.icounter());
self.key_str.append(op.key.key_str());
self.insert.append(op.insert);
self.action.append(op.action);
self.value_meta.append(op.value.meta());
if let Some(bytes) = op.value.as_raw() {
self.value.extend(&*bytes);
}
self.pred_count.append(op.pred.len() as u64);
for id in op.pred {
self.pred_actor.append(id.actoridx());
self.pred_ctr.append(id.icounter());
}
self.expand.append(op.expand);
self.mark_name.append(op.mark_name);
}
fn flush(&mut self) {
let queue = std::mem::take(&mut self.queue);
for (_index, op) in queue {
self.append(op);
self.len += 1;
}
}
pub(crate) fn build_mapping(
&mut self,
default: usize,
m: &mut ActorMapper<'_>,
) -> Vec<Option<ActorIdx>> {
m.other_actors.truncate(0);
let mut seen_index = 1;
if default >= self.actors.len() {
self.actors.resize(default + 1, false);
}
let mut mapping = vec![None; self.actors.len()];
self.actors[default] = false;
mapping[default] = Some(ActorIdx(0));
for (index, seen) in self.actors.iter().enumerate() {
if *seen {
m.other_actors.push(index);
mapping[index] = Some(ActorIdx(seen_index));
seen_index += 1;
}
}
mapping
}
pub(crate) fn save_to(
mut self,
actor: usize,
data: &mut Vec<u8>,
mapper: &mut ActorMapper<'_>,
) -> ChangeOpsColumns {
let mapper = self.build_mapping(actor, mapper);
let remap = |actor: &ActorIdx| mapper[usize::from(*actor)].as_ref();
let obj_actor = self.obj_actor.save_to_and_remap_unless_empty(data, &remap);
let obj_ctr = self.obj_ctr.save_to_unless_empty(data);
let key_actor = self.key_actor.save_to_and_remap_unless_empty(data, &remap);
let key_ctr = self.key_ctr.save_to_unless_empty(data);
let key_str = self.key_str.save_to_unless_empty(data);
let insert = self.insert.save_to(data);
let action = self.action.save_to_unless_empty(data);
let value_meta = self.value_meta.save_to_unless_empty(data);
let value = {
let start = data.len();
data.extend(self.value);
start..data.len()
};
let pred_count = self.pred_count.save_to_unless_empty(data);
let pred_actor = self.pred_actor.save_to_and_remap_unless_empty(data, &remap);
let pred_ctr = self.pred_ctr.save_to_unless_empty(data);
let expand = self.expand.save_to_unless_empty(data);
let mark_name = self.mark_name.save_to_unless_empty(data);
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,
}
}
fn finish(
mut self,
change: &BuildChangeMetadata<'_>,
mapper: &mut ActorMapper<'_>,
) -> ChangeCols {
self.flush();
let mut data = vec![];
let num_ops = self.len as u64;
let start_op = self.start_op;
let meta = self.save_to(change.actor, &mut data, mapper).into();
let actor = mapper.actors[change.actor].clone();
let other_actors = mapper.iter().collect();
ChangeCols {
actor,
other_actors,
start_op,
num_ops,
data,
meta,
}
}
}
impl<'a> ChangeBuilder<'a> {
pub(crate) fn finish<G>(
&mut self,
change: &BuildChangeMetadata<'_>,
graph: &G,
mapper: &mut ActorMapper<'_>,
) -> Result<StoredChange<'static, Verified>, Error>
where
G: GetHash,
{
let encoder = std::mem::take(&mut self.encoder);
encoder.finish(change, graph, mapper)
}
pub(crate) fn max_op(&self) -> u64 {
self.start_op + self.encoder.num_ops() - 1
}
pub(crate) fn add(&mut self, op: OpBuilder<'a>) {
let counter = op.id.counter();
let index = (counter - self.start_op) as usize;
self.encoder.add(index, op);
}
}
impl<'a> ChangeCollector<'a> {
pub(crate) fn with_index(self, index: &'a mut IndexBuilder) -> IndexedChangeCollector<'a> {
IndexedChangeCollector {
collector: self,
index,
}
}
pub(crate) fn try_new(
change_cols: &'a ChangeGraphCols,
op_set: &'a OpSet,
) -> Result<ChangeCollector<'a>, OutOfMemory> {
let mut meta = Vec::new();
meta.try_reserve(change_cols.len())
.map_err(|_| OutOfMemory)?;
for c in change_cols.iter() {
meta.push(c);
}
ChangeCollector::try_from_change_meta(meta, &op_set.actors)
}
pub(crate) fn process_ops(&mut self, op_set: &'a OpSet) -> Result<(), ReadOpError> {
let mut iter = op_set.iter();
while let Some(op) = iter.try_next()? {
let op_id = op.id;
let op_succ = op.succ();
self.process_op(op);
for id in op_succ {
self.process_succ(op_id, id);
}
}
Ok(())
}
pub(crate) fn from_bundle_changes(
changes: Vec<BundleChange<'a>>,
actors: &'a [ActorId],
) -> ChangeCollector<'a> {
let changes = changes.into_iter().map(|c| c.into()).collect();
Self::from_change_meta(changes, actors)
}
pub(crate) fn try_from_change_meta(
mut changes: Vec<BuildChangeMetadata<'a>>,
actors: &'a [ActorId],
) -> Result<ChangeCollector<'a>, OutOfMemory> {
let mut builders = Vec::new();
builders
.try_reserve(changes.len())
.map_err(|_| OutOfMemory)?;
for (index, e) in changes.iter().enumerate() {
builders.push(ChangeBuilder {
actor: e.actor,
seq: e.seq,
change: index,
start_op: e.start_op,
encoder: OpEncoderStrategy::try_new(e.num_ops())?,
})
}
builders.sort_unstable_by(|a, b| a.actor.cmp(&b.actor).then(a.seq.cmp(&b.seq)));
let mapper = ActorMapper::new(actors);
builders
.iter()
.enumerate()
.for_each(|(index, b)| changes[b.change].builder = index);
Ok(ChangeCollector {
mapper,
changes,
builders,
last: None,
preds: HashMap::default(),
})
}
pub(crate) fn from_change_meta(
changes: Vec<BuildChangeMetadata<'a>>,
actors: &'a [ActorId],
) -> ChangeCollector<'a> {
Self::try_from_change_meta(changes, actors).unwrap()
}
pub(crate) fn exclude_hashes(
op_set: &'a OpSet,
change_graph: &'a ChangeGraph,
have_deps: &[ChangeHash],
) -> Vec<Change> {
let changes = change_graph.get_build_metadata_clock(have_deps);
Self::from_build_meta(op_set, change_graph, changes)
}
pub(crate) fn exclude_hashes_meta(
op_set: &'a OpSet,
change_graph: &'a ChangeGraph,
have_deps: &[ChangeHash],
) -> Vec<ChangeMetadata<'a>> {
let changes = change_graph.get_build_metadata_clock(have_deps);
changes
.into_iter()
.map(|c| ChangeMetadata {
actor: Cow::Borrowed(&op_set.actors[c.actor]),
seq: c.seq,
start_op: c.start_op,
max_op: c.max_op,
timestamp: c.timestamp,
message: c.message,
deps: c
.deps
.iter()
.filter_map(|n| change_graph.index_to_hash(*n as usize).cloned())
.collect(),
hash: change_graph.index_to_hash(c.builder).cloned().unwrap(),
extra: c.extra,
})
.collect()
}
pub(crate) fn meta_for_hashes<I>(
op_set: &'a OpSet,
change_graph: &'a ChangeGraph,
hashes: I,
) -> Result<Vec<ChangeMetadata<'a>>, AutomergeError>
where
I: IntoIterator<Item = ChangeHash>,
{
let changes = change_graph.get_build_metadata(hashes)?;
Ok(changes
.into_iter()
.map(|c| ChangeMetadata {
actor: Cow::Borrowed(&op_set.actors[c.actor]),
seq: c.seq,
start_op: c.start_op,
max_op: c.max_op,
timestamp: c.timestamp,
message: c.message,
deps: c
.deps
.iter()
.filter_map(|n| change_graph.index_to_hash(*n as usize).cloned())
.collect(),
hash: change_graph.index_to_hash(c.builder).cloned().unwrap(),
extra: c.extra,
})
.collect())
}
pub(crate) fn for_hashes<I>(
op_set: &'a OpSet,
change_graph: &'a ChangeGraph,
hashes: I,
) -> Result<Vec<Change>, AutomergeError>
where
I: IntoIterator<Item = ChangeHash>,
{
let changes = change_graph.get_build_metadata(hashes)?;
Ok(Self::from_build_meta(op_set, change_graph, changes))
}
fn from_build_meta(
op_set: &'a OpSet,
change_graph: &'a ChangeGraph,
changes: Vec<BuildChangeMetadata<'a>>,
) -> Vec<Change> {
let r1 = Self::from_build_meta_inner(op_set, change_graph, changes.clone());
debug_assert_eq!(
r1,
crate::storage::Bundle::for_hashes(op_set, change_graph, r1.iter().map(|c| c.hash()))
.unwrap()
.to_changes()
.unwrap()
);
r1
}
fn from_build_meta_inner(
op_set: &'a OpSet,
change_graph: &'a ChangeGraph,
changes: Vec<BuildChangeMetadata<'a>>,
) -> Vec<Change> {
let min = changes
.iter()
.map(|c| c.start_op as usize)
.min()
.unwrap_or(0);
let max = changes.iter().map(|c| c.max_op as usize).max().unwrap_or(0) + 1;
let mut collector = Self::from_change_meta(changes, &op_set.actors);
for op in op_set.iter_ctr_range(min..max) {
let op_id = op.id;
let op_succ = op.succ();
collector.process_op(op);
for id in op_succ {
collector.process_succ(op_id, id);
}
}
collector.finish(change_graph).unwrap()
}
pub(crate) fn process_succ(&mut self, op_id: OpId, succ_id: OpId) {
self.preds.entry(succ_id).or_default().push(op_id);
}
pub(crate) fn process_op(&mut self, op: Op<'a>) {
let next = Some((op.obj, op.elemid_or_key()));
let flush = self.last != next;
self.process_op_internal(op, flush);
if flush {
self.last = next;
}
}
fn process_op_internal(&mut self, op: Op<'a>, flush: bool) {
if flush {
self.flush_deletes();
}
let pred = self.preds.remove(&op.id).unwrap_or_default();
let op = op.build(pred);
self.add(op);
}
pub(crate) fn add(&mut self, op: OpBuilder<'a>) {
if let Some(index) = self.builders_index(op.id) {
self.builders[index].add(op);
}
}
pub(crate) fn builders_index(&self, id: OpId) -> Option<usize> {
self.builders
.binary_search_by(|builder| {
builder
.actor
.cmp(&id.actor())
.then_with(|| match id.counter() {
c if c < builder.start_op => Ordering::Greater,
c if c > builder.max_op() => Ordering::Less,
_ => Ordering::Equal,
})
})
.ok()
}
pub(crate) fn flush_deletes(&mut self) {
if let Some((obj, key)) = self.last.take() {
for (id, pred) in &self.preds {
let op = Op::del(*id, obj, key.clone());
let op = op.build(pred.to_vec());
if let Some(index) = self.builders_index(op.id) {
self.builders[index].add(op);
}
}
self.preds.clear();
}
}
pub(crate) fn finish(self, change_graph: &ChangeGraph) -> Result<Vec<Change>, Error> {
self.finish_inner(change_graph, None)
}
fn finish_inner(
mut self,
graph: &ChangeGraph,
index: Option<&mut IndexBuilder>,
) -> Result<Vec<Change>, Error> {
self.flush_deletes();
if let Some(i) = index {
i.flush()
}
let mut changes = Vec::with_capacity(self.changes.len());
for change in self.changes.into_iter() {
let actor = change.actor;
if actor >= self.mapper.actors.len() {
return Err(Error::MissingActor);
}
let change = self.builders[change.builder].finish(&change, graph, &mut self.mapper)?;
changes.push(Change::new(change))
}
Ok(changes)
}
pub(crate) fn unbundle(
mut self,
actors: &[ActorId],
deps: &[ChangeHash],
) -> Result<Vec<Change>, Error> {
let num_actors = actors.len();
let num_changes = self.changes.len();
let mut changes = Vec::with_capacity(num_changes);
for change in self.changes.into_iter() {
if change.actor >= num_actors {
return Err(Error::MissingActor);
}
let all_deps = BundleDeps::new(num_changes, &changes, deps);
let change = self.builders[change.builder]
.finish(&change, &all_deps, &mut self.mapper)
.unwrap();
changes.push(Change::from(change));
}
Ok(changes)
}
pub(crate) fn collect(mut self, op_set: &OpSet) -> Result<CollectedChanges, Error> {
self.flush_deletes();
let num_actors = op_set.actors.len();
let mut max_ops = vec![0; num_actors];
let mut seq = vec![0; num_actors];
let mut changes = Vec::with_capacity(self.changes.len());
let mut heads = BTreeSet::new();
for change in self.changes.into_iter() {
let actor = change.actor;
if actor >= num_actors {
return Err(Error::MissingActor);
}
if seq[actor] + 1 != change.seq {
return Err(Error::ChangesOutOfOrder);
}
seq[actor] = change.seq;
let builder = change.builder;
let max_op = change.max_op;
if change.start_op == 0 || max_op < max_ops[actor] {
return Err(Error::IncorrectMaxOp);
}
max_ops[actor] = max_op;
let change = self.builders[builder].finish(&change, &changes, &mut self.mapper)?;
let hash = change.hash();
for dep in change.dependencies() {
heads.remove(dep);
}
heads.insert(hash);
changes.push(Change::from(change));
}
Ok(CollectedChanges { changes, heads })
}
}
pub(crate) struct CollectedChanges {
pub(crate) changes: Vec<Change>,
pub(crate) heads: BTreeSet<ChangeHash>,
}
struct BundleDeps<'a> {
num_changes: usize,
changes: &'a Vec<Change>,
deps: &'a [ChangeHash],
}
impl<'a> BundleDeps<'a> {
fn new(num_changes: usize, changes: &'a Vec<Change>, deps: &'a [ChangeHash]) -> Self {
Self {
num_changes,
changes,
deps,
}
}
}
impl GetHash for BundleDeps<'_> {
fn get_hash(&self, index: usize) -> Option<ChangeHash> {
if index >= self.num_changes {
self.deps.get(index - self.num_changes).copied()
} else {
Some(self.changes.get(index)?.hash())
}
}
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) struct ChangeCols {
pub(crate) num_ops: u64,
pub(crate) start_op: Option<u64>,
pub(crate) meta: crate::storage::change::ChangeOpsColumns,
pub(crate) actor: ActorId,
pub(crate) other_actors: Vec<ActorId>,
pub(crate) data: Vec<u8>,
}
#[cfg(all(feature = "wasm", target_family = "wasm"))]
const CAN_OOM: bool = true;
#[cfg(not(all(feature = "wasm", target_family = "wasm")))]
const CAN_OOM: bool = false;