mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-23 05:24:51 +00:00
test: recursive fuzz test for refactored
This commit is contained in:
parent
a3488c7088
commit
93252c9522
7 changed files with 1431 additions and 1254 deletions
File diff suppressed because it is too large
Load diff
|
@ -33,6 +33,7 @@ pub struct SharedArena {
|
|||
text: Arc<Mutex<AppendOnlyBytes>>,
|
||||
text_utf16_len: Arc<AtomicUsize>,
|
||||
values: Arc<Mutex<Vec<LoroValue>>>,
|
||||
root_c_idx: Arc<Mutex<Vec<ContainerIdx>>>,
|
||||
}
|
||||
|
||||
pub struct StrAllocResult {
|
||||
|
@ -53,6 +54,9 @@ impl SharedArena {
|
|||
container_idx_to_id.push(id.clone());
|
||||
let ans = ContainerIdx::from_index_and_type(idx as u32, id.container_type());
|
||||
container_id_to_idx.insert(id.clone(), ans);
|
||||
if id.is_root() {
|
||||
self.root_c_idx.lock().unwrap().push(ans);
|
||||
}
|
||||
ans
|
||||
}
|
||||
|
||||
|
@ -240,4 +244,8 @@ impl SharedArena {
|
|||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn root_containers(&self) -> Vec<ContainerIdx> {
|
||||
self.root_c_idx.lock().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,20 +107,42 @@ impl ListHandler {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn insert(&self, txn: &mut Transaction, pos: usize, s: &str) {
|
||||
if s.is_empty() {
|
||||
pub fn insert(&self, txn: &mut Transaction, pos: usize, v: LoroValue) {
|
||||
if let Some(container) = v.as_container() {
|
||||
self.insert_container(txn, pos, container.container_type());
|
||||
return;
|
||||
}
|
||||
|
||||
txn.apply_local_op(
|
||||
self.container_idx,
|
||||
crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
|
||||
slice: ListSlice::RawStr(Cow::Borrowed(s)),
|
||||
slice: ListSlice::RawData(Cow::Owned(vec![v])),
|
||||
pos,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn insert_container(
|
||||
&self,
|
||||
txn: &mut Transaction,
|
||||
pos: usize,
|
||||
c_type: ContainerType,
|
||||
) -> ContainerIdx {
|
||||
let id = txn.next_id();
|
||||
let container_id = ContainerID::new_normal(id, c_type);
|
||||
let child_idx = txn.arena.id_to_idx(&container_id).unwrap();
|
||||
txn.arena.set_parent(child_idx, Some(self.container_idx));
|
||||
let v = LoroValue::Container(container_id);
|
||||
txn.apply_local_op(
|
||||
self.container_idx,
|
||||
crate::op::RawOpContent::List(crate::container::list::list_op::ListOp::Insert {
|
||||
slice: ListSlice::RawData(Cow::Owned(vec![v])),
|
||||
pos,
|
||||
}),
|
||||
);
|
||||
child_idx
|
||||
}
|
||||
|
||||
pub fn delete(&self, txn: &mut Transaction, pos: usize, len: usize) {
|
||||
if len == 0 {
|
||||
return;
|
||||
|
@ -181,6 +203,11 @@ impl MapHandler {
|
|||
}
|
||||
|
||||
pub fn insert(&self, txn: &mut Transaction, key: &str, value: LoroValue) {
|
||||
if let Some(value) = value.as_container() {
|
||||
self.insert_container(txn, key, value.container_type());
|
||||
return;
|
||||
}
|
||||
|
||||
txn.apply_local_op(
|
||||
self.container_idx,
|
||||
crate::op::RawOpContent::Map(crate::container::map::MapSet {
|
||||
|
@ -190,6 +217,26 @@ impl MapHandler {
|
|||
);
|
||||
}
|
||||
|
||||
pub fn insert_container(
|
||||
&self,
|
||||
txn: &mut Transaction,
|
||||
key: &str,
|
||||
c_type: ContainerType,
|
||||
) -> ContainerIdx {
|
||||
let id = txn.next_id();
|
||||
let container_id = ContainerID::new_normal(id, c_type);
|
||||
let child_idx = txn.arena.id_to_idx(&container_id).unwrap();
|
||||
txn.arena.set_parent(child_idx, Some(self.container_idx));
|
||||
txn.apply_local_op(
|
||||
self.container_idx,
|
||||
crate::op::RawOpContent::Map(crate::container::map::MapSet {
|
||||
key: key.into(),
|
||||
value: LoroValue::Container(container_id),
|
||||
}),
|
||||
);
|
||||
child_idx
|
||||
}
|
||||
|
||||
pub fn delete(&self, txn: &mut Transaction, key: &str) {
|
||||
txn.apply_local_op(
|
||||
self.container_idx,
|
||||
|
|
|
@ -3,7 +3,10 @@ use std::{
|
|||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use loro_common::LoroValue;
|
||||
|
||||
use crate::{
|
||||
container::{registry::ContainerIdx, ContainerIdRaw},
|
||||
id::PeerID,
|
||||
log_store::encoding::{ConcreteEncodeMode, ENCODE_SCHEMA_VERSION, MAGIC_BYTES},
|
||||
EncodeMode, LoroError, VersionVector,
|
||||
|
@ -15,6 +18,7 @@ use super::{
|
|||
snapshot_encode::{decode_app_snapshot, encode_app_snapshot},
|
||||
state::{AppState, AppStateDiff, ContainerStateDiff},
|
||||
txn::Transaction,
|
||||
ListHandler, MapHandler, TextHandler,
|
||||
};
|
||||
|
||||
/// `LoroApp` serves as the library's primary entry point.
|
||||
|
@ -103,6 +107,10 @@ impl LoroApp {
|
|||
&self.state
|
||||
}
|
||||
|
||||
pub fn get_state_deep_value(&self) -> LoroValue {
|
||||
self.state.lock().unwrap().get_deep_value()
|
||||
}
|
||||
|
||||
pub fn oplog(&self) -> &Arc<Mutex<OpLog>> {
|
||||
&self.oplog
|
||||
}
|
||||
|
@ -179,6 +187,47 @@ impl LoroApp {
|
|||
pub(crate) fn vv_cloned(&self) -> VersionVector {
|
||||
self.oplog.lock().unwrap().vv().clone()
|
||||
}
|
||||
|
||||
/// id can be a str, ContainerID, or ContainerIdRaw.
|
||||
/// if it's str it will use Root container, which will not be None
|
||||
pub fn get_text<I: Into<ContainerIdRaw>>(&self, id: I) -> Option<TextHandler> {
|
||||
let idx = self.get_container_idx(id);
|
||||
idx.map(|x| TextHandler::new(x, Arc::downgrade(&self.state)))
|
||||
}
|
||||
|
||||
/// id can be a str, ContainerID, or ContainerIdRaw.
|
||||
/// if it's str it will use Root container, which will not be None
|
||||
pub fn get_list<I: Into<ContainerIdRaw>>(&self, id: I) -> Option<ListHandler> {
|
||||
let idx = self.get_container_idx(id);
|
||||
idx.map(|x| ListHandler::new(x, Arc::downgrade(&self.state)))
|
||||
}
|
||||
|
||||
/// id can be a str, ContainerID, or ContainerIdRaw.
|
||||
/// if it's str it will use Root container, which will not be None
|
||||
pub fn get_map<I: Into<ContainerIdRaw>>(&self, id: I) -> Option<MapHandler> {
|
||||
let idx = self.get_container_idx(id);
|
||||
idx.map(|x| MapHandler::new(x, Arc::downgrade(&self.state)))
|
||||
}
|
||||
|
||||
fn get_container_idx<I: Into<ContainerIdRaw>>(&self, id: I) -> Option<ContainerIdx> {
|
||||
let id: ContainerIdRaw = id.into();
|
||||
match id {
|
||||
ContainerIdRaw::Root { name } => {
|
||||
Some(self.oplog().lock().unwrap().arena.register_container(
|
||||
&crate::container::ContainerID::Root {
|
||||
name,
|
||||
container_type: crate::ContainerType::Text,
|
||||
},
|
||||
))
|
||||
}
|
||||
ContainerIdRaw::Normal { id: _ } => self
|
||||
.oplog()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.arena
|
||||
.id_to_idx(&id.with_type(crate::ContainerType::Text)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LoroApp {
|
||||
|
|
|
@ -204,6 +204,7 @@ struct PreEncodedState {
|
|||
}
|
||||
|
||||
fn preprocess_app_state(app_state: &AppState) -> PreEncodedState {
|
||||
assert!(!app_state.is_in_txn());
|
||||
let mut peers = Vec::new();
|
||||
let mut peer_lookup = FxHashMap::default();
|
||||
let mut bytes = Vec::new();
|
||||
|
@ -625,6 +626,7 @@ pub fn decode_oplog(
|
|||
|
||||
pub fn decode_state(app_state: &mut AppState, data: &FinalPhase) -> Result<(), LoroError> {
|
||||
assert!(app_state.is_empty());
|
||||
assert!(!app_state.is_in_txn());
|
||||
let arena = app_state.arena.clone();
|
||||
let common = CommonArena::decode(&data)?;
|
||||
let state_arena = TempArena::decode_state_arena(&data)?;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
use enum_as_inner::EnumAsInner;
|
||||
use enum_dispatch::enum_dispatch;
|
||||
|
@ -234,6 +234,67 @@ impl AppState {
|
|||
pub fn is_empty(&self) -> bool {
|
||||
!self.in_txn && self.states.is_empty() && self.arena.is_empty()
|
||||
}
|
||||
|
||||
pub fn get_deep_value(&self) -> LoroValue {
|
||||
let roots = self.arena.root_containers();
|
||||
let mut ans = FxHashMap::with_capacity_and_hasher(roots.len(), Default::default());
|
||||
for root_idx in roots {
|
||||
let id = self.arena.idx_to_id(root_idx).unwrap();
|
||||
match id {
|
||||
loro_common::ContainerID::Root { name, .. } => {
|
||||
ans.insert(name.to_string(), self.get_container_deep_value(root_idx));
|
||||
}
|
||||
loro_common::ContainerID::Normal { .. } => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LoroValue::Map(Arc::new(ans))
|
||||
}
|
||||
|
||||
pub fn get_container_deep_value(&self, container: ContainerIdx) -> LoroValue {
|
||||
let state = self.states.get(&container).unwrap();
|
||||
let value = state.get_value();
|
||||
match value {
|
||||
LoroValue::Container(_) => unreachable!(),
|
||||
LoroValue::List(mut list) => {
|
||||
if list.iter().all(|x| !x.is_container()) {
|
||||
return LoroValue::List(list);
|
||||
}
|
||||
|
||||
let list_mut = Arc::make_mut(&mut list);
|
||||
for item in list_mut.iter_mut() {
|
||||
if item.is_container() {
|
||||
let container = item.as_container().unwrap();
|
||||
let container_idx = self.arena.id_to_idx(container).unwrap();
|
||||
let value = self.get_container_deep_value(container_idx);
|
||||
*item = value;
|
||||
}
|
||||
}
|
||||
|
||||
LoroValue::List(list)
|
||||
}
|
||||
LoroValue::Map(mut map) => {
|
||||
if map.iter().all(|x| !x.1.is_container()) {
|
||||
return LoroValue::Map(map);
|
||||
}
|
||||
|
||||
let map_mut = Arc::make_mut(&mut map);
|
||||
for (_key, value) in map_mut.iter_mut() {
|
||||
if value.is_container() {
|
||||
let container = value.as_container().unwrap();
|
||||
let container_idx = self.arena.id_to_idx(container).unwrap();
|
||||
let new_value = self.get_container_deep_value(container_idx);
|
||||
*value = new_value;
|
||||
}
|
||||
}
|
||||
|
||||
LoroValue::Map(map)
|
||||
}
|
||||
_ => value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_state(kind: ContainerType) -> State {
|
||||
|
|
|
@ -30,7 +30,7 @@ pub struct Transaction {
|
|||
oplog: Arc<Mutex<OpLog>>,
|
||||
frontiers: Frontiers,
|
||||
local_ops: RleVec<[Op; 1]>,
|
||||
arena: SharedArena,
|
||||
pub(super) arena: SharedArena,
|
||||
finished: bool,
|
||||
}
|
||||
|
||||
|
@ -183,6 +183,13 @@ impl Transaction {
|
|||
let state = self.state.lock().unwrap();
|
||||
f(state.get_state(idx).unwrap())
|
||||
}
|
||||
|
||||
pub fn next_id(&self) -> ID {
|
||||
ID {
|
||||
peer: self.peer,
|
||||
counter: self.next_counter,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Transaction {
|
||||
|
|
Loading…
Reference in a new issue