mirror of
https://github.com/loro-dev/loro.git
synced 2025-02-05 20:17:13 +00:00
refactor: reset snapshot
This commit is contained in:
parent
50c7a827c9
commit
590a6e8ee6
14 changed files with 125 additions and 45 deletions
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"cSpell.words": [
|
||||
"smstring"
|
||||
]
|
||||
}
|
|
@ -14,6 +14,8 @@ fxhash = "0.2.1"
|
|||
ring = "0.16.20"
|
||||
moveit = "0.5.0"
|
||||
pin-project = "1.0.10"
|
||||
serde = {version = "1.0.140", features = ["derive"]}
|
||||
thiserror = "1.0.31"
|
||||
|
||||
[dev-dependencies]
|
||||
proptest = "1.0.0"
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
//! Every [Container] can take a [Snapshot], which contains [crate::LoroValue] that describes the state.
|
||||
//!
|
||||
use crate::{
|
||||
op::OpProxy, snapshot::Snapshot, version::VersionVector, InsertContent, InternalString,
|
||||
LogStore, Op, SmString, ID,
|
||||
op::OpProxy, version::VersionVector, InsertContent, InternalString, LogStore, LoroValue, Op,
|
||||
SmString, ID,
|
||||
};
|
||||
use rle::{HasLength, Mergable, Sliceable};
|
||||
use serde::Serialize;
|
||||
use std::{
|
||||
alloc::Layout,
|
||||
any::{self, Any, TypeId},
|
||||
|
@ -26,10 +27,12 @@ pub use manager::*;
|
|||
|
||||
pub trait Container: Debug + Any + Unpin {
|
||||
fn id(&self) -> &ContainerID;
|
||||
fn container_type(&self) -> ContainerType;
|
||||
fn type_(&self) -> ContainerType;
|
||||
fn apply(&mut self, op: &OpProxy);
|
||||
fn snapshot(&mut self) -> Snapshot;
|
||||
fn checkout_version(&mut self, vv: &VersionVector, log: &LogStore);
|
||||
fn checkout_version(&mut self, vv: &VersionVector);
|
||||
fn get_value(&mut self) -> &LoroValue;
|
||||
// TODO: need a custom serializer
|
||||
// fn serialize(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
pub(crate) trait Cast<T> {
|
||||
|
@ -49,7 +52,7 @@ impl<T: Any> Cast<T> for dyn Container {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Debug, Clone)]
|
||||
#[derive(Hash, PartialEq, Eq, Debug, Clone, Serialize)]
|
||||
pub enum ContainerID {
|
||||
/// Root container does not need a insert op to create. It can be created implicitly.
|
||||
Root {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use crate::{InsertContent, SmString, ID};
|
||||
use crate::{smstring::SmString, InsertContent, ID};
|
||||
use rle::{HasLength, Mergable, Sliceable};
|
||||
use serde::Serialize;
|
||||
use std::alloc::Layout;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum Slot {}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
|
||||
pub enum ContainerType {
|
||||
/// See [`crate::text::TextContent`]
|
||||
Text,
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use std::{pin::Pin, ptr::NonNull, rc::Weak};
|
||||
|
||||
use fxhash::FxHashMap;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
container::{Container, ContainerID, ContainerType},
|
||||
id::ID,
|
||||
id::{Counter, ID},
|
||||
op::{utils::downcast_ref, Op},
|
||||
op::{OpContent, OpProxy},
|
||||
value::{InsertValue, LoroValue},
|
||||
version::TotalOrderStamp,
|
||||
ClientID, InternalString, Lamport, LogStore, OpType, Snapshot,
|
||||
ClientID, InternalString, Lamport, LogStore, OpType,
|
||||
};
|
||||
|
||||
use super::MapInsertContent;
|
||||
|
@ -22,12 +23,14 @@ pub struct MapContainer {
|
|||
id: ContainerID,
|
||||
state: FxHashMap<InternalString, ValueSlot>,
|
||||
log_store: NonNull<LogStore>,
|
||||
value: Option<LoroValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ValueSlot {
|
||||
value: InsertValue,
|
||||
order: TotalOrderStamp,
|
||||
counter: Counter,
|
||||
}
|
||||
|
||||
impl MapContainer {
|
||||
|
@ -37,6 +40,7 @@ impl MapContainer {
|
|||
id,
|
||||
state: FxHashMap::default(),
|
||||
log_store: store,
|
||||
value: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,8 +57,10 @@ impl MapContainer {
|
|||
lamport: store.next_lamport(),
|
||||
};
|
||||
|
||||
let id = store.next_id(client_id);
|
||||
let counter = id.counter;
|
||||
store.append_local_ops(vec![Op {
|
||||
id: store.next_id(client_id),
|
||||
id,
|
||||
content: OpContent::Normal {
|
||||
container: self_id,
|
||||
content: Box::new(MapInsertContent {
|
||||
|
@ -64,7 +70,14 @@ impl MapContainer {
|
|||
},
|
||||
}]);
|
||||
|
||||
self.state.insert(key, ValueSlot { value, order });
|
||||
self.state.insert(
|
||||
key,
|
||||
ValueSlot {
|
||||
value,
|
||||
order,
|
||||
counter,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -79,7 +92,7 @@ impl Container for MapContainer {
|
|||
&self.id
|
||||
}
|
||||
|
||||
fn container_type(&self) -> ContainerType {
|
||||
fn type_(&self) -> ContainerType {
|
||||
ContainerType::Map
|
||||
}
|
||||
|
||||
|
@ -104,6 +117,7 @@ impl Container for MapContainer {
|
|||
ValueSlot {
|
||||
value: v.value.clone(),
|
||||
order,
|
||||
counter: op.id().counter,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -112,16 +126,19 @@ impl Container for MapContainer {
|
|||
}
|
||||
}
|
||||
|
||||
fn snapshot(&mut self) -> Snapshot {
|
||||
let mut map = FxHashMap::default();
|
||||
for (key, value) in self.state.iter() {
|
||||
map.insert(key.clone(), value.value.clone().into());
|
||||
fn get_value(&mut self) -> &LoroValue {
|
||||
if self.value.is_none() {
|
||||
let mut map = FxHashMap::default();
|
||||
for (key, value) in self.state.iter() {
|
||||
map.insert(key.clone(), value.value.clone().into());
|
||||
}
|
||||
self.value = Some(LoroValue::Map(map));
|
||||
}
|
||||
|
||||
Snapshot::new(LoroValue::Map(map))
|
||||
self.value.as_ref().unwrap()
|
||||
}
|
||||
|
||||
fn checkout_version(&mut self, _vv: &crate::version::VersionVector, _log: &crate::LogStore) {
|
||||
fn checkout_version(&mut self, vv: &crate::version::VersionVector) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use rle::{HasLength, Mergable, Sliceable};
|
||||
|
||||
use crate::{value::InsertValue, ContentType, InsertContent, InternalString};
|
||||
use crate::{id::ID, value::InsertValue, ContentType, InsertContent, InternalString};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct MapInsertContent {
|
||||
|
|
|
@ -28,7 +28,7 @@ fn basic() {
|
|||
"haha".into() => LoroValue::Integer(1)
|
||||
);
|
||||
|
||||
assert_eq!(*container.snapshot().value(), LoroValue::Map(ans));
|
||||
assert_eq!(*container.get_value(), LoroValue::Map(ans));
|
||||
}
|
||||
|
||||
#[cfg(not(no_proptest))]
|
||||
|
@ -47,9 +47,8 @@ mod map_proptest {
|
|||
for (k, v) in key.iter().zip(value.iter()) {
|
||||
map.insert(k.clone(), v.clone());
|
||||
container.insert(k.clone().into(), v.clone());
|
||||
let snapshot = container.snapshot();
|
||||
let snapshot = snapshot.value().to_map().unwrap();
|
||||
for (key, value) in snapshot.iter() {
|
||||
let snapshot = container.get_value();
|
||||
for (key, value) in snapshot.to_map().unwrap().iter() {
|
||||
assert_eq!(map.get(&key.to_string()).map(|x|x.clone().into()), Some(value.clone()));
|
||||
}
|
||||
}
|
||||
|
|
11
crates/loro-core/src/error.rs
Normal file
11
crates/loro-core/src/error.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum LoroError {
|
||||
// #[error("the data for key `{0}` is not available")]
|
||||
// Redaction(String),
|
||||
// #[error("invalid header (expected {expected:?}, found {found:?})")]
|
||||
// InvalidHeader { expected: String, found: String },
|
||||
// #[error("unknown data store error")]
|
||||
// Unknown,
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
use serde::Serialize;
|
||||
|
||||
pub type ClientID = u64;
|
||||
pub type Counter = u32;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug, Copy, PartialOrd, Ord)]
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug, Copy, PartialOrd, Ord, Serialize)]
|
||||
pub struct ID {
|
||||
pub client_id: u64,
|
||||
pub counter: u32,
|
||||
|
|
|
@ -10,9 +10,11 @@ pub mod id;
|
|||
pub mod op;
|
||||
pub mod version;
|
||||
|
||||
mod error;
|
||||
mod id_span;
|
||||
mod log_store;
|
||||
mod loro;
|
||||
mod smstring;
|
||||
mod snapshot;
|
||||
mod tests;
|
||||
mod value;
|
||||
|
@ -20,9 +22,8 @@ mod value;
|
|||
pub(crate) mod macros;
|
||||
pub(crate) use change::{Change, Lamport, Timestamp};
|
||||
pub(crate) use id::{ClientID, ID};
|
||||
pub(crate) use snapshot::Snapshot;
|
||||
pub(crate) type SmString = SmartString<LazyCompact>;
|
||||
pub(crate) use op::{ContentType, InsertContent, Op, OpContent, OpType};
|
||||
pub(crate) use smstring::SmString;
|
||||
pub(crate) type InternalString = DefaultAtom;
|
||||
|
||||
pub use container::ContainerType;
|
||||
|
@ -30,5 +31,4 @@ pub use log_store::LogStore;
|
|||
pub use loro::*;
|
||||
pub use value::LoroValue;
|
||||
|
||||
use smartstring::{LazyCompact, SmartString};
|
||||
use string_cache::DefaultAtom;
|
||||
|
|
46
crates/loro-core/src/smstring.rs
Normal file
46
crates/loro-core/src/smstring.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use std::ops::DerefMut;
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
use serde::Serialize;
|
||||
use smartstring::LazyCompact;
|
||||
|
||||
use smartstring::SmartString;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone)]
|
||||
pub struct SmString(pub(crate) SmartString<LazyCompact>);
|
||||
|
||||
impl Deref for SmString {
|
||||
type Target = SmartString<LazyCompact>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for SmString {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SmartString<LazyCompact>> for SmString {
|
||||
fn from(s: SmartString<LazyCompact>) -> Self {
|
||||
SmString(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for SmString {
|
||||
fn from(s: String) -> Self {
|
||||
SmString(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for SmString {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.0)
|
||||
}
|
||||
}
|
|
@ -1,16 +1 @@
|
|||
use crate::value::LoroValue;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Snapshot {
|
||||
value: LoroValue,
|
||||
}
|
||||
|
||||
impl Snapshot {
|
||||
pub fn new(value: LoroValue) -> Self {
|
||||
Snapshot { value }
|
||||
}
|
||||
|
||||
pub fn value(&self) -> &LoroValue {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use fxhash::FxHashMap;
|
||||
|
||||
use crate::{container::ContainerID, InternalString, SmString};
|
||||
use crate::{container::ContainerID, smstring::SmString, InternalString};
|
||||
|
||||
/// [LoroValue] is used to represents the state of CRDT at a given version
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone, serde::Serialize)]
|
||||
pub enum LoroValue {
|
||||
Null,
|
||||
Bool(bool),
|
||||
|
|
9
justfile
9
justfile
|
@ -7,3 +7,12 @@ test:
|
|||
# test without proptest
|
||||
test-fast:
|
||||
RUSTFLAGS='--cfg no_proptest' cargo nextest run
|
||||
|
||||
check-unsafe:
|
||||
env RUSTFLAGS="-Funsafe-code --cap-lints=warn" cargo check
|
||||
|
||||
deny:
|
||||
cargo deny check
|
||||
|
||||
crev:
|
||||
cargo crev crate check
|
||||
|
|
Loading…
Reference in a new issue