automerge 0.11.0

A JSON-like data structure (a CRDT) that can be modified concurrently by different users, and merged again automatically
Documentation
use crate::op_set2::op_set::{MarkIndexBuilder, MarkIndexColumn};
use crate::op_set2::{ChangeOp, Op, OpBuilder, OpSet};
use crate::types::{ObjId, ObjType, OpId, SequenceType, TextEncoding};
use std::collections::HashMap;

// TODO : this could be faster and use less memory if
// hexane::Encoder was used here instead of Vec<>

pub(crate) struct IndexBuilder {
    counters: HashMap<OpId, Vec<(usize, usize)>>,
    succ: Vec<u32>,
    top: Vec<bool>,
    widths: Vec<u64>,
    incs: Vec<Option<i64>>,
    marks: Vec<Option<MarkIndexBuilder>>,
    obj_info: ObjIndex,
    last_flush: usize,
    text_encoding: TextEncoding,
    mark_order: MarkOrderValidator,
}

#[derive(Debug, Default, Clone)]
pub(crate) struct MarkOrderValidator {
    begins: HashMap<OpId, ObjId>,
    error: Option<String>,
}

impl MarkOrderValidator {
    pub(crate) fn process_op(&mut self, op: &Op<'_>) {
        let mark_index = op.mark_index();
        self.process_mark_index(op, &mark_index);
    }

    pub(crate) fn process_mark_index(
        &mut self,
        op: &Op<'_>,
        mark_index: &Option<MarkIndexBuilder>,
    ) {
        if self.error.is_some() {
            return;
        }
        self.check_mark_op(op, mark_index);
    }

    pub(crate) fn take_error(&mut self) -> Option<String> {
        self.error.take()
    }

    /// Check that mark ops:
    /// * Always start and end in the same object
    /// * Have the start op appear before the end op
    fn check_mark_op(&mut self, op: &Op<'_>, mark_index: &Option<MarkIndexBuilder>) {
        match mark_index {
            Some(MarkIndexBuilder::Start(id, _)) => {
                self.begins.insert(*id, op.obj);
            }
            Some(MarkIndexBuilder::End(begin)) => match self.begins.get(begin) {
                Some(obj) if *obj == op.obj => {}
                Some(_) => {
                    self.error = Some(format!(
                        "mark end {:?} references mark begin {:?} in a different object",
                        op.id, begin
                    ));
                }
                None => {
                    self.error = Some(format!(
                        "mark end {:?} occurs before mark begin {:?}",
                        op.id, begin
                    ));
                }
            },
            None => {}
        }
    }
}

#[derive(Debug, Default, Clone)]
pub(crate) struct ObjIndex(pub(crate) HashMap<OpId, ObjInfo>);

impl ObjIndex {
    pub(crate) fn object_type(&self, obj: &ObjId) -> Option<ObjType> {
        if obj.is_root() {
            Some(ObjType::Map)
        } else {
            self.0.get(&obj.0).map(|p| p.obj_type)
        }
    }

    pub(crate) fn object_parent(&self, obj: &ObjId) -> Option<ObjId> {
        if obj.is_root() {
            None
        } else {
            self.0.get(&obj.0).map(|p| p.parent)
        }
    }

    pub(crate) fn insert(&mut self, id: OpId, obj_info: ObjInfo) {
        self.0.insert(id, obj_info);
    }

    pub(crate) fn remove(&mut self, id: OpId) {
        self.0.remove(&id);
    }
}

#[derive(Copy, Clone, Debug)]
pub(crate) struct ObjInfo {
    pub(crate) parent: ObjId,
    pub(crate) obj_type: ObjType,
}

impl ObjInfo {
    pub(crate) fn with_new_actor(self, idx: usize) -> Self {
        Self {
            parent: self.parent.with_new_actor(idx),
            obj_type: self.obj_type,
        }
    }

    pub(crate) fn without_actor(self, idx: usize) -> Option<Self> {
        Some(Self {
            parent: self.parent.without_actor(idx)?,
            obj_type: self.obj_type,
        })
    }
}

