mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-22 21:07:43 +00:00
feat: create doc from snapshot (#136)
This commit is contained in:
parent
c9cf106338
commit
e1ab03f30f
5 changed files with 57 additions and 21 deletions
|
@ -677,7 +677,7 @@ pub fn decode_oplog_v2(oplog: &mut OpLog, input: &[u8]) -> Result<(), LoroError>
|
|||
// convert change into inner format
|
||||
let mut ops = RleVec::new();
|
||||
for op in change.ops {
|
||||
let mut lamport = change.lamport;
|
||||
let lamport = change.lamport;
|
||||
let content = op.content;
|
||||
let op = converter.convert_single_op(
|
||||
&op.container,
|
||||
|
@ -686,7 +686,6 @@ pub fn decode_oplog_v2(oplog: &mut OpLog, input: &[u8]) -> Result<(), LoroError>
|
|||
lamport,
|
||||
content,
|
||||
);
|
||||
lamport += op.atom_len() as Lamport;
|
||||
ops.push(op);
|
||||
}
|
||||
|
||||
|
|
|
@ -83,6 +83,20 @@ impl LoroDoc {
|
|||
doc
|
||||
}
|
||||
|
||||
pub fn from_snapshot(bytes: &[u8]) -> LoroResult<Self> {
|
||||
let doc = Self::new();
|
||||
let (input, mode) = parse_encode_header(bytes)?;
|
||||
match mode {
|
||||
EncodeMode::Snapshot => {
|
||||
decode_app_snapshot(&doc, input, true)?;
|
||||
Ok(doc)
|
||||
}
|
||||
_ => Err(LoroError::DecodeError(
|
||||
"Invalid encode mode".to_string().into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the document empty? (no ops)
|
||||
#[inline(always)]
|
||||
pub fn can_reset_with_snapshot(&self) -> bool {
|
||||
|
@ -340,21 +354,7 @@ impl LoroDoc {
|
|||
bytes: &[u8],
|
||||
origin: string_cache::Atom<string_cache::EmptyStaticAtomSet>,
|
||||
) -> Result<(), LoroError> {
|
||||
if bytes.len() <= 6 {
|
||||
return Err(LoroError::DecodeError("Invalid bytes".into()));
|
||||
}
|
||||
|
||||
let (magic_bytes, input) = bytes.split_at(4);
|
||||
let magic_bytes: [u8; 4] = magic_bytes.try_into().unwrap();
|
||||
if magic_bytes != MAGIC_BYTES {
|
||||
return Err(LoroError::DecodeError("Invalid header bytes".into()));
|
||||
}
|
||||
let (version, input) = input.split_at(1);
|
||||
if version != [ENCODE_SCHEMA_VERSION] {
|
||||
return Err(LoroError::DecodeError("Invalid version".into()));
|
||||
}
|
||||
|
||||
let mode: EncodeMode = input[0].try_into()?;
|
||||
let (input, mode) = parse_encode_header(bytes)?;
|
||||
match mode {
|
||||
EncodeMode::Updates | EncodeMode::RleUpdates | EncodeMode::CompressedRleUpdates => {
|
||||
// TODO: need to throw error if state is in transaction
|
||||
|
@ -386,10 +386,10 @@ impl LoroDoc {
|
|||
}
|
||||
EncodeMode::Snapshot => {
|
||||
if self.can_reset_with_snapshot() {
|
||||
decode_app_snapshot(self, &input[1..], !self.detached)?;
|
||||
decode_app_snapshot(self, input, !self.detached)?;
|
||||
} else {
|
||||
let app = LoroDoc::new();
|
||||
decode_app_snapshot(&app, &input[1..], false)?;
|
||||
decode_app_snapshot(&app, input, false)?;
|
||||
let oplog = self.oplog.lock().unwrap();
|
||||
// TODO: PERF: the ser and de can be optimized out
|
||||
let updates = app.export_from(oplog.vv());
|
||||
|
@ -629,6 +629,23 @@ impl LoroDoc {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_encode_header(bytes: &[u8]) -> Result<(&[u8], EncodeMode), LoroError> {
|
||||
if bytes.len() <= 6 {
|
||||
return Err(LoroError::DecodeError("Invalid import data".into()));
|
||||
}
|
||||
let (magic_bytes, input) = bytes.split_at(4);
|
||||
let magic_bytes: [u8; 4] = magic_bytes.try_into().unwrap();
|
||||
if magic_bytes != MAGIC_BYTES {
|
||||
return Err(LoroError::DecodeError("Invalid header bytes".into()));
|
||||
}
|
||||
let (version, input) = input.split_at(1);
|
||||
if version != [ENCODE_SCHEMA_VERSION] {
|
||||
return Err(LoroError::DecodeError("Invalid version".into()));
|
||||
}
|
||||
let mode: EncodeMode = input[0].try_into()?;
|
||||
Ok((&input[1..], mode))
|
||||
}
|
||||
|
||||
impl Default for LoroDoc {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
|
|
|
@ -208,7 +208,7 @@ impl OpLog {
|
|||
pub(super) fn to_local_op(change: Change<RemoteOp>, converter: &mut OpConverter) -> Change {
|
||||
let mut ops = RleVec::new();
|
||||
for op in change.ops {
|
||||
let mut lamport = change.lamport;
|
||||
let lamport = change.lamport;
|
||||
let content = op.content;
|
||||
let op = converter.convert_single_op(
|
||||
&op.container,
|
||||
|
@ -217,7 +217,6 @@ pub(super) fn to_local_op(change: Change<RemoteOp>, converter: &mut OpConverter)
|
|||
lamport,
|
||||
content,
|
||||
);
|
||||
lamport += op.atom_len() as Lamport;
|
||||
ops.push(op);
|
||||
}
|
||||
Change {
|
||||
|
|
|
@ -43,6 +43,21 @@ fn import_after_init_handlers() {
|
|||
a.import(&b.export_snapshot()).unwrap();
|
||||
a.commit();
|
||||
}
|
||||
fn test_from_snapshot() {
|
||||
let a = LoroDoc::new_auto_commit();
|
||||
a.get_text("text").insert_(0, "0").unwrap();
|
||||
let snapshot = a.export_snapshot();
|
||||
let c = LoroDoc::from_snapshot(&snapshot).unwrap();
|
||||
assert_eq!(a.get_deep_value(), c.get_deep_value());
|
||||
assert_eq!(a.oplog_frontiers(), c.oplog_frontiers());
|
||||
assert_eq!(a.state_frontiers(), c.state_frontiers());
|
||||
let updates = a.export_from(&Default::default());
|
||||
let d = match LoroDoc::from_snapshot(&updates) {
|
||||
Ok(_) => panic!(),
|
||||
Err(e) => e,
|
||||
};
|
||||
assert!(matches!(d, loro_common::LoroError::DecodeError(..)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pending() {
|
||||
|
|
|
@ -146,6 +146,12 @@ impl Loro {
|
|||
Self(doc)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "fromSnapshot")]
|
||||
pub fn from_snapshot(snapshot: &[u8]) -> JsResult<Loro> {
|
||||
let doc = LoroDoc::from_snapshot(snapshot)?;
|
||||
Ok(Loro(doc))
|
||||
}
|
||||
|
||||
pub fn attach(&mut self) {
|
||||
self.0.attach();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue