feat: list init

This commit is contained in:
Zixuan Chen 2022-08-26 20:19:38 +08:00
parent da8b2668e7
commit 29c4d2011e
12 changed files with 293 additions and 162 deletions

View file

@ -17,6 +17,7 @@ pin-project = "1.0.10"
serde = {version = "1.0.140", features = ["derive"]}
thiserror = "1.0.31"
im = "15.1.0"
enum-as-inner = "0.5.1"
[dev-dependencies]
proptest = "1.0.0"

View file

@ -20,6 +20,7 @@ use std::{
mod container_content;
mod manager;
pub mod list;
pub mod map;
pub mod text;
pub use container_content::*;

View file

@ -0,0 +1,28 @@
use rle::{HasLength, Mergable, Sliceable};
use crate::{id::ID, value::InsertValue, ContentType, InsertContent, InternalString};
#[derive(Clone, Debug, PartialEq)]
pub struct ListInsertContent {
pub(crate) key: u32,
pub(crate) value: InsertValue,
}
impl Mergable for ListInsertContent {}
impl Sliceable for ListInsertContent {
fn slice(&self, from: usize, to: usize) -> Self {
assert!(from == 0 && to == 1);
self.clone()
}
}
impl HasLength for ListInsertContent {
fn len(&self) -> usize {
1
}
}
impl InsertContent for ListInsertContent {
fn id(&self) -> ContentType {
ContentType::Map
}
}

View file

@ -0,0 +1,2 @@
mod list_container;
mod list_content;

View file

@ -1,3 +1,5 @@
mod text_container;
mod y_span;
pub use y_span::*;
mod text_content;
pub use text_content::*;
mod tracker;

View file

@ -0,0 +1,63 @@
use std::{pin::Pin, ptr::NonNull, rc::Weak};
use fxhash::FxHashMap;
use rle::RleVec;
use serde::Serialize;
use smallvec::SmallVec;
use crate::{
change::Change,
container::{Container, ContainerID, ContainerType},
id::{Counter, ID},
op::{utils::downcast_ref, Op},
op::{OpContent, OpProxy},
span::IdSpan,
value::{InsertValue, LoroValue},
version::TotalOrderStamp,
ClientID, InternalString, Lamport, LogStore, OpType,
};
use super::y_span::YSpan;
#[derive(Clone, Debug)]
struct DagNode {
id: IdSpan,
deps: SmallVec<[ID; 2]>,
}
#[derive(Clone, Debug)]
pub struct TextContainer {
id: ContainerID,
sub_dag: FxHashMap<ClientID, RleVec<DagNode, ()>>,
log_store: NonNull<LogStore>,
}
impl TextContainer {
pub fn insert(&mut self, pos: usize, text: &str) -> ID {
todo!()
}
pub fn delete(&mut self, pos: usize, len: usize) {}
}
impl Container for TextContainer {
fn id(&self) -> &ContainerID {
&self.id
}
fn type_(&self) -> ContainerType {
ContainerType::Text
}
fn apply(&mut self, op: &OpProxy) {
todo!()
}
fn checkout_version(&mut self, vv: &crate::VersionVector) {
todo!()
}
fn get_value(&mut self) -> &LoroValue {
todo!()
}
}

View file

@ -1,162 +1,19 @@
use crate::{id::Counter, ContentType, InsertContent, ID};
use rle::{HasLength, Mergable, Sliceable};
use std::ops::Range;
#[derive(Debug, Clone)]
pub struct TextContent {
origin_left: ID,
origin_right: ID,
id: ID,
text: String,
}
impl Mergable for TextContent {
fn is_mergable(&self, other: &Self, _: &()) -> bool {
other.id.client_id == self.id.client_id
&& self.id.counter + self.len() as Counter == other.id.counter
&& self.id.client_id == other.origin_left.client_id
&& self.id.counter + self.len() as Counter - 1 == other.origin_left.counter
&& self.origin_right == other.origin_right
}
fn merge(&mut self, other: &Self, _: &()) {
self.text.push_str(&other.text);
}
}
impl Sliceable for TextContent {
fn slice(&self, from: usize, to: usize) -> Self {
if from == 0 {
TextContent {
origin_left: self.origin_left,
origin_right: self.origin_right,
id: self.id,
text: self.text[..to].to_owned(),
}
} else {
TextContent {
origin_left: ID {
client_id: self.id.client_id,
counter: self.id.counter + from as Counter - 1,
},
origin_right: self.origin_right,
id: ID {
client_id: self.id.client_id,
counter: self.id.counter + from as Counter,
},
text: self.text[from..to].to_owned(),
}
}
}
}
impl InsertContent for TextContent {
fn id(&self) -> ContentType {
ContentType::Text
}
}
impl HasLength for TextContent {
fn len(&self) -> usize {
self.text.len()
}
}
#[cfg(test)]
mod test {
use crate::{
container::{ContainerID, ContainerType},
id::ROOT_ID,
ContentType, Op, OpContent, ID,
};
use rle::RleVec;
use super::TextContent;
#[test]
fn test_merge() {
let mut vec: RleVec<Op> = RleVec::new();
vec.push(Op::new(
ID::new(0, 1),
OpContent::Normal {
content: Box::new(TextContent {
origin_left: ID::new(0, 0),
origin_right: ID::null(),
id: ID::new(0, 1),
text: "a".to_owned(),
}),
},
ContainerID::Normal {
id: ROOT_ID,
container_type: ContainerType::Text,
},
));
vec.push(Op::new(
ID::new(0, 2),
OpContent::Normal {
content: Box::new(TextContent {
origin_left: ID::new(0, 1),
origin_right: ID::null(),
id: ID::new(0, 2),
text: "b".to_owned(),
}),
},
ContainerID::Normal {
id: ROOT_ID,
container_type: ContainerType::Text,
},
));
assert_eq!(vec.merged_len(), 1);
let merged = vec.get_merged(0).unwrap();
assert_eq!(merged.insert_content().id(), ContentType::Text);
let text_content =
crate::op::utils::downcast_ref::<TextContent>(&**merged.insert_content()).unwrap();
assert_eq!(text_content.text, "ab");
}
#[test]
fn slice() {
let mut vec: RleVec<Op> = RleVec::new();
vec.push(Op::new(
ID::new(0, 1),
OpContent::Normal {
content: Box::new(TextContent {
origin_left: ID::new(0, 0),
origin_right: ID::null(),
id: ID::new(0, 1),
text: "1234".to_owned(),
}),
},
ContainerID::Normal {
id: ROOT_ID,
container_type: ContainerType::Text,
},
));
vec.push(Op::new(
ID::new(0, 2),
OpContent::Normal {
content: Box::new(TextContent {
origin_left: ID::new(0, 0),
origin_right: ID::new(0, 1),
id: ID::new(0, 5),
text: "5678".to_owned(),
}),
},
ContainerID::Normal {
id: ROOT_ID,
container_type: ContainerType::Text,
},
));
assert_eq!(vec.merged_len(), 2);
assert_eq!(
vec.slice_iter(2, 6)
.map(|x| crate::op::utils::downcast_ref::<TextContent>(
&**x.into_inner().insert_content()
)
.unwrap()
.text
.clone())
.collect::<Vec<String>>(),
vec!["34", "56"]
)
}
use enum_as_inner::EnumAsInner;
use crate::id::ID;
#[derive(Debug, EnumAsInner)]
pub(super) enum TextContent {
Insert {
id: ID,
text: Range<usize>,
pos: usize,
},
Delete {
id: ID,
pos: usize,
len: usize,
},
}

View file

@ -0,0 +1,12 @@
use crate::{span::IdSpan, VersionVector};
use super::text_content::TextContent;
struct Tracker {}
impl Tracker {
fn turn_on(&mut self, id: IdSpan) {}
fn turn_off(&mut self, id: IdSpan) {}
fn checkout(&mut self, vv: VersionVector) {}
fn apply(&mut self, content: TextContent) {}
}

View file

@ -0,0 +1,162 @@
use crate::{id::Counter, ContentType, InsertContent, ID};
use rle::{HasLength, Mergable, Sliceable};
#[derive(Debug, Clone)]
pub struct YSpan {
origin_left: ID,
origin_right: ID,
id: ID,
text: String,
}
impl Mergable for YSpan {
fn is_mergable(&self, other: &Self, _: &()) -> bool {
other.id.client_id == self.id.client_id
&& self.id.counter + self.len() as Counter == other.id.counter
&& self.id.client_id == other.origin_left.client_id
&& self.id.counter + self.len() as Counter - 1 == other.origin_left.counter
&& self.origin_right == other.origin_right
}
fn merge(&mut self, other: &Self, _: &()) {
self.text.push_str(&other.text);
}
}
impl Sliceable for YSpan {
fn slice(&self, from: usize, to: usize) -> Self {
if from == 0 {
YSpan {
origin_left: self.origin_left,
origin_right: self.origin_right,
id: self.id,
text: self.text[..to].to_owned(),
}
} else {
YSpan {
origin_left: ID {
client_id: self.id.client_id,
counter: self.id.counter + from as Counter - 1,
},
origin_right: self.origin_right,
id: ID {
client_id: self.id.client_id,
counter: self.id.counter + from as Counter,
},
text: self.text[from..to].to_owned(),
}
}
}
}
impl InsertContent for YSpan {
fn id(&self) -> ContentType {
ContentType::Text
}
}
impl HasLength for YSpan {
fn len(&self) -> usize {
self.text.len()
}
}
#[cfg(test)]
mod test {
use crate::{
container::{ContainerID, ContainerType},
id::ROOT_ID,
ContentType, Op, OpContent, ID,
};
use rle::RleVec;
use super::YSpan;
#[test]
fn test_merge() {
let mut vec: RleVec<Op> = RleVec::new();
vec.push(Op::new(
ID::new(0, 1),
OpContent::Normal {
content: Box::new(YSpan {
origin_left: ID::new(0, 0),
origin_right: ID::null(),
id: ID::new(0, 1),
text: "a".to_owned(),
}),
},
ContainerID::Normal {
id: ROOT_ID,
container_type: ContainerType::Text,
},
));
vec.push(Op::new(
ID::new(0, 2),
OpContent::Normal {
content: Box::new(YSpan {
origin_left: ID::new(0, 1),
origin_right: ID::null(),
id: ID::new(0, 2),
text: "b".to_owned(),
}),
},
ContainerID::Normal {
id: ROOT_ID,
container_type: ContainerType::Text,
},
));
assert_eq!(vec.merged_len(), 1);
let merged = vec.get_merged(0).unwrap();
assert_eq!(merged.insert_content().id(), ContentType::Text);
let text_content =
crate::op::utils::downcast_ref::<YSpan>(&**merged.insert_content()).unwrap();
assert_eq!(text_content.text, "ab");
}
#[test]
fn slice() {
let mut vec: RleVec<Op> = RleVec::new();
vec.push(Op::new(
ID::new(0, 1),
OpContent::Normal {
content: Box::new(YSpan {
origin_left: ID::new(0, 0),
origin_right: ID::null(),
id: ID::new(0, 1),
text: "1234".to_owned(),
}),
},
ContainerID::Normal {
id: ROOT_ID,
container_type: ContainerType::Text,
},
));
vec.push(Op::new(
ID::new(0, 2),
OpContent::Normal {
content: Box::new(YSpan {
origin_left: ID::new(0, 0),
origin_right: ID::new(0, 1),
id: ID::new(0, 5),
text: "5678".to_owned(),
}),
},
ContainerID::Normal {
id: ROOT_ID,
container_type: ContainerType::Text,
},
));
assert_eq!(vec.merged_len(), 2);
assert_eq!(
vec.slice_iter(2, 6)
.map(|x| crate::op::utils::downcast_ref::<YSpan>(
&**x.into_inner().insert_content()
)
.unwrap()
.text
.clone())
.collect::<Vec<String>>(),
vec!["34", "56"]
)
}
}

View file

@ -1,7 +1,7 @@
use std::{
borrow::{Borrow, BorrowMut},
cell::{Ref, RefCell, RefMut},
ops::{Range},
ops::Range,
rc::Rc,
};

View file

@ -28,6 +28,9 @@ pub struct InternalNode<'a, T: Rle, A: RleTreeTrait<T>> {
_a: PhantomData<A>,
}
// TODO: remove bump field
// TODO: remove parent field?
// TODO: only one child?
pub struct LeafNode<'a, T: Rle, A: RleTreeTrait<T>> {
bump: &'a Bump,
parent: NonNull<InternalNode<'a, T, A>>,