mirror of
https://github.com/loro-dev/loro.git
synced 2024-12-01 04:28:18 +00:00
fix: vec slice bug
This commit is contained in:
parent
a9d57bfc14
commit
cf3e3ee361
7 changed files with 112 additions and 159 deletions
|
@ -278,9 +278,11 @@ impl Tracker {
|
|||
}
|
||||
}
|
||||
|
||||
self.content.update_at_cursors(
|
||||
&mut cursors,
|
||||
&mut |v| {
|
||||
let len = cursors.len();
|
||||
self.content.update_at_cursors_with_args(
|
||||
&cursors,
|
||||
&vec![(); len],
|
||||
&mut |v, _| {
|
||||
v.status.apply(change);
|
||||
},
|
||||
&mut make_notify(&mut self.id_to_cursor),
|
||||
|
|
|
@ -134,6 +134,22 @@ impl ContentMap {
|
|||
}
|
||||
}
|
||||
|
||||
assert_eq!(len, ans.len());
|
||||
ans
|
||||
}
|
||||
|
||||
pub fn get_id_spans(&self, pos: usize, len: usize) -> RleVec<IdSpan> {
|
||||
let mut ans = RleVec::new();
|
||||
for cursor in self.iter_range(pos, Some(pos + len)) {
|
||||
let id = cursor.as_ref().id;
|
||||
let cursor = cursor.unwrap();
|
||||
ans.push(IdSpan::new(
|
||||
id.client_id,
|
||||
id.counter + (cursor.offset as i32),
|
||||
id.counter + (cursor.offset + cursor.len) as i32,
|
||||
));
|
||||
}
|
||||
|
||||
ans
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,101 +177,8 @@ impl HasLength for YSpan {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use tabled::{Style, Table, Tabled};
|
||||
impl Tabled for YSpan {
|
||||
const LENGTH: usize = 7;
|
||||
|
||||
fn fields(&self) -> Vec<std::borrow::Cow<'_, str>> {
|
||||
vec![
|
||||
self.id.to_string().into(),
|
||||
self.len.to_string().into(),
|
||||
self.status.future.to_string().into(),
|
||||
self.status.delete_times.to_string().into(),
|
||||
self.status.undo_times.to_string().into(),
|
||||
self.origin_left
|
||||
.map(|id| id.to_string())
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
self.origin_right
|
||||
.map(|id| id.to_string())
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
]
|
||||
}
|
||||
|
||||
fn headers() -> Vec<Cow<'static, str>> {
|
||||
vec![
|
||||
"id".into(),
|
||||
"len".into(),
|
||||
"future".into(),
|
||||
"del".into(),
|
||||
"undo".into(),
|
||||
"origin\nleft".into(),
|
||||
"origin\nright".into(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table() {
|
||||
let y_spans = vec![
|
||||
YSpan {
|
||||
id: ID::new(1, 0),
|
||||
len: 1,
|
||||
status: Status::new(),
|
||||
origin_left: None,
|
||||
origin_right: None,
|
||||
slice: Default::default(),
|
||||
},
|
||||
YSpan {
|
||||
id: ID::new(1, 1),
|
||||
len: 1,
|
||||
status: Status::new(),
|
||||
origin_left: Some(ID {
|
||||
client_id: 0,
|
||||
counter: 2,
|
||||
}),
|
||||
origin_right: None,
|
||||
slice: Default::default(),
|
||||
},
|
||||
YSpan {
|
||||
id: ID::new(1, 2),
|
||||
len: 1,
|
||||
status: Status {
|
||||
future: true,
|
||||
delete_times: 5,
|
||||
undo_times: 3,
|
||||
},
|
||||
origin_left: None,
|
||||
origin_right: None,
|
||||
slice: Default::default(),
|
||||
},
|
||||
YSpan {
|
||||
id: ID::new(1, 3),
|
||||
len: 1,
|
||||
status: Status::new(),
|
||||
origin_left: None,
|
||||
origin_right: None,
|
||||
slice: Default::default(),
|
||||
},
|
||||
YSpan {
|
||||
id: ID::new(1, 4),
|
||||
len: 1,
|
||||
status: Status::new(),
|
||||
origin_left: None,
|
||||
origin_right: None,
|
||||
slice: Default::default(),
|
||||
},
|
||||
];
|
||||
let mut t = Table::new(y_spans);
|
||||
t.with(Style::rounded());
|
||||
println!("{}", &t)
|
||||
}
|
||||
|
||||
#[cfg(any(test, features = "fuzzing"))]
|
||||
pub mod test {
|
||||
use crate::{
|
||||
container::{ContainerID, ContainerType},
|
||||
id::ROOT_ID,
|
||||
|
|
|
@ -165,20 +165,56 @@ mod test {
|
|||
|
||||
#[cfg(feature = "fuzzing")]
|
||||
pub mod fuzz {
|
||||
#![allow(unused_imports)]
|
||||
use crdt_list::{
|
||||
test::{Action, TestFramework},
|
||||
yata::Yata,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use tabled::Tabled;
|
||||
impl Tabled for YSpan {
|
||||
const LENGTH: usize = 7;
|
||||
|
||||
fn fields(&self) -> Vec<std::borrow::Cow<'_, str>> {
|
||||
vec![
|
||||
self.id.to_string().into(),
|
||||
self.len.to_string().into(),
|
||||
self.status.future.to_string().into(),
|
||||
self.status.delete_times.to_string().into(),
|
||||
self.status.undo_times.to_string().into(),
|
||||
self.origin_left
|
||||
.map(|id| id.to_string())
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
self.origin_right
|
||||
.map(|id| id.to_string())
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
]
|
||||
}
|
||||
|
||||
fn headers() -> Vec<Cow<'static, str>> {
|
||||
vec![
|
||||
"id".into(),
|
||||
"len".into(),
|
||||
"future".into(),
|
||||
"del".into(),
|
||||
"undo".into(),
|
||||
"origin\nleft".into(),
|
||||
"origin\nright".into(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
use crdt_list::test::{Action, TestFramework};
|
||||
use rle::RleVec;
|
||||
use tabled::TableIteratorExt;
|
||||
|
||||
use crate::{
|
||||
container::text::{
|
||||
text_content::ListSlice,
|
||||
tracker::{y_span::StatusChange, Tracker},
|
||||
tracker::{
|
||||
y_span::{StatusChange, YSpan},
|
||||
Tracker,
|
||||
},
|
||||
},
|
||||
id::{ClientID, ID},
|
||||
span::{self, IdSpan},
|
||||
span::IdSpan,
|
||||
};
|
||||
|
||||
use super::YataImpl;
|
||||
|
@ -230,8 +266,8 @@ pub mod fuzz {
|
|||
if aa != bb {
|
||||
dbg!(a.client_id);
|
||||
dbg!(b.client_id);
|
||||
dbg!(aa.vec());
|
||||
dbg!(bb.vec());
|
||||
println!("{}", aa.vec().table());
|
||||
println!("{}", bb.vec().table());
|
||||
// dbg!(&a.content);
|
||||
// dbg!(&b.content);
|
||||
}
|
||||
|
@ -300,52 +336,17 @@ pub mod fuzz {
|
|||
5,
|
||||
100,
|
||||
vec![
|
||||
NewOp {
|
||||
client_id: 255,
|
||||
pos: 11,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 255,
|
||||
pos: 252,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 255,
|
||||
pos: 64,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 151,
|
||||
pos: 151,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 151,
|
||||
pos: 151,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 255,
|
||||
pos: 252,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 151,
|
||||
pos: 158,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 254,
|
||||
pos: 121,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 3,
|
||||
pos: 255,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 144,
|
||||
},
|
||||
Delete {
|
||||
client_id: 255,
|
||||
pos: 255,
|
||||
len: 134,
|
||||
client_id: 2,
|
||||
pos: 58,
|
||||
len: 177,
|
||||
},
|
||||
Sync { from: 2, to: 0 },
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 255,
|
||||
len: 255,
|
||||
},
|
||||
Sync { from: 29, to: 29 },
|
||||
],
|
||||
)
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ macro_rules! debug_log {
|
|||
($($arg:tt)*) => {{
|
||||
if cfg!(test) {
|
||||
use colored::Colorize;
|
||||
print!("{}:{}\t", file!().purple(), line!().to_string().purple());
|
||||
println!($($arg)*);
|
||||
// print!("{}:{}\t", file!().purple(), line!().to_string().purple());
|
||||
// println!($($arg)*);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -103,12 +103,15 @@ impl<T: HasLength> HasLength for &T {
|
|||
impl<T: HasLength + Sliceable, A: Array<Item = T>> Sliceable for SmallVec<A> {
|
||||
fn slice(&self, from: usize, to: usize) -> Self {
|
||||
let mut index = 0;
|
||||
let mut ans = smallvec::smallvec![];
|
||||
let mut ans: SmallVec<A> = smallvec::smallvec![];
|
||||
if to == from {
|
||||
return ans;
|
||||
}
|
||||
|
||||
for item in self.iter() {
|
||||
if index < to && from < index + item.content_len() {
|
||||
let start = if index < from { from - index } else { 0 };
|
||||
let len = (item.content_len() - start).min(to - index);
|
||||
ans.push(item.slice(start, start + len));
|
||||
ans.push(item.slice(start, item.content_len().min(to - index)));
|
||||
}
|
||||
|
||||
index += item.content_len();
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
cursor::SafeCursorMut,
|
||||
tree_trait::{FindPosResult, Position},
|
||||
},
|
||||
Sliceable,
|
||||
HasLength, Sliceable,
|
||||
};
|
||||
use std::fmt::{Debug, Error, Formatter};
|
||||
|
||||
|
@ -427,8 +427,9 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let mut ans = SmallVec::new();
|
||||
let mut ans: SmallVec<[T; 2]> = SmallVec::new();
|
||||
ans.push(self.children[child_index].clone());
|
||||
let ans_len = self.children[child_index].content_len();
|
||||
for i in 0..offsets.len() {
|
||||
let offset = offsets[i];
|
||||
let len = lens[i];
|
||||
|
@ -439,12 +440,13 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
|
|||
update_fn(span, arg);
|
||||
}
|
||||
|
||||
let mut end = ans.slice(offset + len, ans.len());
|
||||
let mut end = ans.slice(offset + len, ans_len);
|
||||
ans = ans.slice(0, offset);
|
||||
ans.append(&mut target_spans);
|
||||
ans.append(&mut end);
|
||||
}
|
||||
|
||||
debug_assert_eq!(ans_len, ans.iter().map(|x| x.content_len()).sum());
|
||||
Some(ans)
|
||||
}
|
||||
|
||||
|
@ -789,3 +791,25 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> Debug for LeafNode<'a, T, A> {
|
|||
debug_struct.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn slice<T: HasLength + Sliceable>(
|
||||
vec: &[T],
|
||||
beginning: usize,
|
||||
from: usize,
|
||||
to: usize,
|
||||
) -> SmallVec<[T; 2]> {
|
||||
let mut index = beginning;
|
||||
let mut ans = smallvec::smallvec![];
|
||||
dbg!(from, to);
|
||||
for item in vec.iter() {
|
||||
if index < to && from < index + item.content_len() {
|
||||
let start = if index < from { from - index } else { 0 };
|
||||
let len = (item.content_len() - start).min(to - index);
|
||||
ans.push(item.slice(start, start + len));
|
||||
}
|
||||
|
||||
index += item.content_len();
|
||||
}
|
||||
|
||||
ans
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue