-
-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: assemble runtime crdt state (#408)
* feat: calc clock in update refs * feat: add refs * feat: impl store * feat: add utils for doc store * feat: add state vector & self check * feat: doc integrate skeleton * feat: new id * test: `get_state` & `get_state_vector` * test: `add_item` & `get_item` * feat: init doc struct * feat: integrate part2 * feat: crdt trait * chore: move update_missing_sv * feat: add split item * feat: doc integrate part3 * chore: ignore dead code temporarily * chore: fix clippy lint
- Loading branch information
1 parent
d0f1b1c
commit 154546a
Showing
15 changed files
with
868 additions
and
139 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 8 additions & 2 deletions
10
libs/jwst-codec/src/doc/id.rs → libs/jwst-codec/src/doc/codec/id.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,19 @@ | ||
use super::*; | ||
|
||
#[derive(Debug)] | ||
#[derive(Debug, Clone, PartialEq)] | ||
pub struct Id { | ||
pub client: u64, | ||
pub clock: u64, | ||
} | ||
|
||
impl Id { | ||
pub fn new(client: u64, clock: u64) -> Self { | ||
Self { client, clock } | ||
} | ||
} | ||
|
||
pub fn read_item_id(input: &[u8]) -> IResult<&[u8], Id> { | ||
let (tail, client) = read_var_u64(input)?; | ||
let (tail, clock) = read_var_u64(tail)?; | ||
Ok((tail, Id { client, clock })) | ||
Ok((tail, Id::new(client, clock))) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
mod any; | ||
mod content; | ||
mod id; | ||
mod item; | ||
mod refs; | ||
mod update; | ||
|
||
pub use any::Any; | ||
pub use content::Content; | ||
pub use id::Id; | ||
pub use item::Item; | ||
pub use refs::StructInfo; | ||
pub use update::{read_update, Update}; | ||
|
||
use super::*; | ||
use any::read_any; | ||
use content::read_content; | ||
use id::read_item_id; | ||
use item::read_item; | ||
use refs::read_client_struct_refs; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
use super::*; | ||
use nom::{multi::count, number::complete::be_u8}; | ||
use std::collections::HashMap; | ||
|
||
enum RawStructInfo { | ||
GC(u64), | ||
Skip(u64), | ||
Item(Item), | ||
} | ||
|
||
struct RawRefs { | ||
client: u64, | ||
refs: Vec<StructInfo>, | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq)] | ||
pub enum StructInfo { | ||
GC { id: Id, len: u64 }, | ||
Skip { id: Id, len: u64 }, | ||
Item { id: Id, item: Item }, | ||
} | ||
|
||
impl StructInfo { | ||
pub fn id(&self) -> &Id { | ||
match self { | ||
StructInfo::GC { id, .. } => id, | ||
StructInfo::Skip { id, .. } => id, | ||
StructInfo::Item { id, .. } => id, | ||
} | ||
} | ||
|
||
pub fn client_id(&self) -> u64 { | ||
self.id().client | ||
} | ||
|
||
pub fn clock(&self) -> u64 { | ||
self.id().clock | ||
} | ||
|
||
pub fn len(&self) -> u64 { | ||
match self { | ||
StructInfo::GC { len, .. } => *len, | ||
StructInfo::Skip { len, .. } => *len, | ||
StructInfo::Item { item, .. } => item.content.clock_len(), | ||
} | ||
} | ||
|
||
pub fn is_gc(&self) -> bool { | ||
matches!(self, StructInfo::GC { .. }) | ||
} | ||
|
||
pub fn is_skip(&self) -> bool { | ||
matches!(self, StructInfo::Skip { .. }) | ||
} | ||
|
||
pub fn is_item(&self) -> bool { | ||
matches!(self, StructInfo::Item { .. }) | ||
} | ||
|
||
pub fn split_item(&mut self, diff: u64) -> JwstCodecResult<(Self, Self)> { | ||
if let Self::Item { id, item } = self { | ||
let right_id = Id::new(id.client, id.clock + diff); | ||
let (left_content, right_content) = item.content.split(diff)?; | ||
|
||
let left_item = StructInfo::Item { | ||
id: id.clone(), | ||
item: Item { | ||
right_id: Some(right_id.clone()), | ||
content: left_content, | ||
..item.clone() | ||
}, | ||
}; | ||
|
||
let right_item = StructInfo::Item { | ||
id: right_id, | ||
item: Item { | ||
left_id: Some(Id::new(id.client, id.clock + diff - 1)), | ||
right_id: item.right_id.clone(), | ||
parent: item.parent.clone(), | ||
parent_sub: item.parent_sub.clone(), | ||
content: right_content, | ||
}, | ||
}; | ||
|
||
Ok((left_item, right_item)) | ||
} else { | ||
Err(JwstCodecError::ItemSplitNotSupport) | ||
} | ||
} | ||
} | ||
|
||
fn read_struct(input: &[u8]) -> IResult<&[u8], RawStructInfo> { | ||
let (input, info) = be_u8(input)?; | ||
let first_5_bit = info & 0b11111; | ||
|
||
match first_5_bit { | ||
0 => { | ||
let (input, len) = read_var_u64(input)?; | ||
Ok((input, RawStructInfo::GC(len))) | ||
} | ||
10 => { | ||
let (input, len) = read_var_u64(input)?; | ||
Ok((input, RawStructInfo::Skip(len))) | ||
} | ||
_ => { | ||
let (input, item) = read_item(input, info, first_5_bit)?; | ||
Ok((input, RawStructInfo::Item(item))) | ||
} | ||
} | ||
} | ||
|
||
fn read_refs(input: &[u8]) -> IResult<&[u8], RawRefs> { | ||
let (input, num_of_structs) = read_var_u64(input)?; | ||
let (input, client) = read_var_u64(input)?; | ||
let (input, clock) = read_var_u64(input)?; | ||
let (input, structs) = count(read_struct, num_of_structs as usize)(input)?; | ||
let (refs, _) = structs | ||
.into_iter() | ||
.fold((vec![], clock), |(mut vec, clock), s| { | ||
let id = Id::new(client, clock); | ||
match s { | ||
RawStructInfo::GC(len) => { | ||
vec.push(StructInfo::GC { id, len }); | ||
(vec, clock + len) | ||
} | ||
RawStructInfo::Skip(len) => { | ||
vec.push(StructInfo::Skip { id, len }); | ||
(vec, clock + len) | ||
} | ||
RawStructInfo::Item(item) => { | ||
let len = item.content.clock_len(); | ||
vec.push(StructInfo::Item { id, item }); | ||
(vec, clock + len) | ||
} | ||
} | ||
}); | ||
|
||
Ok((input, RawRefs { client, refs })) | ||
} | ||
|
||
pub fn read_client_struct_refs(input: &[u8]) -> IResult<&[u8], HashMap<u64, Vec<StructInfo>>> { | ||
let (input, num_of_updates) = read_var_u64(input)?; | ||
let (tail, updates) = count(read_refs, num_of_updates as usize)(input)?; | ||
|
||
Ok(( | ||
tail, | ||
updates.into_iter().map(|u| (u.client, u.refs)).collect(), | ||
)) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_struct_info() { | ||
{ | ||
let struct_info = StructInfo::GC { | ||
id: Id::new(1, 0), | ||
len: 10, | ||
}; | ||
assert_eq!(struct_info.len(), 10); | ||
assert_eq!(struct_info.client_id(), 1); | ||
assert_eq!(struct_info.clock(), 0); | ||
} | ||
|
||
{ | ||
let struct_info = StructInfo::Skip { | ||
id: Id::new(2, 0), | ||
len: 20, | ||
}; | ||
assert_eq!(struct_info.len(), 20); | ||
assert_eq!(struct_info.client_id(), 2); | ||
assert_eq!(struct_info.clock(), 0); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
use super::*; | ||
use nom::multi::count; | ||
use std::collections::HashMap; | ||
|
||
#[derive(Debug)] | ||
pub struct Delete { | ||
pub clock: u64, | ||
pub clock_len: u64, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct DeleteSets { | ||
pub client: u64, | ||
pub deletes: Vec<Delete>, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct Update { | ||
pub delete_sets: Vec<DeleteSets>, | ||
pub structs: HashMap<u64, Vec<StructInfo>>, | ||
} | ||
|
||
fn read_delete(input: &[u8]) -> IResult<&[u8], Delete> { | ||
let (tail, clock) = read_var_u64(input)?; | ||
let (tail, clock_len) = read_var_u64(tail)?; | ||
Ok((tail, Delete { clock, clock_len })) | ||
} | ||
|
||
fn parse_delete_set(input: &[u8]) -> IResult<&[u8], DeleteSets> { | ||
let (input, client) = read_var_u64(input)?; | ||
let (input, num_of_deletes) = read_var_u64(input)?; | ||
let (tail, deletes) = count(read_delete, num_of_deletes as usize)(input)?; | ||
|
||
Ok((tail, DeleteSets { client, deletes })) | ||
} | ||
|
||
fn read_delete_set(input: &[u8]) -> IResult<&[u8], Vec<DeleteSets>> { | ||
let (input, num_of_clients) = read_var_u64(input)?; | ||
let (tail, deletes) = count(parse_delete_set, num_of_clients as usize)(input)?; | ||
|
||
Ok((tail, deletes)) | ||
} | ||
|
||
pub fn read_update(input: &[u8]) -> IResult<&[u8], Update> { | ||
let (tail, structs) = read_client_struct_refs(input)?; | ||
let (tail, delete_sets) = read_delete_set(tail)?; | ||
Ok(( | ||
tail, | ||
Update { | ||
structs, | ||
delete_sets, | ||
}, | ||
)) | ||
} |
Oops, something went wrong.
154546a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
octobase – ./
octobase-toeverything.vercel.app
crdts.cloud
octobase-git-master-toeverything.vercel.app
octobase.vercel.app
octobase.pro