use crate::branch::{Branch, BranchPtr};
use crate::doc::{DocAddr, OffsetKind};
use crate::encoding::read::Error;
use crate::error::UpdateError;
use crate::gc::GCCollector;
use crate::moving::Move;
use crate::slice::{BlockSlice, GCSlice, ItemSlice};
use crate::store::Store;
use crate::transaction::TransactionMut;
use crate::types::text::update_current_attributes;
use crate::types::{Attrs, TypePtr, TypeRef};
use crate::undo::UndoStack;
use crate::updates::decoder::{Decode, Decoder};
use crate::updates::encoder::{Encode, Encoder};
use crate::utils::OptionExt;
use crate::{Any, DeleteSet, Doc, Options, Out, Transact};
use serde::{Deserialize, Serialize};
use smallstr::SmallString;
use std::collections::HashSet;
use std::convert::TryFrom;
use std::fmt::Formatter;
use std::hash::Hash;
use std::ops::{Deref, DerefMut};
use std::panic;
use std::ptr::NonNull;
use std::sync::Arc;
pub const BLOCK_GC_REF_NUMBER: u8 = 0;
pub const BLOCK_ITEM_DELETED_REF_NUMBER: u8 = 1;
pub const BLOCK_ITEM_JSON_REF_NUMBER: u8 = 2;
pub const BLOCK_ITEM_BINARY_REF_NUMBER: u8 = 3;
pub const BLOCK_ITEM_STRING_REF_NUMBER: u8 = 4;
pub const BLOCK_ITEM_EMBED_REF_NUMBER: u8 = 5;
pub const BLOCK_ITEM_FORMAT_REF_NUMBER: u8 = 6;
pub const BLOCK_ITEM_TYPE_REF_NUMBER: u8 = 7;
pub const BLOCK_ITEM_ANY_REF_NUMBER: u8 = 8;
pub const BLOCK_ITEM_DOC_REF_NUMBER: u8 = 9;
pub const BLOCK_SKIP_REF_NUMBER: u8 = 10;
pub const BLOCK_ITEM_MOVE_REF_NUMBER: u8 = 11;
pub const HAS_RIGHT_ORIGIN: u8 = 0b01000000;
pub const HAS_ORIGIN: u8 = 0b10000000;
pub const HAS_PARENT_SUB: u8 = 0b00100000;
pub type ClientID = u64;
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ID {
pub client: ClientID,
pub clock: u32,
}
impl ID {
pub fn new(client: ClientID, clock: u32) -> Self {
ID { client, clock }
}
}
pub(crate) enum BlockCell {
GC(GC),
Block(Box<Item>),
}
impl PartialEq for BlockCell {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(BlockCell::GC(a), BlockCell::GC(b)) => a == b,
(BlockCell::Block(a), BlockCell::Block(b)) => a.id == b.id,
_ => false,
}
}
}
impl std::fmt::Debug for BlockCell {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
BlockCell::GC(gc) => write!(f, "gc({}..={})", gc.start, gc.end),
BlockCell::Block(item) => item.fmt(f),
}
}
}
impl BlockCell {
pub fn clock_start(&self) -> u32 {
match self {
BlockCell::GC(gc) => gc.start,
BlockCell::Block(item) => item.id.clock,
}
}
pub fn clock_end(&self) -> u32 {
match self {
BlockCell::GC(gc) => gc.end,
BlockCell::Block(item) => item.id.clock + item.len - 1,
}
}
#[inline]
pub fn clock_range(&self) -> (u32, u32) {
match self {
BlockCell::GC(gc) => (gc.start, gc.end),
BlockCell::Block(block) => block.clock_range(),
}
}
pub fn is_deleted(&self) -> bool {
match self {
BlockCell::GC(_) => true,
BlockCell::Block(item) => item.is_deleted(),
}
}
pub fn len(&self) -> u32 {
match self {
BlockCell::GC(gc) => gc.end - gc.start + 1,
BlockCell::Block(block) => block.len(),
}
}
pub fn as_slice(&self) -> BlockSlice {
match self {
BlockCell::GC(gc) => BlockSlice::GC(GCSlice::from(gc.clone())),
BlockCell::Block(item) => {
let ptr = ItemPtr::from(item);
BlockSlice::Item(ItemSlice::from(ptr))
}
}
}
pub fn as_item(&self) -> Option<ItemPtr> {
if let BlockCell::Block(item) = self {
Some(ItemPtr::from(item))
} else {
None
}
}
}
impl From<Box<Item>> for BlockCell {
fn from(value: Box<Item>) -> Self {
BlockCell::Block(value)
}
}
impl From<GC> for BlockCell {
fn from(gc: GC) -> Self {
BlockCell::GC(gc)
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) struct GC {
pub start: u32,
pub end: u32,
}
impl GC {
#[inline]
pub fn new(start: u32, end: u32) -> Self {
GC { start, end }
}
pub fn len(&self) -> u32 {
self.end - self.start + 1
}
}
impl From<BlockRange> for GC {
fn from(value: BlockRange) -> Self {
let start = value.id.clock;
let end = start + value.len - 1;
GC { start, end }
}
}
impl Encode for GC {
fn encode<E: Encoder>(&self, encoder: &mut E) {
encoder.write_info(BLOCK_GC_REF_NUMBER);
encoder.write_len(self.len());
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Hash)]
pub struct ItemPtr(NonNull<Item>);
unsafe impl Send for ItemPtr {}
unsafe impl Sync for ItemPtr {}
impl ItemPtr {
pub(crate) fn redo<M>(
&mut self,
txn: &mut TransactionMut,
redo_items: &HashSet<ItemPtr>,
items_to_delete: &DeleteSet,
s1: &UndoStack<M>,
s2: &UndoStack<M>,
) -> Option<ItemPtr> {
let self_ptr = self.clone();
let item = self.deref_mut();
if let Some(redone) = item.redone.as_ref() {
let slice = txn.store.blocks.get_item_clean_start(redone)?;
return Some(txn.store.materialize(slice));
}
let mut parent_block = item.parent.as_branch().and_then(|b| b.item);
if let Some(mut parent) = parent_block.clone() {
if parent.is_deleted() {
if parent.redone.is_none()
&& (!redo_items.contains(&parent)
|| parent
.redo(txn, redo_items, items_to_delete, s1, s2)
.is_none())
{
return None;
}
let mut redone = parent.redone;
while let Some(id) = redone.as_ref() {
parent_block = txn
.store
.blocks
.get_item_clean_start(id)
.map(|slice| txn.store.materialize(slice));
redone = parent_block.and_then(|ptr| ptr.redone);
}
}
}
let parent_branch = BranchPtr::from(if let Some(item) = parent_block.as_deref() {
if let ItemContent::Type(b) = &item.content {
b.as_ref()
} else {
item.parent.as_branch().unwrap()
}
} else {
item.parent.as_branch().unwrap()
});
let mut left = None;
let mut right = None;
if let Some(sub) = item.parent_sub.as_ref() {
if item.right.is_some() {
left = Some(self_ptr);
while let Some(left_item) = left.as_deref() {
if let Some(left_right) = left_item.right {
let id = left_right.id();
if left_right.redone.is_some()
|| items_to_delete.is_deleted(id)
|| s1.is_deleted(id)
|| s2.is_deleted(id)
{
left = Some(left_right);
while let Some(item) = left.as_deref() {
if let Some(id) = item.redone.as_ref() {
left = match txn.store.blocks.get_item_clean_start(id) {
None => break,
Some(slice) => {
let ptr = txn.store.materialize(slice);
txn.merge_blocks.push(ptr.id().clone());
Some(ptr)
}
};
} else {
break;
}
}
continue;
}
}
break;
}
if let Some(left_item) = left.as_deref() {
if left_item.right.is_some() {
return None;
}
}
} else {
left = parent_branch.map.get(sub).cloned();
}
} else {
left = item.left;
right = Some(self_ptr);
while let Some(left_item) = left.clone().as_deref() {
let mut left_trace = left;
while let Some(trace) = left_trace.as_deref() {
let p = trace.parent.as_branch().and_then(|p| p.item);
if parent_block != p {
left_trace = if let Some(redone) = trace.redone.as_ref() {
let slice = txn.store.blocks.get_item_clean_start(redone);
slice.map(|s| txn.store.materialize(s))
} else {
None
};
} else {
break;
}
}
if let Some(trace) = left_trace.as_deref() {
let p = trace.parent.as_branch().and_then(|p| p.item);
if parent_block == p {
left = left_trace;
break;
}
}
left = left_item.left.clone();
}
while let Some(right_item) = right.clone().as_deref() {
let mut right_trace = right;
while let Some(trace) = right_trace.as_deref() {
let p = trace.parent.as_branch().and_then(|p| p.item);
if parent_block != p {
right_trace = if let Some(redone) = trace.redone.as_ref() {
let slice = txn.store.blocks.get_item_clean_start(redone);
slice.map(|s| txn.store.materialize(s))
} else {
None
};
} else {
break;
}
}
if let Some(trace) = right_trace.as_deref() {
let p = trace.parent.as_branch().and_then(|p| p.item);
if parent_block == p {
right = right_trace;
break;
}
}
right = right_item.right.clone();
}
}
let next_clock = txn.store.get_local_state();
let next_id = ID::new(txn.store.client_id, next_clock);
let mut redone_item = Item::new(
next_id,
left,
left.map(|p| p.last_id()),
right,
right.map(|p| *p.id()),
TypePtr::Branch(parent_branch),
item.parent_sub.clone(),
item.content.clone(),
)?;
item.redone = Some(*redone_item.id());
redone_item.info.set_keep();
let mut block_ptr = ItemPtr::from(&mut redone_item);
block_ptr.integrate(txn, 0);
txn.store_mut().blocks.push_block(redone_item);
Some(block_ptr)
}
pub(crate) fn keep(&self, keep: bool) {
let mut curr = Some(*self);
while let Some(item) = curr.as_deref_mut() {
if item.info.is_keep() == keep {
break;
} else {
if keep {
item.info.set_keep();
} else {
item.info.clear_keep();
}
curr = item.parent.as_branch().and_then(|b| b.item);
}
}
}
pub(crate) fn delete_as_cleanup(&self, txn: &mut TransactionMut, is_local: bool) {
txn.delete(*self);
if is_local {
txn.delete_set.insert(*self.id(), self.len());
}
}
pub(crate) fn splice(&mut self, offset: u32, encoding: OffsetKind) -> Option<Box<Item>> {
let self_ptr = self.clone();
if offset == 0 {
None
} else {
let item = self.deref_mut();
let client = item.id.client;
let clock = item.id.clock;
let len = item.len;
let content = item.content.splice(offset as usize, encoding).unwrap();
item.len = offset;
let mut new = Box::new(Item {
id: ID::new(client, clock + offset),
len: len - offset,
left: Some(self_ptr),
right: item.right.clone(),
origin: Some(ID::new(client, clock + offset - 1)),
right_origin: item.right_origin.clone(),
content,
parent: item.parent.clone(),
moved: item.moved.clone(),
parent_sub: item.parent_sub.clone(),
info: item.info.clone(),
redone: item.redone.map(|id| ID::new(id.client, id.clock + offset)),
});
let new_ptr = ItemPtr::from(&mut new);
if let Some(right) = item.right.as_deref_mut() {
right.left = Some(new_ptr);
}
if let Some(parent_sub) = item.parent_sub.as_ref() {
if item.right.is_none() {
if let TypePtr::Branch(mut branch) = item.parent {
branch.map.insert(parent_sub.clone(), new_ptr);
}
}
}
item.right = Some(new_ptr);
Some(new)
}
}
pub(crate) fn integrate(&mut self, txn: &mut TransactionMut, offset: u32) -> bool {
let self_ptr = self.clone();
let this = self.deref_mut();
let store = txn.store_mut();
let encoding = store.offset_kind;
if offset > 0 {
this.id.clock += offset;
this.left = store
.blocks
.get_item_clean_end(&ID::new(this.id.client, this.id.clock - 1))
.map(|slice| store.materialize(slice));
this.origin = this.left.as_deref().map(|b: &Item| b.last_id());
this.content = this
.content
.splice(offset as usize, OffsetKind::Utf16)
.unwrap();
this.len -= offset;
}
let parent = match &this.parent {
TypePtr::Branch(branch) => Some(*branch),
TypePtr::Named(name) => {
let branch = store.get_or_create_type(name.clone(), TypeRef::Undefined);
this.parent = TypePtr::Branch(branch);
Some(branch)
}
TypePtr::ID(id) => {
if let Some(item) = store.blocks.get_item(id) {
if let Some(branch) = item.as_branch() {
this.parent = TypePtr::Branch(branch);
Some(branch)
} else {
None
}
} else {
None
}
}
TypePtr::Unknown => return true,
};
let left: Option<&Item> = this.left.as_deref();
let right: Option<&Item> = this.right.as_deref();
let right_is_null_or_has_left = match right {
None => true,
Some(i) => i.left.is_some(),
};
let left_has_other_right_than_self = match left {
Some(i) => i.right != this.right,
_ => false,
};
if let Some(mut parent_ref) = parent {
if (left.is_none() && right_is_null_or_has_left) || left_has_other_right_than_self {
let mut o = if let Some(left) = left {
left.right
} else if let Some(sub) = &this.parent_sub {
let mut o = parent_ref.map.get(sub).cloned();
while let Some(item) = o.as_deref() {
if item.left.is_some() {
o = item.left.clone();
continue;
}
break;
}
o.clone()
} else {
parent_ref.start
};
let mut left = this.left.clone();
let mut conflicting_items = HashSet::new();
let mut items_before_origin = HashSet::new();
while let Some(item) = o {
if Some(item) == this.right {
break;
}
items_before_origin.insert(item);
conflicting_items.insert(item);
if this.origin == item.origin {
if item.id.client < this.id.client {
left = Some(item.clone());
conflicting_items.clear();
} else if this.right_origin == item.right_origin {
break;
}
} else {
if let Some(origin_ptr) = item
.origin
.as_ref()
.and_then(|id| store.blocks.get_item(id))
{
if items_before_origin.contains(&origin_ptr) {
if !conflicting_items.contains(&origin_ptr) {
left = Some(item.clone());
conflicting_items.clear();
}
} else {
break;
}
} else {
break;
}
}
o = item.right.clone();
}
this.left = left;
}
if this.parent_sub.is_none() {
if let Some(item) = this.left.as_deref() {
if item.parent_sub.is_some() {
this.parent_sub = item.parent_sub.clone();
} else if let Some(item) = this.right.as_deref() {
this.parent_sub = item.parent_sub.clone();
}
}
}
if let Some(left) = this.left.as_deref_mut() {
this.right = left.right.replace(self_ptr);
} else {
let r = if let Some(parent_sub) = &this.parent_sub {
let mut r = parent_ref.map.get(parent_sub).cloned();
while let Some(item) = r {
if item.left.is_some() {
r = item.left;
} else {
break;
}
}
r
} else {
let start = parent_ref.start.replace(self_ptr);
start
};
this.right = r;
}
if let Some(right) = this.right.as_deref_mut() {
right.left = Some(self_ptr);
} else if let Some(parent_sub) = &this.parent_sub {
parent_ref.map.insert(parent_sub.clone(), self_ptr);
if let Some(mut left) = this.left {
#[cfg(feature = "weak")]
{
if left.info.is_linked() {
left.info.clear_linked();
this.info.set_linked();
let all_links = &mut txn.store.linked_by;
if let Some(linked_by) = all_links.remove(&left) {
all_links.insert(self_ptr, linked_by);
}
}
}
txn.delete(left);
}
}
if this.parent_sub.is_none() && !this.is_deleted() {
if this.is_countable() {
parent_ref.block_len += this.len;
parent_ref.content_len += this.content_len(encoding);
}
#[cfg(feature = "weak")]
match (this.left, this.right) {
(Some(l), Some(r)) if l.info.is_linked() || r.info.is_linked() => {
crate::types::weak::join_linked_range(self_ptr, txn)
}
_ => {}
}
}
let left_moved = this.left.and_then(|i| i.moved);
let right_moved = this.right.and_then(|i| i.moved);
if left_moved.is_some() || right_moved.is_some() {
if left_moved == right_moved {
this.moved = left_moved;
} else {
#[inline]
fn try_integrate(mut item: ItemPtr, txn: &mut TransactionMut) {
let ptr = item.clone();
if let ItemContent::Move(m) = &mut item.content {
if !m.is_collapsed() {
m.integrate_block(txn, ptr);
}
}
}
if let Some(ptr) = left_moved {
try_integrate(ptr, txn);
}
if let Some(ptr) = right_moved {
try_integrate(ptr, txn);
}
}
}
match &mut this.content {
ItemContent::Deleted(len) => {
txn.delete_set.insert(this.id, *len);
this.mark_as_deleted();
}
ItemContent::Move(m) => m.integrate_block(txn, self_ptr),
ItemContent::Doc(parent_doc, doc) => {
*parent_doc = Some(txn.doc().clone());
{
let mut child_txn = doc.transact_mut();
child_txn.store.parent = Some(self_ptr);
}
let subdocs = txn.subdocs.get_or_init();
subdocs.added.insert(DocAddr::new(doc), doc.clone());
if doc.should_load() {
subdocs.loaded.insert(doc.addr(), doc.clone());
}
}
ItemContent::Format(_, _) => {
}
ItemContent::Type(branch) => {
let ptr = BranchPtr::from(branch);
#[cfg(feature = "weak")]
if let TypeRef::WeakLink(source) = &ptr.type_ref {
source.materialize(txn, ptr);
}
}
_ => {
}
}
txn.add_changed_type(parent_ref, this.parent_sub.clone());
if this.info.is_linked() {
if let Some(links) = txn.store.linked_by.get(&self_ptr).cloned() {
for link in links.iter() {
txn.add_changed_type(*link, this.parent_sub.clone());
}
}
}
let parent_deleted = if let TypePtr::Branch(ptr) = &this.parent {
if let Some(block) = ptr.item {
block.is_deleted()
} else {
false
}
} else {
false
};
if parent_deleted || (this.parent_sub.is_some() && this.right.is_some()) {
true
} else {
false
}
} else {
true
}
}
pub(crate) fn try_squash(&mut self, other: ItemPtr) -> bool {
if self.id.client == other.id.client
&& self.id.clock + self.len() == other.id.clock
&& other.origin == Some(self.last_id())
&& self.right_origin == other.right_origin
&& self.right == Some(other)
&& self.is_deleted() == other.is_deleted()
&& (self.redone.is_none() && other.redone.is_none())
&& (!self.info.is_linked() && !other.info.is_linked()) && self.moved == other.moved
&& self.content.try_squash(&other.content)
{
self.len = self.content.len(OffsetKind::Utf16);
if let Some(mut right_right) = other.right {
right_right.left = Some(*self);
}
if other.info.is_keep() {
self.info.set_keep();
}
self.right = other.right;
true
} else {
false
}
}
pub(crate) fn as_branch(self) -> Option<BranchPtr> {
if let ItemContent::Type(branch) = &self.content {
Some(BranchPtr::from(branch))
} else {
None
}
}
}
impl Deref for ItemPtr {
type Target = Item;
fn deref(&self) -> &Self::Target {
unsafe { self.0.as_ref() }
}
}
impl DerefMut for ItemPtr {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.0.as_mut() }
}
}
impl<'a> From<&'a mut Box<Item>> for ItemPtr {
fn from(block: &'a mut Box<Item>) -> Self {
ItemPtr(NonNull::from(block.as_mut()))
}
}
impl<'a> From<&'a Box<Item>> for ItemPtr {
fn from(block: &'a Box<Item>) -> Self {
ItemPtr(unsafe { NonNull::new_unchecked(block.as_ref() as *const Item as *mut Item) })
}
}
impl Eq for ItemPtr {}
impl PartialEq for ItemPtr {
fn eq(&self, other: &Self) -> bool {
self.id() == other.id()
}
}
impl TryFrom<ItemPtr> for Any {
type Error = ItemPtr;
fn try_from(item: ItemPtr) -> Result<Self, Self::Error> {
match &item.content {
ItemContent::Any(v) => Ok(v[0].clone()),
ItemContent::Embed(v) => Ok(v.clone()),
ItemContent::Binary(v) => Ok(v.clone().into()),
ItemContent::JSON(v) => Ok(v[0].clone().into()),
ItemContent::String(v) => Ok(v.to_string().into()),
_ => Err(item),
}
}
}
impl Item {
#[inline]
pub(crate) fn clock_range(&self) -> (u32, u32) {
let start = self.id.clock;
let end = start + self.len - 1;
(start, end)
}
pub fn encode<E: Encoder>(&self, encoder: &mut E) {
let info = self.info();
let cant_copy_parent_info = info & (HAS_ORIGIN | HAS_RIGHT_ORIGIN) == 0;
encoder.write_info(info);
if let Some(origin_id) = self.origin.as_ref() {
encoder.write_left_id(origin_id);
}
if let Some(right_origin_id) = self.right_origin.as_ref() {
encoder.write_right_id(right_origin_id);
}
if cant_copy_parent_info {
match &self.parent {
TypePtr::Branch(branch) => {
if let Some(block) = branch.item {
encoder.write_parent_info(false);
encoder.write_left_id(block.id());
} else if let Some(name) = branch.name.as_deref() {
encoder.write_parent_info(true);
encoder.write_string(name);
} else {
unreachable!("Could not get parent branch info for item")
}
}
TypePtr::Named(name) => {
encoder.write_parent_info(true);
encoder.write_string(name);
}
TypePtr::ID(id) => {
encoder.write_parent_info(false);
encoder.write_left_id(id);
}
TypePtr::Unknown => {
panic!("Couldn't get item's parent")
}
}
if let Some(parent_sub) = self.parent_sub.as_ref() {
encoder.write_string(parent_sub.as_ref());
}
}
self.content.encode(encoder);
}
pub fn id(&self) -> &ID {
&self.id
}
}
#[derive(Debug)]
pub(crate) struct ItemPosition {
pub parent: TypePtr,
pub left: Option<ItemPtr>,
pub right: Option<ItemPtr>,
pub index: u32,
pub current_attrs: Option<Box<Attrs>>,
}
impl ItemPosition {
pub fn forward(&mut self) -> bool {
if let Some(right) = self.right.as_deref() {
if !right.is_deleted() {
match &right.content {
ItemContent::String(_) | ItemContent::Embed(_) => {
self.index += right.len();
}
ItemContent::Format(key, value) => {
let attrs = self.current_attrs.get_or_init();
update_current_attributes(attrs, key, value.as_ref());
}
_ => {}
}
}
let new = right.right.clone();
self.left = self.right.take();
self.right = new;
return true;
}
false
}
pub fn unset_missing(&self, attributes: &mut Attrs) {
if let Some(attrs) = self.current_attrs.as_ref() {
for (k, _) in attrs.iter() {
if !attributes.contains_key(k) {
attributes.insert(k.clone(), Any::Null);
}
}
}
}
}
const ITEM_FLAG_LINKED: u16 = 0b0001_0000_0000;
const ITEM_FLAG_MARKED: u16 = 0b0000_1000;
const ITEM_FLAG_DELETED: u16 = 0b0000_0100;
const ITEM_FLAG_COUNTABLE: u16 = 0b0000_0010;
const ITEM_FLAG_KEEP: u16 = 0b0000_0001;
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ItemFlags(u16);
impl ItemFlags {
pub fn new(source: u16) -> Self {
ItemFlags(source)
}
#[inline]
fn set(&mut self, value: u16) {
self.0 |= value
}
#[inline]
fn clear(&mut self, value: u16) {
self.0 &= !value
}
#[inline]
fn check(&self, value: u16) -> bool {
self.0 & value == value
}
#[inline]
pub fn is_keep(&self) -> bool {
self.check(ITEM_FLAG_KEEP)
}
#[inline]
pub fn set_keep(&mut self) {
self.set(ITEM_FLAG_KEEP)
}
#[inline]
pub fn clear_keep(&mut self) {
self.clear(ITEM_FLAG_KEEP)
}
#[inline]
pub fn set_countable(&mut self) {
self.set(ITEM_FLAG_COUNTABLE)
}
#[inline]
pub fn clear_countable(&mut self) {
self.clear(ITEM_FLAG_COUNTABLE)
}
#[inline]
pub fn is_countable(&self) -> bool {
self.check(ITEM_FLAG_COUNTABLE)
}
#[inline]
pub fn set_deleted(&mut self) {
self.set(ITEM_FLAG_DELETED)
}
#[inline]
pub fn is_deleted(&self) -> bool {
self.check(ITEM_FLAG_DELETED)
}
#[inline]
pub fn is_marked(&self) -> bool {
self.check(ITEM_FLAG_MARKED)
}
#[inline]
pub fn is_linked(&self) -> bool {
self.check(ITEM_FLAG_LINKED)
}
#[inline]
pub fn set_linked(&mut self) {
self.set(ITEM_FLAG_LINKED)
}
#[inline]
pub fn clear_linked(&mut self) {
self.clear(ITEM_FLAG_LINKED)
}
}
impl Into<u16> for ItemFlags {
#[inline]
fn into(self) -> u16 {
self.0
}
}
#[derive(PartialEq)]
pub struct Item {
pub(crate) id: ID,
pub(crate) len: u32,
pub(crate) left: Option<ItemPtr>,
pub(crate) right: Option<ItemPtr>,
pub(crate) origin: Option<ID>,
pub(crate) right_origin: Option<ID>,
pub(crate) content: ItemContent,
pub(crate) parent: TypePtr,
pub(crate) redone: Option<ID>,
pub(crate) parent_sub: Option<Arc<str>>,
pub(crate) moved: Option<ItemPtr>,
pub(crate) info: ItemFlags,
}
#[derive(PartialEq, Eq, Clone)]
pub struct BlockRange {
pub id: ID,
pub len: u32,
}
impl BlockRange {
pub fn new(id: ID, len: u32) -> Self {
BlockRange { id, len }
}
pub fn last_id(&self) -> ID {
ID::new(self.id.client, self.id.clock + self.len)
}
pub fn slice(&self, offset: u32) -> Self {
let mut next = self.clone();
next.id.clock += offset;
next.len -= offset;
next
}
pub(crate) fn integrate(&mut self, pivot: u32) -> bool {
if pivot > 0 {
self.id.clock += pivot;
self.len -= pivot;
}
false
}
#[inline]
pub(crate) fn merge(&mut self, other: &Self) {
self.len += other.len;
}
pub fn contains(&self, id: &ID) -> bool {
self.id.client == id.client
&& id.clock >= self.id.clock
&& id.clock < self.id.clock + self.len
}
}
impl std::fmt::Debug for BlockRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl std::fmt::Display for BlockRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}-{})", self.id, self.len)
}
}
impl Item {
pub(crate) fn new(
id: ID,
left: Option<ItemPtr>,
origin: Option<ID>,
right: Option<ItemPtr>,
right_origin: Option<ID>,
parent: TypePtr,
parent_sub: Option<Arc<str>>,
content: ItemContent,
) -> Option<Box<Item>> {
let info = ItemFlags::new(if content.is_countable() {
ITEM_FLAG_COUNTABLE
} else {
0
});
let len = content.len(OffsetKind::Utf16);
if len == 0 {
return None;
}
let root_name = if let TypePtr::Named(root) = &parent {
Some(root.clone())
} else {
None
};
let mut item = Box::new(Item {
id,
len,
left,
right,
origin,
right_origin,
content,
parent,
parent_sub,
info,
moved: None,
redone: None,
});
let item_ptr = ItemPtr::from(&mut item);
if let ItemContent::Type(branch) = &mut item.content {
branch.item = Some(item_ptr);
if branch.name.is_none() {
branch.name = root_name;
}
}
Some(item)
}
pub fn contains(&self, id: &ID) -> bool {
self.id.client == id.client
&& id.clock >= self.id.clock
&& id.clock < self.id.clock + self.len()
}
pub fn is_deleted(&self) -> bool {
self.info.is_deleted()
}
pub fn is_countable(&self) -> bool {
self.info.is_countable()
}
pub(crate) fn mark_as_deleted(&mut self) {
self.info.set_deleted()
}
pub(crate) fn repair(&mut self, store: &mut Store) -> Result<(), UpdateError> {
if let Some(origin) = self.origin.as_ref() {
self.left = store
.blocks
.get_item_clean_end(origin)
.map(|slice| store.materialize(slice));
}
if let Some(origin) = self.right_origin.as_ref() {
self.right = store
.blocks
.get_item_clean_start(origin)
.map(|slice| store.materialize(slice));
}
self.parent = match &self.parent {
TypePtr::Branch(branch_ptr) => TypePtr::Branch(*branch_ptr),
TypePtr::Unknown => match (self.left, self.right) {
(Some(item), _) if item.parent != TypePtr::Unknown => {
self.parent_sub = item.parent_sub.clone();
item.parent.clone()
}
(_, Some(item)) if item.parent != TypePtr::Unknown => {
self.parent_sub = item.parent_sub.clone();
item.parent.clone()
}
_ => TypePtr::Unknown,
},
TypePtr::Named(name) => {
let branch = store.get_or_create_type(name.clone(), TypeRef::Undefined);
TypePtr::Branch(branch)
}
TypePtr::ID(id) => {
let ptr = store.blocks.get_item(id);
if let Some(item) = ptr {
match &item.content {
ItemContent::Type(branch) => {
TypePtr::Branch(BranchPtr::from(branch.as_ref()))
}
ItemContent::Deleted(_) => TypePtr::Unknown,
other => {
return Err(UpdateError::InvalidParent(
id.clone(),
other.get_ref_number(),
))
}
}
} else {
TypePtr::Unknown
}
}
};
Ok(())
}
pub fn len(&self) -> u32 {
self.len
}
pub fn content_len(&self, kind: OffsetKind) -> u32 {
self.content.len(kind)
}
pub fn last_id(&self) -> ID {
ID::new(self.id.client, self.id.clock + self.len() - 1)
}
pub fn info(&self) -> u8 {
let info = if self.origin.is_some() { HAS_ORIGIN } else { 0 } | if self.right_origin.is_some() { HAS_RIGHT_ORIGIN } else { 0 } | if self.parent_sub.is_some() { HAS_PARENT_SUB } else { 0 }
| (self.content.get_ref_number() & 0b1111);
info
}
pub(crate) fn gc(&mut self, collector: &mut GCCollector, parent_gc: bool) {
if self.is_deleted() && !self.info.is_keep() {
self.content.gc(collector);
let len = self.len();
if parent_gc {
collector.mark(&self.id);
} else {
self.content = ItemContent::Deleted(len);
self.info.clear_countable();
}
}
}
}
#[derive(PartialEq, PartialOrd, Eq, Ord, Debug, Clone)]
pub struct SplittableString {
content: SmallString<[u8; 8]>,
}
impl SplittableString {
pub fn len(&self, kind: OffsetKind) -> usize {
let len = self.content.len();
if len == 1 {
len } else {
match kind {
OffsetKind::Bytes => len,
OffsetKind::Utf16 => self.utf16_len(),
}
}
}
#[inline(always)]
pub fn as_str(&self) -> &str {
self.content.as_str()
}
#[inline(always)]
pub fn utf16_len(&self) -> usize {
self.encode_utf16().count()
}
pub(crate) fn block_offset(&self, offset: u32, kind: OffsetKind) -> u32 {
match kind {
OffsetKind::Utf16 => offset,
OffsetKind::Bytes => {
let mut remaining = offset;
let mut i = 0;
for c in self.content.chars() {
if remaining == 0 {
break;
}
remaining -= c.len_utf8() as u32;
i += c.len_utf16() as u32;
}
i
}
}
}
pub fn push_str(&mut self, str: &str) {
self.content.push_str(str);
}
}
impl std::fmt::Display for SplittableString {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.content.fmt(f)
}
}
impl Into<SmallString<[u8; 8]>> for SplittableString {
#[inline(always)]
fn into(self) -> SmallString<[u8; 8]> {
self.content
}
}
impl Into<Box<str>> for SplittableString {
#[inline(always)]
fn into(self) -> Box<str> {
self.content.into_string().into_boxed_str()
}
}
impl From<SmallString<[u8; 8]>> for SplittableString {
fn from(content: SmallString<[u8; 8]>) -> Self {
SplittableString { content }
}
}
impl<'a> From<&'a str> for SplittableString {
fn from(str: &'a str) -> Self {
Self::from(SmallString::from_str(str))
}
}
impl Deref for SplittableString {
type Target = str;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.content
}
}
pub(crate) fn split_str(str: &str, offset: usize, kind: OffsetKind) -> (&str, &str) {
fn map_utf16_offset(str: &str, offset: u32) -> u32 {
let mut off = 0;
let mut i = 0;
for c in str.chars() {
if i >= offset {
break;
}
off += c.len_utf8() as u32;
i += c.len_utf16() as u32;
}
off
}
let off = match kind {
OffsetKind::Bytes => offset,
OffsetKind::Utf16 => map_utf16_offset(str, offset as u32) as usize,
};
str.split_at(off)
}
#[derive(Debug, PartialEq)]
pub enum ItemContent {
Any(Vec<Any>),
Binary(Vec<u8>),
Deleted(u32),
Doc(Option<Doc>, Doc),
JSON(Vec<String>),
Embed(Any),
Format(Arc<str>, Box<Any>),
String(SplittableString),
Type(Box<Branch>),
Move(Box<Move>),
}
impl ItemContent {
pub fn get_ref_number(&self) -> u8 {
match self {
ItemContent::Any(_) => BLOCK_ITEM_ANY_REF_NUMBER,
ItemContent::Binary(_) => BLOCK_ITEM_BINARY_REF_NUMBER,
ItemContent::Deleted(_) => BLOCK_ITEM_DELETED_REF_NUMBER,
ItemContent::Doc(_, _) => BLOCK_ITEM_DOC_REF_NUMBER,
ItemContent::JSON(_) => BLOCK_ITEM_JSON_REF_NUMBER,
ItemContent::Embed(_) => BLOCK_ITEM_EMBED_REF_NUMBER,
ItemContent::Format(_, _) => BLOCK_ITEM_FORMAT_REF_NUMBER,
ItemContent::String(_) => BLOCK_ITEM_STRING_REF_NUMBER,
ItemContent::Type(_) => BLOCK_ITEM_TYPE_REF_NUMBER,
ItemContent::Move(_) => BLOCK_ITEM_MOVE_REF_NUMBER,
}
}
pub fn is_countable(&self) -> bool {
match self {
ItemContent::Any(_) => true,
ItemContent::Binary(_) => true,
ItemContent::Doc(_, _) => true,
ItemContent::JSON(_) => true,
ItemContent::Embed(_) => true,
ItemContent::String(_) => true,
ItemContent::Type(_) => true,
ItemContent::Deleted(_) => false,
ItemContent::Format(_, _) => false,
ItemContent::Move(_) => false,
}
}
pub fn len(&self, kind: OffsetKind) -> u32 {
match self {
ItemContent::Deleted(deleted) => *deleted,
ItemContent::String(str) => str.len(kind) as u32,
ItemContent::Any(v) => v.len() as u32,
ItemContent::JSON(v) => v.len() as u32,
_ => 1,
}
}
pub fn read(&self, offset: usize, buf: &mut [Out]) -> usize {
if buf.is_empty() {
0
} else {
match self {
ItemContent::Any(values) => {
let mut i = offset;
let mut j = 0;
while i < values.len() && j < buf.len() {
let any = &values[i];
buf[j] = Out::Any(any.clone());
i += 1;
j += 1;
}
j
}
ItemContent::String(v) => {
let chars = v.chars().skip(offset).take(buf.len());
let mut j = 0;
for c in chars {
buf[j] = Out::Any(Any::from(c.to_string()));
j += 1;
}
j
}
ItemContent::JSON(elements) => {
let mut i = offset;
let mut j = 0;
while i < elements.len() && j < buf.len() {
let elem = elements[i].as_str();
buf[j] = Out::Any(Any::from(elem));
i += 1;
j += 1;
}
j
}
ItemContent::Binary(v) => {
buf[0] = Out::Any(Any::from(v.deref()));
1
}
ItemContent::Doc(_, doc) => {
buf[0] = Out::YDoc(doc.clone());
1
}
ItemContent::Type(c) => {
let branch_ref = BranchPtr::from(c);
buf[0] = branch_ref.into();
1
}
ItemContent::Embed(v) => {
buf[0] = Out::Any(v.clone());
1
}
ItemContent::Move(_) => 0,
ItemContent::Deleted(_) => 0,
ItemContent::Format(_, _) => 0,
}
}
}
pub fn get_content(&self) -> Vec<Out> {
let len = self.len(OffsetKind::Utf16) as usize;
let mut values = vec![Out::default(); len];
let read = self.read(0, &mut values);
if read == len {
values
} else {
Vec::default()
}
}
pub fn get_first(&self) -> Option<Out> {
match self {
ItemContent::Any(v) => v.first().map(|a| Out::Any(a.clone())),
ItemContent::Binary(v) => Some(Out::Any(Any::from(v.deref()))),
ItemContent::Deleted(_) => None,
ItemContent::Move(_) => None,
ItemContent::Doc(_, v) => Some(Out::YDoc(v.clone())),
ItemContent::JSON(v) => v.first().map(|v| Out::Any(Any::from(v.deref()))),
ItemContent::Embed(v) => Some(Out::Any(v.clone())),
ItemContent::Format(_, _) => None,
ItemContent::String(v) => Some(Out::Any(Any::from(v.clone().as_str()))),
ItemContent::Type(c) => Some(BranchPtr::from(c).into()),
}
}
pub fn get_last(&self) -> Option<Out> {
match self {
ItemContent::Any(v) => v.last().map(|a| Out::Any(a.clone())),
ItemContent::Binary(v) => Some(Out::Any(Any::from(v.deref()))),
ItemContent::Deleted(_) => None,
ItemContent::Move(_) => None,
ItemContent::Doc(_, v) => Some(Out::YDoc(v.clone())),
ItemContent::JSON(v) => v.last().map(|v| Out::Any(Any::from(v.as_str()))),
ItemContent::Embed(v) => Some(Out::Any(v.clone())),
ItemContent::Format(_, _) => None,
ItemContent::String(v) => Some(Out::Any(Any::from(v.as_str()))),
ItemContent::Type(c) => Some(BranchPtr::from(c).into()),
}
}
pub fn encode_slice<E: Encoder>(&self, encoder: &mut E, start: u32, end: u32) {
match self {
ItemContent::Deleted(_) => encoder.write_len(end - start + 1),
ItemContent::Binary(buf) => encoder.write_buf(buf),
ItemContent::String(s) => {
let slice = if start != 0 {
let (_, right) = split_str(&s, start as usize, OffsetKind::Utf16);
right
} else {
&s
};
let slice = if end != 0 {
let (left, _) =
split_str(&slice, (end - start + 1) as usize, OffsetKind::Utf16);
left
} else {
slice
};
encoder.write_string(slice)
}
ItemContent::Embed(s) => encoder.write_json(s),
ItemContent::JSON(s) => {
encoder.write_len(end - start + 1);
for i in start..=end {
encoder.write_string(s[i as usize].as_str())
}
}
ItemContent::Format(k, v) => {
encoder.write_key(k.as_ref());
encoder.write_json(v.as_ref());
}
ItemContent::Type(inner) => {
inner.type_ref.encode(encoder);
}
ItemContent::Any(any) => {
encoder.write_len(end - start + 1);
for i in start..=end {
encoder.write_any(&any[i as usize]);
}
}
ItemContent::Doc(_, doc) => doc.store().options().encode(encoder),
ItemContent::Move(m) => m.encode(encoder),
}
}
pub fn encode<E: Encoder>(&self, encoder: &mut E) {
match self {
ItemContent::Deleted(len) => encoder.write_len(*len),
ItemContent::Binary(buf) => encoder.write_buf(buf),
ItemContent::String(s) => encoder.write_string(s.as_str()),
ItemContent::Embed(s) => encoder.write_json(s),
ItemContent::JSON(s) => {
encoder.write_len(s.len() as u32);
for json in s.iter() {
encoder.write_string(json.as_str())
}
}
ItemContent::Format(k, v) => {
encoder.write_key(k.as_ref());
encoder.write_json(v.as_ref());
}
ItemContent::Type(inner) => {
inner.type_ref.encode(encoder);
}
ItemContent::Any(any) => {
encoder.write_len(any.len() as u32);
for a in any.iter() {
encoder.write_any(a);
}
}
ItemContent::Doc(_, doc) => doc.store().options().encode(encoder),
ItemContent::Move(m) => m.encode(encoder),
}
}
pub fn decode<D: Decoder>(decoder: &mut D, ref_num: u8) -> Result<Self, Error> {
match ref_num & 0b1111 {
BLOCK_ITEM_DELETED_REF_NUMBER => Ok(ItemContent::Deleted(decoder.read_len()?)),
BLOCK_ITEM_JSON_REF_NUMBER => {
let mut remaining = decoder.read_len()? as i32;
let mut buf = Vec::new();
buf.try_reserve(remaining as usize)?;
while remaining >= 0 {
buf.push(decoder.read_string()?.to_owned());
remaining -= 1;
}
Ok(ItemContent::JSON(buf))
}
BLOCK_ITEM_BINARY_REF_NUMBER => Ok(ItemContent::Binary(decoder.read_buf()?.to_owned())),
BLOCK_ITEM_STRING_REF_NUMBER => Ok(ItemContent::String(decoder.read_string()?.into())),
BLOCK_ITEM_EMBED_REF_NUMBER => Ok(ItemContent::Embed(decoder.read_json()?.into())),
BLOCK_ITEM_FORMAT_REF_NUMBER => Ok(ItemContent::Format(
decoder.read_key()?,
decoder.read_json()?.into(),
)),
BLOCK_ITEM_TYPE_REF_NUMBER => {
let type_ref = TypeRef::decode(decoder)?;
let inner = Branch::new(type_ref);
Ok(ItemContent::Type(inner))
}
BLOCK_ITEM_ANY_REF_NUMBER => {
let len = decoder.read_len()? as usize;
let mut values = Vec::new();
values.try_reserve(len)?;
let mut i = 0;
while i < len {
values.push(decoder.read_any()?);
i += 1;
}
Ok(ItemContent::Any(values))
}
BLOCK_ITEM_MOVE_REF_NUMBER => {
let m = Move::decode(decoder)?;
Ok(ItemContent::Move(Box::new(m)))
}
BLOCK_ITEM_DOC_REF_NUMBER => {
let mut options = Options::decode(decoder)?;
options.should_load = options.should_load || options.auto_load;
Ok(ItemContent::Doc(None, Doc::with_options(options)))
}
_ => Err(Error::UnexpectedValue),
}
}
pub(crate) fn splice(&mut self, offset: usize, encoding: OffsetKind) -> Option<ItemContent> {
match self {
ItemContent::Any(value) => {
let (left, right) = value.split_at(offset);
let left = left.to_vec();
let right = right.to_vec();
*self = ItemContent::Any(left);
Some(ItemContent::Any(right))
}
ItemContent::String(string) => {
let (left, right) = split_str(&string, offset, encoding);
let left: SplittableString = left.into();
let right: SplittableString = right.into();
*self = ItemContent::String(left);
Some(ItemContent::String(right))
}
ItemContent::Deleted(len) => {
let right = ItemContent::Deleted(*len - offset as u32);
*len = offset as u32;
Some(right)
}
ItemContent::JSON(value) => {
let (left, right) = value.split_at(offset);
let left = left.to_vec();
let right = right.to_vec();
*self = ItemContent::JSON(left);
Some(ItemContent::JSON(right))
}
_ => None,
}
}
pub fn try_squash(&mut self, other: &Self) -> bool {
match (self, other) {
(ItemContent::Any(v1), ItemContent::Any(v2)) => {
v1.append(&mut v2.clone());
true
}
(ItemContent::Deleted(v1), ItemContent::Deleted(v2)) => {
*v1 = *v1 + *v2;
true
}
(ItemContent::JSON(v1), ItemContent::JSON(v2)) => {
v1.append(&mut v2.clone());
true
}
(ItemContent::String(v1), ItemContent::String(v2)) => {
v1.push_str(v2.as_str());
true
}
_ => false,
}
}
pub(crate) fn gc(&mut self, collector: &mut GCCollector) {
match self {
ItemContent::Type(branch) => {
let mut curr = branch.start.take();
while let Some(mut item) = curr {
curr = item.right.clone();
item.gc(collector, true);
}
for (_, ptr) in branch.map.drain() {
curr = Some(ptr);
while let Some(mut item) = curr {
curr = item.left.clone();
item.gc(collector, true);
continue;
}
}
}
_ => {}
}
}
}
impl Clone for ItemContent {
fn clone(&self) -> Self {
match self {
ItemContent::Any(array) => ItemContent::Any(array.clone()),
ItemContent::Binary(bytes) => ItemContent::Binary(bytes.clone()),
ItemContent::Deleted(len) => ItemContent::Deleted(*len),
ItemContent::Doc(store, doc) => ItemContent::Doc(store.clone(), doc.clone()),
ItemContent::JSON(array) => ItemContent::JSON(array.clone()),
ItemContent::Embed(json) => ItemContent::Embed(json.clone()),
ItemContent::Format(key, value) => ItemContent::Format(key.clone(), value.clone()),
ItemContent::String(chunk) => ItemContent::String(chunk.clone()),
ItemContent::Type(branch) => ItemContent::Type(Branch::new(branch.type_ref.clone())),
ItemContent::Move(range) => ItemContent::Move(range.clone()),
}
}
}
impl std::fmt::Debug for Item {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl std::fmt::Display for Item {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, len: {}", self.id, self.len)?;
match &self.parent {
TypePtr::Unknown => {}
TypePtr::Branch(b) => {
if let Some(ptr) = b.item.as_ref() {
write!(f, ", parent: {}", ptr.id())?;
} else {
write!(f, ", parent: <root>")?;
}
}
other => {
write!(f, ", parent: {}", other)?;
}
}
if let Some(m) = self.moved {
write!(f, ", moved-to: {}", m)?;
}
if let Some(id) = self.redone.as_ref() {
write!(f, ", redone: {}", id)?;
}
if let Some(origin) = self.origin.as_ref() {
write!(f, ", origin-l: {}", origin)?;
}
if let Some(origin) = self.right_origin.as_ref() {
write!(f, ", origin-r: {}", origin)?;
}
if let Some(left) = self.left.as_ref() {
write!(f, ", left: {}", left.id())?;
}
if let Some(right) = self.right.as_ref() {
write!(f, ", right: {}", right.id())?;
}
if let Some(key) = self.parent_sub.as_ref() {
write!(f, ", '{}' =>", key)?;
} else {
write!(f, ":")?;
}
if self.is_deleted() {
write!(f, " ~{}~", &self.content)?;
} else {
write!(f, " {}", &self.content)?;
}
if self.info.is_linked() {
write!(f, "|linked")?;
}
write!(f, ")")
}
}
impl std::fmt::Display for ItemContent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ItemContent::String(s) => write!(f, "'{}'", s),
ItemContent::Any(s) => {
write!(f, "[")?;
let mut iter = s.iter();
if let Some(a) = iter.next() {
write!(f, "{}", a.to_string())?;
}
while let Some(a) = iter.next() {
write!(f, ", {}", a.to_string())?;
}
write!(f, "]")
}
ItemContent::JSON(s) => {
write!(f, "{{")?;
let mut iter = s.iter();
if let Some(a) = iter.next() {
write!(f, "{}", a)?;
}
while let Some(a) = iter.next() {
write!(f, ", {}", a)?;
}
write!(f, "}}")
}
ItemContent::Format(k, v) => write!(f, "<{}={}>", k, v),
ItemContent::Deleted(s) => write!(f, "deleted({})", s),
ItemContent::Binary(s) => write!(f, "{:?}", s),
ItemContent::Type(inner) => match &inner.type_ref {
TypeRef::Array => {
if let Some(ptr) = inner.start {
write!(f, "<array(head: {})>", ptr)
} else {
write!(f, "<array>")
}
}
TypeRef::Map => {
write!(f, "<map({{")?;
let mut iter = inner.map.iter();
if let Some((k, ptr)) = iter.next() {
write!(f, "'{}': {}", k, ptr)?;
}
while let Some((k, ptr)) = iter.next() {
write!(f, ", '{}': {}", k, ptr)?;
}
write!(f, "}})>")
}
TypeRef::Text => {
if let Some(start) = inner.start {
write!(f, "<text(head: {})>", start)
} else {
write!(f, "<text>")
}
}
TypeRef::XmlElement(name) => {
write!(f, "<xml element: {}>", name)
}
TypeRef::XmlFragment => write!(f, "<xml fragment>"),
TypeRef::XmlHook => write!(f, "<xml hook>"),
TypeRef::XmlText => write!(f, "<xml text>"),
#[cfg(feature = "weak")]
TypeRef::WeakLink(s) => write!(f, "<weak({}..{})>", s.quote_start, s.quote_end),
_ => write!(f, "<undefined type ref>"),
},
ItemContent::Move(m) => std::fmt::Display::fmt(m.as_ref(), f),
ItemContent::Doc(_, doc) => std::fmt::Display::fmt(doc, f),
_ => Ok(()),
}
}
}
impl std::fmt::Display for ItemPosition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "(index: {}", self.index)?;
if let Some(l) = self.left.as_ref() {
write!(f, ", left: {}", l)?;
}
if let Some(r) = self.right.as_ref() {
write!(f, ", right: {}", r)?;
}
write!(f, ")")
}
}
pub trait Prelim: Sized {
type Return: TryFrom<ItemPtr>;
fn into_content(self, txn: &mut TransactionMut) -> (ItemContent, Option<Self>);
fn integrate(self, txn: &mut TransactionMut, inner_ref: BranchPtr);
}
impl<T> Prelim for T
where
T: Into<Any>,
{
type Return = Unused;
fn into_content(self, _txn: &mut TransactionMut) -> (ItemContent, Option<Self>) {
let value: Any = self.into();
(ItemContent::Any(vec![value]), None)
}
fn integrate(self, _txn: &mut TransactionMut, _inner_ref: BranchPtr) {}
}
#[derive(Debug)]
pub(crate) struct PrelimString(pub SmallString<[u8; 8]>);
impl Prelim for PrelimString {
type Return = Unused;
fn into_content(self, _txn: &mut TransactionMut) -> (ItemContent, Option<Self>) {
(ItemContent::String(self.0.into()), None)
}
fn integrate(self, _txn: &mut TransactionMut, _inner_ref: BranchPtr) {}
}
#[repr(transparent)]
pub struct Unused;
impl TryFrom<ItemPtr> for Unused {
type Error = ItemPtr;
#[inline(always)]
fn try_from(_: ItemPtr) -> Result<Self, Self::Error> {
Ok(Unused)
}
}
#[derive(Debug)]
pub enum EmbedPrelim<T> {
Primitive(Any),
Shared(T),
}
impl<T> Prelim for EmbedPrelim<T>
where
T: Prelim,
{
type Return = T::Return;
fn into_content(self, txn: &mut TransactionMut) -> (ItemContent, Option<Self>) {
match self {
EmbedPrelim::Primitive(any) => (ItemContent::Embed(any), None),
EmbedPrelim::Shared(prelim) => {
let (branch, content) = prelim.into_content(txn);
let carrier = if let Some(carrier) = content {
Some(EmbedPrelim::Shared(carrier))
} else {
None
};
(branch, carrier)
}
}
}
fn integrate(self, txn: &mut TransactionMut, inner_ref: BranchPtr) {
if let EmbedPrelim::Shared(carrier) = self {
carrier.integrate(txn, inner_ref)
}
}
}
impl<T> From<T> for EmbedPrelim<T>
where
T: Into<Any>,
{
fn from(value: T) -> Self {
EmbedPrelim::Primitive(value.into())
}
}
impl std::fmt::Display for ID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<{}#{}>", self.client, self.clock)
}
}
impl std::fmt::Debug for ItemPtr {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl std::fmt::Display for ItemPtr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({})", self.id())
}
}
#[cfg(test)]
mod test {
use crate::block::{split_str, SplittableString};
use crate::doc::OffsetKind;
use std::ops::Deref;
#[test]
fn splittable_string_len() {
let s: SplittableString = "Zażółć gęślą jaźń😀 女".into();
assert_eq!(s.len(OffsetKind::Bytes), 34, "wrong byte length");
assert_eq!(s.len(OffsetKind::Utf16), 21, "wrong UTF-16 length");
}
#[test]
fn splittable_string_push_str() {
let mut s: SplittableString = "Zażółć gęślą jaźń😀".into();
s.push_str("ありがとうございます");
assert_eq!(
s.deref(),
&"Zażółć gęślą jaźń😀ありがとうございます".to_string()
);
assert_eq!(s.len(OffsetKind::Bytes), 60, "wrong byte length");
assert_eq!(s.len(OffsetKind::Utf16), 29, "wrong UTF-16 length");
}
#[test]
fn splittable_string_split_str() {
let s: SplittableString = "Zażółć gęślą jaźń😀ありがとうございます".into();
let (a, b) = split_str(&s, 19, OffsetKind::Utf16);
assert_eq!(a, "Zażółć gęślą jaźń😀");
assert_eq!(b, "ありがとうございます");
let (a, b) = split_str(&s, 30, OffsetKind::Bytes);
assert_eq!(a, "Zażółć gęślą jaźń😀");
assert_eq!(b, "ありがとうございます");
}
}