1use super::*;
2
3#[derive(Debug)]
4struct ReplacementContext<'a> {
5 index: &'a mut usize,
6 index_shift: &'a mut usize,
7 to_delete: &'a mut FxHashMap<ContainerID, usize>,
8 container_remap: &'a mut FxHashMap<ContainerID, ContainerID>,
9 deleted_indices: &'a mut Vec<usize>,
10 next_deleted: &'a mut BinaryHeap<Reverse<usize>>,
11}
12
13impl MovableListHandler {
14 #[tracing::instrument(level = "debug", skip_all)]
30 pub fn apply_delta(
31 &self,
32 delta: loro_delta::DeltaRope<
33 loro_delta::array_vec::ArrayVec<ValueOrHandler, 8>,
34 crate::event::ListDeltaMeta,
35 >,
36 container_remap: &mut FxHashMap<ContainerID, ContainerID>,
37 ) -> LoroResult<()> {
38 {
39 let len = self.len();
41 let mut index = 0;
42 for delta_item in delta.iter() {
43 match delta_item {
44 loro_delta::DeltaItem::Retain { len, .. } => {
45 index += *len;
46 }
47 loro_delta::DeltaItem::Replace { delete, .. } => {
48 index += *delete;
49 }
50 }
51
52 if index > len {
53 return Err(LoroError::OutOfBound {
54 pos: index,
55 len,
56 info: "apply_delta".into(),
57 });
58 }
59 }
60 }
61
62 match &self.inner {
63 MaybeDetached::Detached(_) => {
64 unimplemented!();
65 }
66 MaybeDetached::Attached(_) => {
67 let mut to_delete = self.preprocess_deletions(&delta);
76 let mut index = 0;
78 let mut index_shift = 0;
79 let mut deleted_indices = Vec::new();
80 let mut next_deleted = BinaryHeap::new();
81 let mut delta_change: Vec<isize> = Vec::new();
84
85 for delta_item in delta.iter() {
86 match delta_item {
87 loro_delta::DeltaItem::Retain { len, .. } => {
88 index += len;
89 delta_change.push(*len as isize);
90 }
91 loro_delta::DeltaItem::Replace {
92 value,
93 delete,
94 attr,
95 } => {
96 let old_index = index;
98 self.handle_deletions_in_replace(
99 *delete,
100 &mut index,
101 index_shift,
102 &mut next_deleted,
103 );
104 delta_change.push(-((index - old_index) as isize));
105
106 let mut context = ReplacementContext {
108 index: &mut index,
109 index_shift: &mut index_shift,
110 to_delete: &mut to_delete,
111 container_remap,
112 deleted_indices: &mut deleted_indices,
113 next_deleted: &mut next_deleted,
114 };
115
116 self.process_replacements(value, attr, &mut context)
117 .unwrap();
118 delta_change.push(value.len() as isize);
119 }
120 }
121 }
122
123 self.apply_remaining_deletions(delta_change, &mut deleted_indices)?;
125 Ok(())
126 }
127 }
128 }
129
130 fn preprocess_deletions(
140 &self,
141 delta: &loro_delta::DeltaRope<
142 loro_delta::array_vec::ArrayVec<ValueOrHandler, 8>,
143 crate::event::ListDeltaMeta,
144 >,
145 ) -> FxHashMap<ContainerID, usize> {
146 let mut index = 0;
147 let mut to_delete = FxHashMap::default();
148
149 for delta_item in delta.iter() {
150 match delta_item {
151 loro_delta::DeltaItem::Retain { len, .. } => {
152 index += len;
153 }
154 loro_delta::DeltaItem::Replace { delete, .. } => {
155 if *delete > 0 {
156 for i in index..index + *delete {
157 if let Some(LoroValue::Container(c)) = self.get(i) {
158 to_delete.insert(c, i);
159 }
160 }
161 index += *delete;
162 }
163 }
164 }
165 }
166
167 to_delete
168 }
169
170 fn handle_deletions_in_replace(
180 &self,
181 delete_len: usize,
182 index: &mut usize,
183 index_shift: usize,
184 next_deleted: &mut BinaryHeap<Reverse<usize>>,
185 ) {
186 if delete_len > 0 {
187 let mut remaining_deletes = delete_len;
188 while let Some(Reverse(old_index)) = next_deleted.peek() {
189 if *old_index + index_shift < *index + remaining_deletes {
190 assert!(*index <= *old_index + index_shift);
191 assert!(remaining_deletes > 0);
192 next_deleted.pop();
193 remaining_deletes -= 1;
194 } else {
195 break;
196 }
197 }
198
199 *index += remaining_deletes;
201 }
202 }
203
204 fn process_replacements(
212 &self,
213 values: &loro_delta::array_vec::ArrayVec<ValueOrHandler, 8>,
214 attr: &crate::event::ListDeltaMeta,
215 context: &mut ReplacementContext,
216 ) -> LoroResult<()> {
217 for v in values.iter() {
218 match v {
219 ValueOrHandler::Value(LoroValue::Container(old_id)) => {
220 self.apply_insertion(attr, context, old_id.clone())?;
221 }
222 ValueOrHandler::Handler(handler) => {
223 let old_id = handler.id();
224 self.apply_insertion(attr, context, old_id)?;
225 }
226 ValueOrHandler::Value(value) => {
227 self.insert(*context.index, value.clone())?;
228 Self::update_positions_on_insert(context.to_delete, *context.index, 1);
229 *context.index += 1;
230 *context.index_shift += 1;
231 }
232 }
233 }
234
235 Ok(())
236 }
237
238 fn apply_insertion(
239 &self,
240 attr: &crate::event::ListDeltaMeta,
241 context: &mut ReplacementContext<'_>,
242 mut old_id: ContainerID,
243 ) -> Result<(), LoroError> {
244 if !context.to_delete.contains_key(&old_id) {
245 while let Some(new_id) = context.container_remap.get(&old_id) {
246 old_id = new_id.clone();
247 if context.to_delete.contains_key(&old_id) {
248 break;
249 }
250 }
251 }
252 if let Some(old_index) = context.to_delete.remove(&old_id) {
253 if old_index > *context.index {
254 ensure_cov::notify_cov(
255 "loro_internal::handler::movable_list_apply_delta::process_replacements::mov_0",
256 );
257 self.mov(old_index, *context.index)?;
258 context.next_deleted.push(Reverse(old_index));
259 *context.index += 1;
260 *context.index_shift += 1;
261 } else {
262 ensure_cov::notify_cov(
263 "loro_internal::handler::movable_list_apply_delta::process_replacements::mov_1",
264 );
265 self.mov(old_index, *context.index - 1)?;
266 }
267 context.deleted_indices.push(old_index);
268 Self::update_positions_on_delete(context.to_delete, old_index);
269 Self::update_positions_on_insert(context.to_delete, *context.index, 1);
270 } else if !attr.from_move || !self.contains_container(&old_id) {
271 let new_handler = self.insert_container(
273 *context.index,
274 Handler::new_unattached(old_id.container_type()),
275 )?;
276 let new_id = new_handler.id();
277 context.container_remap.insert(old_id, new_id);
278 Self::update_positions_on_insert(context.to_delete, *context.index, 1);
279 *context.index += 1;
280 *context.index_shift += 1;
281 }
282 Ok(())
283 }
284
285 fn contains_container(&self, id: &ContainerID) -> bool {
286 (0..self.len()).any(|index| {
287 matches!(
288 self.get(index),
289 Some(LoroValue::Container(container_id)) if container_id == *id
290 )
291 })
292 }
293
294 fn apply_remaining_deletions(
301 &self,
302 delta: Vec<isize>,
303 deleted_indices: &mut Vec<usize>,
304 ) -> LoroResult<()> {
305 deleted_indices.sort_by_key(|&x| std::cmp::Reverse(x));
307
308 let mut index: usize = 0;
309 for delta_item in delta.iter() {
310 match *delta_item {
311 x if x > 0 => {
312 index += x as usize;
313 }
314 neg_delete => {
315 let delete = neg_delete.unsigned_abs();
316 let mut remaining_deletes = delete;
317 while let Some(&last) = deleted_indices.last() {
318 if last < index {
319 deleted_indices.pop();
320 continue;
321 }
322
323 if last < index + remaining_deletes {
324 deleted_indices.pop();
325 remaining_deletes -= 1;
326 } else {
327 break;
328 }
329 }
330
331 self.delete(index, remaining_deletes)?;
332 }
333 }
334 }
335
336 Ok(())
337 }
338
339 fn update_positions_on_insert(
349 positions: &mut FxHashMap<ContainerID, usize>,
350 index: usize,
351 len: usize,
352 ) {
353 for pos in positions.values_mut() {
354 if *pos >= index {
355 *pos += len;
356 }
357 }
358 }
359
360 fn update_positions_on_delete(positions: &mut FxHashMap<ContainerID, usize>, index: usize) {
369 for pos in positions.values_mut() {
370 if *pos >= index {
371 *pos -= 1;
372 }
373 }
374 }
375}