use crate::block::{ItemPtr, BLOCK_GC_REF_NUMBER, GC, HAS_ORIGIN, HAS_RIGHT_ORIGIN}; use crate::types::TypePtr; use crate::updates::encoder::Encoder; use crate::ID; use std::ops::Deref; #[derive(Debug, Clone, Eq, PartialEq)] pub(crate) enum BlockSlice { Item(ItemSlice), GC(GCSlice), } impl BlockSlice { pub fn clock_start(&self) -> u32 { match self { BlockSlice::Item(s) => s.clock_start(), BlockSlice::GC(s) => s.clock_start(), } } pub fn clock_end(&self) -> u32 { match self { BlockSlice::Item(s) => s.clock_end(), BlockSlice::GC(s) => s.clock_end(), } } pub(crate) fn as_item(&self) -> Option { if let BlockSlice::Item(s) = self { Some(s.ptr) } else { None } } pub fn len(&self) -> u32 { match self { BlockSlice::Item(s) => s.len(), BlockSlice::GC(s) => s.len(), } } #[allow(dead_code)] pub fn is_deleted(&self) -> bool { match self { BlockSlice::Item(s) => s.is_deleted(), BlockSlice::GC(_) => true, } } /// Trim a number of countable elements from the beginning of a current slice. pub fn trim_start(&mut self, count: u32) { match self { BlockSlice::Item(s) => s.trim_start(count), BlockSlice::GC(s) => s.trim_start(count), } } /// Trim a number of countable elements from the end of a current slice. pub fn trim_end(&mut self, count: u32) { match self { BlockSlice::Item(s) => s.trim_end(count), BlockSlice::GC(s) => s.trim_end(count), } } pub fn encode(&self, encoder: &mut E) { match self { BlockSlice::Item(s) => s.encode(encoder), BlockSlice::GC(s) => s.encode(encoder), } } } impl From for BlockSlice { fn from(slice: ItemSlice) -> Self { BlockSlice::Item(slice) } } impl From for BlockSlice { fn from(slice: GCSlice) -> Self { BlockSlice::GC(slice) } } /// Defines a logical slice of an underlying [Item]. Yrs blocks define a series of sequential /// updates performed by a single peer, while [ItemSlice]s enable to refer to a sub-range of these /// blocks without need to splice them. /// /// If an underlying [Item] needs to be spliced to fit the boundaries defined by a corresponding /// [ItemSlice], this can be done with help of transaction (see: [Store::materialize]). #[derive(Debug, Clone, Eq, PartialEq)] pub(crate) struct ItemSlice { pub ptr: ItemPtr, pub start: u32, pub end: u32, } impl ItemSlice { pub fn new(ptr: ItemPtr, start: u32, end: u32) -> Self { debug_assert!(start <= end); ItemSlice { ptr, start, end } } pub fn clock_start(&self) -> u32 { self.ptr.id.clock + self.start } pub fn clock_end(&self) -> u32 { self.ptr.id.clock + self.end } /// Returns the number of elements (counted as Yjs ID clock length) of the block range described /// by current [ItemSlice]. pub fn len(&self) -> u32 { self.end - self.start + 1 } /// Returns the first [ID] covered by this slice (inclusive). pub fn id(&self) -> ID { let mut id = self.ptr.id; id.clock += self.start; id } /// Returns the last [ID] covered by this slice (inclusive). pub fn last_id(&self) -> ID { let mut id = self.ptr.id; id.clock += self.end; id } /// Trim a number of countable elements from the beginning of a current slice. pub(crate) fn trim_start(&mut self, count: u32) { debug_assert!(count <= self.len()); self.start += count; } /// Trim a number of countable elements from the end of a current slice. pub(crate) fn trim_end(&mut self, count: u32) { debug_assert!(count <= self.len()); self.end -= count; } /// Returns true when current [ItemSlice] left boundary is equal to the boundary of the /// [ItemPtr] this slice wraps. pub fn adjacent_left(&self) -> bool { self.start == 0 } /// Returns true when current [ItemSlice] right boundary is equal to the boundary of the /// [ItemPtr] this slice wraps. pub fn adjacent_right(&self) -> bool { self.end == self.ptr.len() - 1 } /// Returns true when boundaries marked by the current [ItemSlice] match the boundaries /// of the underlying [ItemPtr]. pub fn adjacent(&self) -> bool { self.adjacent_left() && self.adjacent_right() } /// Checks if an underlying [Item] has been marked as deleted. pub fn is_deleted(&self) -> bool { self.ptr.is_deleted() } /// Checks if an underlying [Item] has been marked as countable. pub fn is_countable(&self) -> bool { self.ptr.is_countable() } /// Checks if provided `id` exists within the bounds described by current [ItemSlice]. pub fn contains_id(&self, id: &ID) -> bool { let myself = self.ptr.id(); myself.client == id.client && id.clock >= myself.clock + self.start && id.clock <= myself.clock + self.end } pub fn encode(&self, encoder: &mut E) { let item = self.ptr.deref(); let mut info = item.info(); let origin = if self.adjacent_left() { item.origin } else { Some(ID::new(item.id.client, item.id.clock + self.start - 1)) }; if origin.is_some() { info |= HAS_ORIGIN; } let cant_copy_parent_info = info & (HAS_ORIGIN | HAS_RIGHT_ORIGIN) == 0; encoder.write_info(info); if let Some(origin_id) = origin { encoder.write_left_id(&origin_id); } if self.adjacent_right() { if let Some(right_origin_id) = item.right_origin.as_ref() { encoder.write_right_id(right_origin_id); } } if cant_copy_parent_info { match &item.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) = item.parent_sub.as_ref() { encoder.write_string(parent_sub.as_ref()); } } item.content.encode_slice(encoder, self.start, self.end); } /// Returns a [ItemSlice] wrapper for a [Item] identified as a right neighbor of this slice. /// This method doesn't have to be equivalent of [Item::right]: if current slices's end range /// is not an equivalent to the end of underlying [Item], a returned slice will contain the /// same block that starts when the current ends. /// /// # Example /// /// If an underlying block is responsible for clock range of [0..10) and current slice is [2..8) /// then right slice returned by this method will be [8..10). pub fn right(&self) -> Option { let last_clock = self.ptr.len() - 1; if self.end == last_clock { let item = self.ptr.deref(); let right_ptr = item.right?; Some(ItemSlice::from(right_ptr)) } else { Some(ItemSlice::new(self.ptr, self.end + 1, last_clock)) } } /// Returns a [ItemSlice] wrapper for a [Item] identified as a left neighbor of this slice. /// This method doesn't have to be equivalent of [Item::right]: if current slices's start range /// is not an equivalent to the start of underlying [Item], a returned slice will contain the /// range that starts with current underlying block start and end where this slice starts. /// /// # Example /// /// If an underlying block is responsible for clock range of [0..10) and current slice is [2..8) /// then right slice returned by this method will be [0..2). pub fn left(&self) -> Option { if self.start == 0 { let item = self.ptr.deref(); let left_ptr = item.left?; Some(ItemSlice::from(left_ptr)) } else { Some(ItemSlice::new(self.ptr, 0, self.start - 1)) } } } impl From for ItemSlice { fn from(ptr: ItemPtr) -> Self { ItemSlice::new(ptr, 0, ptr.len() - 1) } } #[derive(Debug, Clone, Eq, PartialEq)] pub(crate) struct GCSlice { pub start: u32, pub end: u32, } impl GCSlice { pub fn clock_start(&self) -> u32 { self.start } pub fn clock_end(&self) -> u32 { self.end } /// Trim a number of countable elements from the beginning of a current slice. pub(crate) fn trim_start(&mut self, count: u32) { debug_assert!(count <= self.len()); self.start += count; } /// Trim a number of countable elements from the end of a current slice. pub(crate) fn trim_end(&mut self, count: u32) { debug_assert!(count <= self.len()); self.end -= count; } pub fn len(&self) -> u32 { self.end - self.start + 1 } pub fn encode(&self, encoder: &mut E) { encoder.write_info(BLOCK_GC_REF_NUMBER); encoder.write_len(self.len()); } } impl From for GCSlice { fn from(gc: GC) -> Self { GCSlice { start: gc.start, end: gc.end, } } }