impl Op<'_> {
    pub(crate) fn obj_info(&self) -> Option<ObjInfo> {
        let obj_type = ObjType::try_from(self.action).ok()?;
        let parent = self.obj;
        Some(ObjInfo { parent, obj_type })
    }
}

impl ChangeOp {
    pub(crate) fn obj_info(&self) -> Option<ObjInfo> {
        self.bld.obj_info()
    }
}

impl OpBuilder<'_> {
    pub(crate) fn obj_info(&self) -> Option<ObjInfo> {
        let obj_type = ObjType::try_from(self.action).ok()?;
        let parent = self.obj;
        Some(ObjInfo { parent, obj_type })
    }
}

pub(crate) struct Indexes {
    pub(crate) text: hexane::PrefixColumn<Option<u32>>,
    pub(crate) top: hexane::PrefixColumn<bool>,
    pub(crate) visible: hexane::Column<bool>,
    pub(crate) inc: hexane::Column<Option<i64>>,
    pub(crate) mark: MarkIndexColumn,
    pub(crate) obj_info: ObjIndex,
}

impl IndexBuilder {
    pub(crate) fn new(op_set: &OpSet, encoding: TextEncoding) -> Self {
        Self {
            counters: HashMap::new(),
            succ: Vec::with_capacity(op_set.len()),
            top: Vec::with_capacity(op_set.len()),
            widths: Vec::with_capacity(op_set.len()),
            incs: Vec::with_capacity(op_set.sub_len()),
            marks: Vec::with_capacity(op_set.len()),
            obj_info: ObjIndex::default(),
            last_flush: 0,
            text_encoding: encoding,
            mark_order: MarkOrderValidator::default(),
        }
    }

    pub(crate) fn flush(&mut self) {
        let len = self.succ.len();
        for (delta, succ) in self.succ[self.last_flush..].iter().rev().enumerate() {
            if *succ == 0 {
                self.top[len - delta - 1] = true;
                break;
            }
        }
        self.last_flush = len;
    }
    pub(crate) fn process_op(&mut self, op: &Op<'_>) {
        let mark_index = op.mark_index();
        self.mark_order.process_mark_index(op, &mark_index);
        self.marks.push(mark_index);

        self.succ.push(vis_num(op));
        self.top.push(false);

        self.widths
            .push(op.width(SequenceType::Text, self.text_encoding) as u64);

        let count = self.counters.remove(&op.id);

        if let Some(i) = op.get_increment_value() {
            for (succ_idx, op_idx) in count.into_iter().flatten() {
                self.incs[succ_idx] = Some(i);
                self.succ[op_idx] -= 1;
            }
        }

        if let Some(obj_info) = op.obj_info() {
            self.obj_info.insert(op.id, obj_info);
        }
    }

    pub(crate) fn process_succ(&mut self, op_is_counter: bool, id: OpId) {
        if op_is_counter {
            self.counters
                .entry(id)
                .or_default()
                .push((self.incs.len(), self.succ.len() - 1));
        }
        self.incs.push(None); // will update later
    }

    pub(crate) fn finish(mut self) -> (Indexes, MarkOrderValidator) {
        self.flush();

        let text = self
            .widths
            .iter()
            .zip(self.top.iter())
            .map(|(w, t)| if *t { Some(*w as u32) } else { None })
            .collect();

        let visible: Vec<bool> = self.succ.iter().map(|&n| n == 0).collect();
        let visible = hexane::Column::from_values(visible);

        let top: Vec<bool> = self.top.to_vec();
        let top = hexane::PrefixColumn::from_values(top);

        let inc = hexane::Column::from_values(self.incs);

        let mut mark = MarkIndexColumn::new();
        mark.extend(0, self.marks);

        let obj_info = self.obj_info;

        (
            Indexes {
                text,
                top,
                visible,
                inc,
                mark,
                obj_info,
            },
            self.mark_order,
        )
    }
}

fn vis_num(op: &Op<'_>) -> u32 {
    if op.is_inc() {
        u32::MAX
    } else {
        op.succ().len() as u32
    }
}