Skip to main content

matrix_sdk_common/linked_chunk/
mod.rs

1// Copyright 2024 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![allow(rustdoc::private_intra_doc_links)]
16
17//! A linked chunk is the underlying data structure that holds all events.
18
19/// A macro to test the items and the gap of a `LinkedChunk`. A chunk is
20/// delimited by `[` and `]`. An item chunk has the form `[a, b, c]` where `a`,
21/// `b` and `c` are items. A gap chunk has the form `[-]`.
22///
23/// For example, here is an assertion of 7 chunks: 1 items chunk, 1 gap chunk, 2
24/// items chunks, 1 gap chunk, 2 items chunk. `a` is the oldest item of the
25/// oldest chunk (the first chunk), and `i` is the oldest (and newest) item of
26/// the newest chunk (the last chunk).
27///
28/// ```rust,no_run
29/// assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] ['f', 'g', 'h'] ['i']);
30/// ```
31#[cfg(test)]
32macro_rules! assert_items_eq {
33    ( @_ [ $iterator:ident ] { [-] $( $rest:tt )* } { $( $accumulator:tt )* } ) => {
34        assert_items_eq!(
35            @_
36            [ $iterator ]
37            { $( $rest )* }
38            {
39                $( $accumulator )*
40                {
41                    let chunk = $iterator .next().expect("next chunk (expect gap)");
42                    assert!(chunk.is_gap(), "chunk should be a gap");
43                }
44            }
45        )
46    };
47
48    ( @_ [ $iterator:ident ] { [ $( $item:expr ),* ] $( $rest:tt )* } { $( $accumulator:tt )* } ) => {
49        assert_items_eq!(
50            @_
51            [ $iterator ]
52            { $( $rest )* }
53            {
54                $( $accumulator )*
55                {
56                    let chunk = $iterator .next().expect("next chunk (expect items)");
57                    assert!(chunk.is_items(), "chunk should contain items");
58
59                    let $crate::linked_chunk::ChunkContent::Items(items) = chunk.content() else {
60                        unreachable!()
61                    };
62
63                    let mut items_iterator = items.iter();
64
65                    $(
66                        assert_eq!(items_iterator.next(), Some(& $item ));
67                    )*
68
69                    assert!(items_iterator.next().is_none(), "no more items");
70                }
71            }
72        )
73    };
74
75    ( @_ [ $iterator:ident ] {} { $( $accumulator:tt )* } ) => {
76        {
77            $( $accumulator )*
78            assert!( $iterator .next().is_none(), "no more chunks");
79        }
80    };
81
82    ( $linked_chunk:expr, $( $all:tt )* ) => {
83        assert_items_eq!(
84            @_
85            [ iterator ]
86            { $( $all )* }
87            {
88                let mut iterator = $linked_chunk.chunks();
89            }
90        )
91    }
92}
93
94mod as_vector;
95mod identifiers;
96pub mod lazy_loader;
97mod order_tracker;
98pub mod relational;
99mod updates;
100
101use std::{
102    fmt::{self},
103    marker::PhantomData,
104    ptr::NonNull,
105    sync::{
106        OnceLock,
107        atomic::{self, AtomicU64},
108    },
109};
110
111pub use self::{as_vector::*, identifiers::*, order_tracker::OrderTracker, updates::*};
112
113/// Errors of [`LinkedChunk`].
114#[derive(thiserror::Error, Debug)]
115pub enum Error {
116    /// A chunk identifier is invalid.
117    #[error("The chunk identifier is invalid: `{identifier:?}`")]
118    InvalidChunkIdentifier {
119        /// The chunk identifier.
120        identifier: ChunkIdentifier,
121    },
122
123    /// A chunk is a gap chunk, and it was expected to be an items.
124    #[error("The chunk is a gap: `{identifier:?}`")]
125    ChunkIsAGap {
126        /// The chunk identifier.
127        identifier: ChunkIdentifier,
128    },
129
130    /// A chunk is an items chunk, and it was expected to be a gap.
131    #[error("The chunk is an item: `{identifier:?}`")]
132    ChunkIsItems {
133        /// The chunk identifier.
134        identifier: ChunkIdentifier,
135    },
136
137    /// A chunk is an items chunk, and it was expected to be empty.
138    #[error("The chunk is a non-empty item chunk: `{identifier:?}`")]
139    RemovingNonEmptyItemsChunk {
140        /// The chunk identifier.
141        identifier: ChunkIdentifier,
142    },
143
144    /// We're trying to remove the only chunk in the `LinkedChunk`, and it can't
145    /// be empty.
146    #[error("Trying to remove the only chunk, but a linked chunk can't be empty")]
147    RemovingLastChunk,
148
149    /// An item index is invalid.
150    #[error("The item index is invalid: `{index}`")]
151    InvalidItemIndex {
152        /// The index.
153        index: usize,
154    },
155}
156
157/// Links of a `LinkedChunk`, i.e. the first and last [`Chunk`].
158///
159/// This type was introduced to avoid borrow checking errors when mutably
160/// referencing a subset of fields of a `LinkedChunk`.
161struct Ends<const CHUNK_CAPACITY: usize, Item, Gap> {
162    /// The first chunk.
163    first: OnceLock<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
164
165    /// The last chunk.
166    last: Option<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
167
168    updates_pusher: Option<ObservableUpdatesPusher<Item, Gap>>,
169}
170
171impl<const CAP: usize, Item, Gap> Ends<CAP, Item, Gap> {
172    /// Create a new [`Ends`].
173    fn new(updates: &Option<ObservableUpdates<Item, Gap>>) -> Self {
174        Self {
175            first: OnceLock::new(),
176            last: None,
177            updates_pusher: updates.as_ref().map(ObservableUpdates::new_pusher),
178        }
179    }
180
181    /// Create a new [`Ends`] with a specific first chunk!
182    fn new_with_first_chunk(
183        first_chunk: NonNull<Chunk<CAP, Item, Gap>>,
184        updates: &Option<ObservableUpdates<Item, Gap>>,
185    ) -> Self {
186        Self {
187            first: {
188                let first = OnceLock::new();
189
190                // Initialise with `first_chunk`.
191                first.get_or_init(|| first_chunk);
192
193                first
194            },
195            last: None,
196            updates_pusher: updates.as_ref().map(ObservableUpdates::new_pusher),
197        }
198    }
199
200    /// Lazily get an immutable pointer to the first chunk.
201    fn first_chunk_ptr(&self) -> &NonNull<Chunk<CAP, Item, Gap>> {
202        self.first
203            // Lazily initialise during first access.
204            .get_or_init(|| {
205                let identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
206
207                if let Some(updates) = self.updates_pusher.as_ref() {
208                    updates.push(Update::NewItemsChunk {
209                        previous: None,
210                        new: identifier,
211                        next: None,
212                    });
213                }
214
215                Chunk::new_items_leaked(identifier)
216            })
217    }
218
219    /// Lazily get an mutable pointer to the first chunk.
220    fn first_chunk_mut_ptr(&mut self) -> &mut NonNull<Chunk<CAP, Item, Gap>> {
221        // `OnceLock::get_or_init_mut` is unstable. We can fake it by using a
222        // combo of `get_or_init` + `get_mut`.
223        let _ = self.first_chunk_ptr();
224
225        self.first
226            .get_mut()
227            // SAFETY: `self.first` has been initialised by the call to
228            // `Self::first_chunk_ptr` above. The fact this method takes a
229            // `&mut self` also ensures an exclusive access to the `OnceLock`,
230            // providing the guarantee there is no other reader or writer to it,
231            // which makes it thread-safe.
232            .expect("`first` must have been initialised")
233    }
234
235    /// Get the first chunk, as an immutable reference.
236    fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
237        // SAFETY: The pointer to the first chunk has been correctly initialised
238        // and is convertible to a reference.
239        unsafe { self.first_chunk_ptr().as_ref() }
240    }
241
242    /// Get the first chunk, as a mutable reference.
243    fn first_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
244        // SAFETY: The pointer to the first chunk has been correctly initialised
245        // and is convertible to a mutable reference.
246        unsafe { self.first_chunk_mut_ptr().as_mut() }
247    }
248
249    /// Get the latest chunk, as an immutable reference.
250    fn latest_chunk(&self) -> &Chunk<CAP, Item, Gap> {
251        if let Some(last) = &self.last {
252            // SAFETY: The pointer to the last chunk has been correctly
253            // initialised and is convertible to a reference.
254            unsafe { last.as_ref() }
255        } else {
256            self.first_chunk()
257        }
258    }
259
260    /// Get the latest chunk, as a mutable reference.
261    fn latest_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
262        if let Some(last) = &mut self.last {
263            // SAFETY: The pointer to the last chunk has been correctly
264            // initialised and is convertible to a mutable reference.
265            unsafe { last.as_mut() }
266        } else {
267            self.first_chunk_mut()
268        }
269    }
270
271    /// Get the chunk as a reference, from its identifier, if it exists.
272    fn chunk(&self, identifier: ChunkIdentifier) -> Option<&Chunk<CAP, Item, Gap>> {
273        let mut chunk = self.latest_chunk();
274
275        loop {
276            if chunk.identifier() == identifier {
277                return Some(chunk);
278            }
279
280            chunk = chunk.previous()?;
281        }
282    }
283
284    /// Get the chunk as a mutable reference, from its identifier, if it exists.
285    fn chunk_mut(&mut self, identifier: ChunkIdentifier) -> Option<&mut Chunk<CAP, Item, Gap>> {
286        let mut chunk = self.latest_chunk_mut();
287
288        loop {
289            if chunk.identifier() == identifier {
290                return Some(chunk);
291            }
292
293            chunk = chunk.previous_mut()?;
294        }
295    }
296
297    /// Drop all the chunks, the first chunk will be created lazily with the
298    /// identifier [`ChunkIdentifierGenerator::FIRST_IDENTIFIER`].
299    fn clear(&mut self) {
300        // Loop over all chunks, from the last to the first chunk, and drop
301        // them. Take the latest chunk.
302        let mut current_chunk_ptr = self.last.or_else(|| self.first.get().copied());
303
304        // As long as we have another chunk…
305        while let Some(chunk_ptr) = current_chunk_ptr {
306            // Fetch the previous chunk pointer.
307            let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
308
309            // Re-box the chunk, and let Rust do its job.
310            let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
311
312            // Update the `current_chunk_ptr`.
313            current_chunk_ptr = previous_ptr;
314        }
315
316        // At this step, all chunks have been dropped, including `self.first`.
317        self.first.take();
318        self.last = None;
319    }
320
321    /// Drop all chunks, and replace the first one with the one provided as an
322    /// argument.
323    ///
324    /// # Safety
325    ///
326    /// Be aware to not forget to update
327    /// [`LinkedChunk::chunk_identifier_generator`] because the first chunk has
328    /// the identifier [`ChunkIdentifierGenerator::FIRST_IDENTIFIER`]!
329    unsafe fn replace_with(&mut self, first_chunk: NonNull<Chunk<CAP, Item, Gap>>) {
330        self.clear();
331
332        // At this step, all chunks have been dropped `self.first` is supposed
333        // to be uninitialised. Let's be sure.
334        let mut first_chunk = Some(first_chunk);
335        self.first.get_or_init(|| first_chunk.take().unwrap());
336
337        if first_chunk.is_some() {
338            unreachable!(
339                "`first` must be initialised to `first_chunk` because `clear` has been called"
340            );
341        }
342    }
343}
344
345/// The [`LinkedChunk`] structure.
346///
347/// It is similar to a linked list, except that it contains many items `Item`
348/// instead of a single one. A chunk has a maximum capacity of `CHUNK_CAPACITY`.
349/// Once a chunk is full, a new chunk is created. Not all chunks are necessarily
350/// entirely full. A chunk can represents a `Gap` between other chunks.
351pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
352    /// The links to the chunks, i.e. the first and the last chunk.
353    links: Ends<CHUNK_CAPACITY, Item, Gap>,
354
355    /// The generator of chunk identifiers.
356    chunk_identifier_generator: ChunkIdentifierGenerator,
357
358    /// All updates that have been made on this `LinkedChunk`. If this field is
359    /// `Some(…)`, update history is enabled, otherwise, if it's `None`, update
360    /// history is disabled.
361    updates: Option<ObservableUpdates<Item, Gap>>,
362
363    /// Marker.
364    marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
365}
366
367impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
368    fn default() -> Self {
369        Self::new()
370    }
371}
372
373impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
374    /// Create a new [`Self`].
375    pub fn new() -> Self {
376        let updates = None;
377
378        Self {
379            links: Ends::new(&updates),
380            chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
381            updates,
382            marker: PhantomData,
383        }
384    }
385
386    /// Create a new [`Self`] with a history of updates.
387    ///
388    /// When [`Self`] is built with update history, the
389    /// [`ObservableUpdates::take`] method must be called to consume and clean
390    /// the updates. See [`Self::updates`].
391    pub fn new_with_update_history() -> Self {
392        let updates = Some(ObservableUpdates::new());
393
394        Self {
395            links: Ends::new(&updates),
396            chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
397            updates,
398            marker: PhantomData,
399        }
400    }
401
402    /// Clear all the chunks.
403    pub fn clear(&mut self) {
404        // Clear `self.links`.
405        self.links.clear();
406
407        // Clear `self.chunk_identifier_generator`.
408        self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
409
410        // “Clear” `self.updates`.
411        if let Some(updates) = self.updates.as_mut() {
412            // Clear the previous updates, as we're about to insert a clear they
413            // would be useless.
414            updates.clear_pending();
415            updates.push(Update::Clear);
416        }
417    }
418
419    /// Push items at the end of the [`LinkedChunk`], i.e. on the last chunk.
420    ///
421    /// If the last chunk doesn't have enough space to welcome all `items`, then
422    /// new chunks can be created (and linked appropriately).
423    pub fn push_items_back<I>(&mut self, items: I)
424    where
425        Item: Clone,
426        Gap: Clone,
427        I: IntoIterator<Item = Item>,
428        I::IntoIter: ExactSizeIterator,
429    {
430        let items = items.into_iter();
431
432        let last_chunk = self.links.latest_chunk_mut();
433
434        // Push the items.
435        let last_chunk =
436            last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
437
438        debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
439
440        // We need to update `self.links.last` if and only if `last_chunk`
441        // _is not_ the first chunk, and _is_ the last chunk (ensured by the
442        // `debug_assert!` above).
443        if !last_chunk.is_first_chunk() {
444            // Maybe `last_chunk` is the same as the previous `self.links.last`
445            // chunk, but it's OK.
446            self.links.last = Some(last_chunk.as_ptr());
447        }
448    }
449
450    /// Push a gap at the end of the [`LinkedChunk`], i.e. after the last chunk.
451    pub fn push_gap_back(&mut self, content: Gap)
452    where
453        Item: Clone,
454        Gap: Clone,
455    {
456        let last_chunk = self.links.latest_chunk_mut();
457        last_chunk.insert_next(
458            Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
459            &mut self.updates,
460        );
461
462        self.links.last = last_chunk.next;
463    }
464
465    /// Insert items at a specified position in the [`LinkedChunk`].
466    ///
467    /// Because the `position` can be invalid, this method returns a `Result`.
468    pub fn insert_items_at<I>(&mut self, position: Position, items: I) -> Result<(), Error>
469    where
470        Item: Clone,
471        Gap: Clone,
472        I: IntoIterator<Item = Item>,
473        I::IntoIter: ExactSizeIterator,
474    {
475        let chunk_identifier = position.chunk_identifier();
476        let item_index = position.index();
477
478        let chunk = self
479            .links
480            .chunk_mut(chunk_identifier)
481            .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
482
483        let chunk = match &mut chunk.content {
484            ChunkContent::Gap(..) => {
485                return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
486            }
487
488            ChunkContent::Items(current_items) => {
489                let current_items_length = current_items.len();
490
491                if item_index > current_items_length {
492                    return Err(Error::InvalidItemIndex { index: item_index });
493                }
494
495                // Prepare the items to be pushed.
496                let items = items.into_iter();
497
498                // Push at the end of the current items.
499                if item_index == current_items_length {
500                    chunk
501                        // Push the new items.
502                        .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
503                }
504                // Insert inside the current items.
505                else {
506                    if let Some(updates) = self.updates.as_mut() {
507                        updates.push(Update::DetachLastItems {
508                            at: Position(chunk_identifier, item_index),
509                        });
510                    }
511
512                    // Split the items.
513                    let detached_items = current_items.split_off(item_index);
514
515                    let chunk = chunk
516                        // Push the new items.
517                        .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
518
519                    if let Some(updates) = self.updates.as_mut() {
520                        updates.push(Update::StartReattachItems);
521                    }
522
523                    let chunk = chunk
524                        // Finally, push the items that have been detached.
525                        .push_items(
526                            detached_items.into_iter(),
527                            &self.chunk_identifier_generator,
528                            &mut self.updates,
529                        );
530
531                    if let Some(updates) = self.updates.as_mut() {
532                        updates.push(Update::EndReattachItems);
533                    }
534
535                    chunk
536                }
537            }
538        };
539
540        // We need to update `self.links.last` if and only if `chunk` _is not_
541        // the first chunk, and _is_ the last chunk.
542        if !chunk.is_first_chunk() && chunk.is_last_chunk() {
543            // Maybe `chunk` is the same as the previous `self.links.last`
544            // chunk, but it's OK.
545            self.links.last = Some(chunk.as_ptr());
546        }
547
548        Ok(())
549    }
550
551    /// Remove item at a specified position in the [`LinkedChunk`].
552    ///
553    /// `position` must point to a valid item, otherwise the method returns
554    /// `Err`.
555    ///
556    /// The chunk containing the item represented by `position` may be empty
557    /// once the item has been removed. In this case, the chunk will be removed.
558    pub fn remove_item_at(&mut self, position: Position) -> Result<Item, Error> {
559        let chunk_identifier = position.chunk_identifier();
560        let item_index = position.index();
561
562        let mut chunk_ptr = None;
563        let removed_item;
564
565        {
566            let chunk = self
567                .links
568                .chunk_mut(chunk_identifier)
569                .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
570
571            let current_items = match &mut chunk.content {
572                ChunkContent::Gap(..) => {
573                    return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
574                }
575                ChunkContent::Items(current_items) => current_items,
576            };
577
578            if item_index >= current_items.len() {
579                return Err(Error::InvalidItemIndex { index: item_index });
580            }
581
582            removed_item = current_items.remove(item_index);
583
584            if let Some(updates) = self.updates.as_mut() {
585                updates.push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
586            }
587
588            // If the chunk is empty and not the first one, we can remove it.
589            if current_items.is_empty() && !chunk.is_first_chunk() {
590                // Unlink `chunk`.
591                chunk.unlink(self.updates.as_mut());
592
593                chunk_ptr = Some(chunk.as_ptr());
594
595                // We need to update `self.links.last` if and only if `chunk`
596                // _is_ the last chunk. The new last chunk is the chunk before
597                // `chunk`.
598                if chunk.is_last_chunk() {
599                    self.links.last = chunk.previous;
600                }
601            }
602
603            // Stop borrowing `chunk`.
604        }
605
606        if let Some(chunk_ptr) = chunk_ptr {
607            // `chunk` has been unlinked.
608
609            // Re-box the chunk, and let Rust do its job.
610            //
611            // SAFETY: `chunk` is unlinked and not borrowed anymore.
612            // `LinkedChunk` doesn't use it anymore, it's a leak. It is time to
613            // re-`Box` it and drop it.
614            let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
615        }
616
617        Ok(removed_item)
618    }
619
620    /// Replace item at a specified position in the [`LinkedChunk`].
621    ///
622    /// `position` must point to a valid item, otherwise the method returns
623    /// `Err`.
624    pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
625    where
626        Item: Clone,
627    {
628        let chunk_identifier = position.chunk_identifier();
629        let item_index = position.index();
630
631        let chunk = self
632            .links
633            .chunk_mut(chunk_identifier)
634            .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
635
636        match &mut chunk.content {
637            ChunkContent::Gap(..) => {
638                return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
639            }
640
641            ChunkContent::Items(current_items) => {
642                if item_index >= current_items.len() {
643                    return Err(Error::InvalidItemIndex { index: item_index });
644                }
645
646                // Avoid one spurious clone by notifying about the update
647                // *before* applying it.
648                if let Some(updates) = self.updates.as_mut() {
649                    updates.push(Update::ReplaceItem {
650                        at: Position(chunk_identifier, item_index),
651                        item: item.clone(),
652                    });
653                }
654
655                current_items[item_index] = item;
656            }
657        }
658
659        Ok(())
660    }
661
662    /// Insert a gap at a specified position in the [`LinkedChunk`].
663    ///
664    /// Because the `position` can be invalid, this method returns a `Result`.
665    pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
666    where
667        Item: Clone,
668        Gap: Clone,
669    {
670        let chunk_identifier = position.chunk_identifier();
671        let item_index = position.index();
672
673        let chunk = self
674            .links
675            .chunk_mut(chunk_identifier)
676            .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
677
678        let chunk = match &mut chunk.content {
679            ChunkContent::Gap(..) => {
680                return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
681            }
682
683            ChunkContent::Items(current_items) => {
684                // If `item_index` is 0, we don't want to split the current
685                // items chunk to insert a new gap chunk, otherwise it would
686                // create an empty current items chunk. Let's handle this case
687                // in particular.
688                if item_index == 0 {
689                    let chunk_was_first = chunk.is_first_chunk();
690                    let chunk_was_last = chunk.is_last_chunk();
691
692                    let new_chunk = chunk.insert_before(
693                        Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
694                        self.updates.as_mut(),
695                    );
696
697                    let new_chunk_ptr = new_chunk.as_ptr();
698                    let chunk_ptr = chunk.as_ptr();
699
700                    // `chunk` was the first: let's update `self.links.first`.
701                    //
702                    // If `chunk` was not the first but was the last, there is
703                    // nothing to do, `self.links.last` is already up-to-date.
704                    if chunk_was_first {
705                        *self.links.first_chunk_mut_ptr() = new_chunk_ptr;
706
707                        // `chunk` was the first __and__ the last: let's set
708                        // `self.links.last`.
709                        if chunk_was_last {
710                            self.links.last = Some(chunk_ptr);
711                        }
712                    }
713
714                    return Ok(());
715                }
716
717                let current_items_length = current_items.len();
718
719                if item_index >= current_items_length {
720                    return Err(Error::InvalidItemIndex { index: item_index });
721                }
722
723                if let Some(updates) = self.updates.as_mut() {
724                    updates.push(Update::DetachLastItems {
725                        at: Position(chunk_identifier, item_index),
726                    });
727                }
728
729                // Split the items.
730                let detached_items = current_items.split_off(item_index);
731
732                let chunk = chunk
733                    // Insert a new gap chunk.
734                    .insert_next(
735                        Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
736                        &mut self.updates,
737                    );
738
739                if let Some(updates) = self.updates.as_mut() {
740                    updates.push(Update::StartReattachItems);
741                }
742
743                let chunk = chunk
744                    // Insert a new items chunk.
745                    .insert_next(
746                        Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
747                        &mut self.updates,
748                    )
749                    // Finally, push the items that have been detached.
750                    .push_items(
751                        detached_items.into_iter(),
752                        &self.chunk_identifier_generator,
753                        &mut self.updates,
754                    );
755
756                if let Some(updates) = self.updates.as_mut() {
757                    updates.push(Update::EndReattachItems);
758                }
759
760                chunk
761            }
762        };
763
764        // We need to update `self.links.last` if and only if `chunk` _is not_
765        // the first chunk, and _is_ the last chunk.
766        if !chunk.is_first_chunk() && chunk.is_last_chunk() {
767            // Maybe `chunk` is the same as the previous `self.links.last`
768            // chunk, but it's OK.
769            self.links.last = Some(chunk.as_ptr());
770        }
771
772        Ok(())
773    }
774
775    /// Remove a chunk with the given identifier iff it's empty.
776    ///
777    /// A chunk is considered empty if:
778    ///
779    /// - it's a gap chunk, or
780    /// - it's an items chunk with no items.
781    ///
782    /// This returns the next insert position, viz. the start of the next chunk,
783    /// if any, or none if there was no next chunk.
784    pub fn remove_empty_chunk_at(
785        &mut self,
786        chunk_identifier: ChunkIdentifier,
787    ) -> Result<Option<Position>, Error> {
788        // Check that we're not removing the last chunk.
789        if self.links.first_chunk().is_last_chunk() {
790            return Err(Error::RemovingLastChunk);
791        }
792
793        let chunk = self
794            .links
795            .chunk_mut(chunk_identifier)
796            .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
797
798        if chunk.num_items() > 0 {
799            return Err(Error::RemovingNonEmptyItemsChunk { identifier: chunk_identifier });
800        }
801
802        let chunk_was_first = chunk.is_first_chunk();
803        let chunk_was_last = chunk.is_last_chunk();
804        let next_ptr = chunk.next;
805        let previous_ptr = chunk.previous;
806        let position_of_next = chunk.next().map(|next| next.first_position());
807
808        chunk.unlink(self.updates.as_mut());
809
810        let chunk_ptr = chunk.as_ptr();
811
812        // If the chunk is the first one, we need to update `self.links.first`…
813        if chunk_was_first {
814            // … if and only if there is a next chunk.
815            if let Some(next_ptr) = next_ptr {
816                *self.links.first_chunk_mut_ptr() = next_ptr;
817            }
818        }
819
820        if chunk_was_last {
821            self.links.last = previous_ptr;
822        }
823
824        // SAFETY: `chunk` is unlinked and not borrowed anymore. `LinkedChunk`
825        // doesn't use it anymore, it's a leak. It is time to re-`Box` it and
826        // drop it.
827        let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
828
829        // Return the first position of the next chunk, if any.
830        Ok(position_of_next)
831    }
832
833    /// Replace the gap identified by `chunk_identifier`, by items.
834    ///
835    /// Because the `chunk_identifier` can represent non-gap chunk, this method
836    /// returns a `Result`.
837    ///
838    /// This method returns a reference to the (first if many) newly created
839    /// `Chunk` that contains the `items`.
840    pub fn replace_gap_at<I>(
841        &mut self,
842        items: I,
843        chunk_identifier: ChunkIdentifier,
844    ) -> Result<&Chunk<CAP, Item, Gap>, Error>
845    where
846        Item: Clone,
847        Gap: Clone,
848        I: IntoIterator<Item = Item>,
849        I::IntoIter: ExactSizeIterator,
850    {
851        let chunk_ptr;
852        let new_chunk_ptr;
853
854        {
855            let chunk = self
856                .links
857                .chunk_mut(chunk_identifier)
858                .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
859
860            if chunk.is_items() {
861                return Err(Error::ChunkIsItems { identifier: chunk_identifier });
862            }
863
864            let chunk_was_first = chunk.is_first_chunk();
865
866            let maybe_last_chunk_ptr = {
867                let items = items.into_iter();
868
869                let last_inserted_chunk = chunk
870                    // Insert a new items chunk…
871                    .insert_next(
872                        Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
873                        &mut self.updates,
874                    )
875                    // … and insert the items.
876                    .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
877
878                last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
879            };
880
881            new_chunk_ptr = chunk
882                .next
883                // SAFETY: A new `Chunk` has just been inserted, so it exists.
884                .unwrap();
885
886            // Now that new items have been pushed, we can unlink the gap chunk.
887            chunk.unlink(self.updates.as_mut());
888
889            // Get the pointer to `chunk`.
890            chunk_ptr = chunk.as_ptr();
891
892            // Update `self.links.first` if the gap chunk was the first chunk.
893            if chunk_was_first {
894                *self.links.first_chunk_mut_ptr() = new_chunk_ptr;
895            }
896
897            // Update `self.links.last` if the gap (so the new) chunk was (is)
898            // the last chunk.
899            if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
900                self.links.last = Some(last_chunk_ptr);
901            }
902
903            // Stop borrowing `chunk`.
904        }
905
906        // Re-box the chunk, and let Rust do its job.
907        //
908        // SAFETY: `chunk` is unlinked and not borrowed anymore. `LinkedChunk`
909        // doesn't use it anymore, it's a leak. It is time to re-`Box` it and
910        // drop it.
911        let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
912
913        Ok(
914            // SAFETY: `new_chunk_ptr` is valid, non-null and well-aligned. It's
915            // taken from `chunk`, and that's how the entire `LinkedChunk` type
916            // works. Pointer construction safety is guaranteed by
917            // `Chunk::new_items_leaked` and `Chunk::new_gap_leaked`.
918            unsafe { new_chunk_ptr.as_ref() },
919        )
920    }
921
922    /// Search backwards for a chunk, and return its identifier.
923    pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
924    where
925        P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
926    {
927        self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
928    }
929
930    /// Search backwards for an item, and return its position.
931    pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
932    where
933        P: FnMut(&'a Item) -> bool,
934    {
935        self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
936    }
937
938    /// Iterate over the chunks, backwards.
939    ///
940    /// It iterates from the last to the first chunk.
941    pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
942        IterBackward::new(self.links.latest_chunk())
943    }
944
945    /// Iterate over the chunks, forward.
946    ///
947    /// It iterates from the first to the last chunk.
948    pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
949        Iter::new(self.links.first_chunk())
950    }
951
952    /// Iterate over the chunks, starting from `identifier`, backward.
953    ///
954    /// It iterates from the chunk with the identifier `identifier` to the first
955    /// chunk.
956    pub fn rchunks_from(
957        &self,
958        identifier: ChunkIdentifier,
959    ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
960        Ok(IterBackward::new(
961            self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
962        ))
963    }
964
965    /// Iterate over the chunks, starting from `position`, forward.
966    ///
967    /// It iterates from the chunk with the identifier `identifier` to the last
968    /// chunk.
969    pub fn chunks_from(
970        &self,
971        identifier: ChunkIdentifier,
972    ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
973        Ok(Iter::new(
974            self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
975        ))
976    }
977
978    /// Iterate over the items, backward.
979    ///
980    /// It iterates from the last to the first item.
981    pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
982        self.ritems_from(self.links.latest_chunk().last_position())
983            .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
984    }
985
986    /// Iterate over the items, forward.
987    ///
988    /// It iterates from the first to the last item.
989    pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
990        let first_chunk = self.links.first_chunk();
991
992        self.items_from(first_chunk.first_position())
993            .expect("`items` cannot fail because at least one empty chunk must exist")
994    }
995
996    /// Iterate over the items, starting from `position`, backward.
997    ///
998    /// It iterates from the item at `position` to the first item.
999    pub fn ritems_from(
1000        &self,
1001        position: Position,
1002    ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1003        Ok(self
1004            .rchunks_from(position.chunk_identifier())?
1005            .filter_map(|chunk| match &chunk.content {
1006                ChunkContent::Gap(..) => None,
1007                ChunkContent::Items(items) => {
1008                    let identifier = chunk.identifier();
1009
1010                    Some(
1011                        items.iter().enumerate().rev().map(move |(item_index, item)| {
1012                            (Position(identifier, item_index), item)
1013                        }),
1014                    )
1015                }
1016            })
1017            .flatten()
1018            .skip_while({
1019                let expected_index = position.index();
1020
1021                move |(Position(chunk_identifier, item_index), _item)| {
1022                    *chunk_identifier == position.chunk_identifier()
1023                        && *item_index != expected_index
1024                }
1025            }))
1026    }
1027
1028    /// Iterate over the items, starting from `position`, forward.
1029    ///
1030    /// It iterates from the item at `position` to the last item.
1031    pub fn items_from(
1032        &self,
1033        position: Position,
1034    ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1035        Ok(self
1036            .chunks_from(position.chunk_identifier())?
1037            .filter_map(|chunk| match &chunk.content {
1038                ChunkContent::Gap(..) => None,
1039                ChunkContent::Items(items) => {
1040                    let identifier = chunk.identifier();
1041
1042                    Some(
1043                        items.iter().enumerate().map(move |(item_index, item)| {
1044                            (Position(identifier, item_index), item)
1045                        }),
1046                    )
1047                }
1048            })
1049            .flatten()
1050            .skip(position.index()))
1051    }
1052
1053    /// Return the first chunk.
1054    pub fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
1055        self.links.first_chunk()
1056    }
1057
1058    /// Get a mutable reference to the `LinkedChunk` updates, aka
1059    /// [`ObservableUpdates`].
1060    ///
1061    /// If the `Option` becomes `None`, it will disable update history. Thus, be
1062    /// careful when you want to empty the update history: do not use
1063    /// `Option::take()` directly but rather [`ObservableUpdates::take`] for
1064    /// example.
1065    ///
1066    /// It returns `None` if updates are disabled, i.e. if this linked chunk has
1067    /// been constructed with [`Self::new`], otherwise, if it's been constructed
1068    /// with [`Self::new_with_update_history`], it returns `Some(…)`.
1069    #[must_use]
1070    pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
1071        self.updates.as_mut()
1072    }
1073
1074    /// Get updates as [`eyeball_im::VectorDiff`], see [`AsVector`] to learn
1075    /// more.
1076    ///
1077    /// It returns `None` if updates are disabled, i.e. if this linked chunk has
1078    /// been constructed with [`Self::new`], otherwise, if it's been constructed
1079    /// with [`Self::new_with_update_history`], it returns `Some(…)`.
1080    pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
1081        let (updates, token) = self
1082            .updates
1083            .as_mut()
1084            .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1085        let chunk_iterator = self.chunks();
1086
1087        Some(AsVector::new(updates, token, chunk_iterator))
1088    }
1089
1090    /// Get an [`OrderTracker`] for the linked chunk, which can be used to
1091    /// compare the relative position of two events in this linked chunk.
1092    ///
1093    /// A pre-requisite is that the linked chunk has been constructed with
1094    /// [`Self::new_with_update_history`], and that if the linked chunk is
1095    /// lazily-loaded, an iterator over the fully-loaded linked chunk is passed
1096    /// at construction time here.
1097    pub fn order_tracker(
1098        &mut self,
1099        all_chunks: Option<Vec<ChunkMetadata>>,
1100    ) -> Option<OrderTracker<Item, Gap>>
1101    where
1102        Item: Clone,
1103    {
1104        let (updates, token) = self
1105            .updates
1106            .as_mut()
1107            .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1108
1109        Some(OrderTracker::new(
1110            updates,
1111            token,
1112            all_chunks.unwrap_or_else(|| {
1113                // Consider the linked chunk as fully loaded.
1114                self.chunks()
1115                    .map(|chunk| ChunkMetadata {
1116                        identifier: chunk.identifier(),
1117                        num_items: chunk.num_items(),
1118                        previous: chunk.previous().map(|prev| prev.identifier()),
1119                        next: chunk.next().map(|next| next.identifier()),
1120                    })
1121                    .collect()
1122            }),
1123        ))
1124    }
1125
1126    /// Returns the number of items of the linked chunk.
1127    pub fn num_items(&self) -> usize {
1128        self.items().count()
1129    }
1130}
1131
1132impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
1133    fn drop(&mut self) {
1134        // Clear the links, which will drop all the chunks.
1135        //
1136        // Calling `Self::clear` would be an error as we don't want to emit an
1137        // `Update::Clear` when `self` is dropped. Instead, we only care about
1138        // freeing memory correctly. Rust can take care of everything except the
1139        // pointers in `self.links`, hence the specific call to
1140        // `self.links.clear()`.
1141        self.links.clear();
1142    }
1143}
1144
1145/// A [`LinkedChunk`] can be safely sent over thread boundaries if `Item: Send`
1146/// and `Gap: Send`. The only unsafe part is around the `NonNull`, but the API
1147/// and the lifetimes to deref them are designed safely.
1148unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1149
1150/// A [`LinkedChunk`] can be safely share between threads if `Item: Sync` and
1151/// `Gap: Sync`. The only unsafe part is around the `NonNull`, but the API and
1152/// the lifetimes to deref them are designed safely.
1153unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1154
1155/// Generator for [`Chunk`]'s identifier.
1156///
1157/// Each [`Chunk`] has a unique identifier. This generator generates the unique
1158/// identifiers.
1159///
1160/// In order to keep good performance, a unique identifier is simply a `u64`
1161/// (see [`ChunkIdentifier`]). Generating a new unique identifier boils down to
1162/// incrementing by one the previous identifier. Note that this is not an index:
1163/// it _is_ an identifier.
1164#[derive(Debug)]
1165pub struct ChunkIdentifierGenerator {
1166    next: AtomicU64,
1167}
1168
1169impl ChunkIdentifierGenerator {
1170    /// The first identifier.
1171    const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1172
1173    /// Create the generator assuming the current [`LinkedChunk`] it belongs to
1174    /// is empty.
1175    pub fn new_from_scratch() -> Self {
1176        Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1177    }
1178
1179    /// Create the generator assuming the current [`LinkedChunk`] it belongs to
1180    /// is not empty, i.e. it already has some [`Chunk`] in it.
1181    pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1182        Self { next: AtomicU64::new(last_chunk_identifier.0) }
1183    }
1184
1185    /// Generate the next unique identifier.
1186    ///
1187    /// Note that it can fail if there is no more unique identifier available.
1188    /// In this case, this method will panic.
1189    fn next(&self) -> ChunkIdentifier {
1190        let previous = self.next.fetch_add(1, atomic::Ordering::Relaxed);
1191
1192        // Check for overflows. unlikely — TODO: call
1193        // `std::intrinsics::unlikely` once it's stable.
1194        if previous == u64::MAX {
1195            panic!(
1196                "No more chunk identifiers available. Congrats, you did it. \
1197                 2^64 identifiers have been consumed."
1198            )
1199        }
1200
1201        ChunkIdentifier(previous + 1)
1202    }
1203
1204    /// Get the current chunk identifier.
1205    //
1206    // This is hidden because it's used only in the tests.
1207    #[doc(hidden)]
1208    pub fn current(&self) -> ChunkIdentifier {
1209        ChunkIdentifier(self.next.load(atomic::Ordering::Relaxed))
1210    }
1211}
1212
1213/// The unique identifier of a chunk in a [`LinkedChunk`].
1214///
1215/// It is not the position of the chunk, just its unique identifier.
1216///
1217/// Learn more with [`ChunkIdentifierGenerator`].
1218#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1219#[repr(transparent)]
1220pub struct ChunkIdentifier(u64);
1221
1222impl ChunkIdentifier {
1223    /// Create a new [`ChunkIdentifier`].
1224    pub fn new(identifier: u64) -> Self {
1225        Self(identifier)
1226    }
1227
1228    /// Get the underlying identifier.
1229    pub fn index(&self) -> u64 {
1230        self.0
1231    }
1232}
1233
1234impl PartialEq<u64> for ChunkIdentifier {
1235    fn eq(&self, other: &u64) -> bool {
1236        self.0 == *other
1237    }
1238}
1239
1240/// The position of something inside a [`Chunk`].
1241///
1242/// It's a pair of a chunk position and an item index.
1243#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1244pub struct Position(ChunkIdentifier, usize);
1245
1246impl Position {
1247    /// Create a new [`Position`].
1248    pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1249        Self(chunk_identifier, index)
1250    }
1251
1252    /// Get the chunk identifier of the item.
1253    pub fn chunk_identifier(&self) -> ChunkIdentifier {
1254        self.0
1255    }
1256
1257    /// Get the index inside the chunk.
1258    pub fn index(&self) -> usize {
1259        self.1
1260    }
1261
1262    /// Decrement the index part (see [`Self::index`]), i.e. subtract 1.
1263    ///
1264    /// # Panic
1265    ///
1266    /// This method will panic if it will underflow, i.e. if the index is 0.
1267    pub fn decrement_index(&mut self) {
1268        self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1269    }
1270
1271    /// Increment the index part (see [`Self::index`]), i.e. add 1.
1272    ///
1273    /// # Panic
1274    ///
1275    /// This method will panic if it will overflow, i.e. if the index is larger
1276    /// than `usize::MAX`.
1277    pub fn increment_index(&mut self) {
1278        self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1279    }
1280}
1281
1282/// An iterator over a [`LinkedChunk`] that traverses the chunk in backward
1283/// direction (i.e. it calls `previous` on each chunk to make progress).
1284#[derive(Debug)]
1285pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1286    chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1287}
1288
1289impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1290    /// Create a new [`LinkedChunkIter`] from a particular [`Chunk`].
1291    fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1292        Self { chunk: Some(from_chunk) }
1293    }
1294}
1295
1296impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1297    type Item = &'a Chunk<CAP, Item, Gap>;
1298
1299    fn next(&mut self) -> Option<Self::Item> {
1300        self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1301    }
1302}
1303
1304/// An iterator over a [`LinkedChunk`] that traverses the chunk in forward
1305/// direction (i.e. it calls `next` on each chunk to make progress).
1306#[derive(Debug)]
1307pub struct Iter<'a, const CAP: usize, Item, Gap> {
1308    chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1309}
1310
1311impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1312    /// Create a new [`LinkedChunkIter`] from a particular [`Chunk`].
1313    fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1314        Self { chunk: Some(from_chunk) }
1315    }
1316}
1317
1318impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1319    type Item = &'a Chunk<CAP, Item, Gap>;
1320
1321    fn next(&mut self) -> Option<Self::Item> {
1322        self.chunk.inspect(|chunk| self.chunk = chunk.next())
1323    }
1324}
1325
1326/// This enum represents the content of a [`Chunk`].
1327#[derive(Clone, Debug)]
1328pub enum ChunkContent<Item, Gap> {
1329    /// The chunk represents a gap in the linked chunk, i.e. a hole. It means
1330    /// that some items are missing in this location.
1331    Gap(Gap),
1332
1333    /// The chunk contains items.
1334    Items(Vec<Item>),
1335}
1336
1337/// A chunk is a node in the [`LinkedChunk`].
1338pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1339    /// The previous chunk.
1340    previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1341
1342    /// If this chunk is the first one, and if the `LinkedChunk` is loaded
1343    /// lazily, chunk-by-chunk, this is the identifier of the previous chunk.
1344    /// This previous chunk is not loaded yet, so it's impossible to get a
1345    /// pointer to it yet. However we know its identifier.
1346    lazy_previous: Option<ChunkIdentifier>,
1347
1348    /// The next chunk.
1349    next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1350
1351    /// Unique identifier.
1352    identifier: ChunkIdentifier,
1353
1354    /// The content of the chunk.
1355    content: ChunkContent<Item, Gap>,
1356}
1357
1358impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1359    /// Create a new gap chunk.
1360    fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1361        Self::new(identifier, ChunkContent::Gap(content))
1362    }
1363
1364    /// Create a new items chunk.
1365    fn new_items(identifier: ChunkIdentifier) -> Self {
1366        Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1367    }
1368
1369    fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1370        Self { previous: None, lazy_previous: None, next: None, identifier, content }
1371    }
1372
1373    /// Create a new chunk given some content, but box it and leak it.
1374    fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1375        let chunk = Self::new(identifier, content);
1376        let chunk_box = Box::new(chunk);
1377
1378        NonNull::from(Box::leak(chunk_box))
1379    }
1380
1381    /// Create a new gap chunk, but box it and leak it.
1382    fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1383        let chunk = Self::new_gap(identifier, content);
1384        let chunk_box = Box::new(chunk);
1385
1386        NonNull::from(Box::leak(chunk_box))
1387    }
1388
1389    /// Create a new items chunk, but box it and leak it.
1390    fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1391        let chunk = Self::new_items(identifier);
1392        let chunk_box = Box::new(chunk);
1393
1394        NonNull::from(Box::leak(chunk_box))
1395    }
1396
1397    /// Get the pointer to `Self`.
1398    pub fn as_ptr(&self) -> NonNull<Self> {
1399        NonNull::from(self)
1400    }
1401
1402    /// Check whether this current chunk is a gap chunk.
1403    pub fn is_gap(&self) -> bool {
1404        matches!(self.content, ChunkContent::Gap(..))
1405    }
1406
1407    /// Check whether this current chunk is an items  chunk.
1408    pub fn is_items(&self) -> bool {
1409        !self.is_gap()
1410    }
1411
1412    /// Is this the definitive first chunk, even in the presence of
1413    /// lazy-loading?
1414    pub fn is_definitive_head(&self) -> bool {
1415        self.previous.is_none() && self.lazy_previous.is_none()
1416    }
1417
1418    /// Check whether this current chunk is the first chunk.
1419    fn is_first_chunk(&self) -> bool {
1420        self.previous.is_none()
1421    }
1422
1423    /// Check whether this current chunk is the last chunk.
1424    fn is_last_chunk(&self) -> bool {
1425        self.next.is_none()
1426    }
1427
1428    /// Return the link to the previous chunk, if it was loaded lazily.
1429    ///
1430    /// Doc hidden because this is mostly for internal debugging purposes.
1431    #[doc(hidden)]
1432    pub fn lazy_previous(&self) -> Option<ChunkIdentifier> {
1433        self.lazy_previous
1434    }
1435
1436    /// Get the unique identifier of the chunk.
1437    pub fn identifier(&self) -> ChunkIdentifier {
1438        self.identifier
1439    }
1440
1441    /// Get the content of the chunk.
1442    pub fn content(&self) -> &ChunkContent<Item, Gap> {
1443        &self.content
1444    }
1445
1446    /// Get the [`Position`] of the first item if any.
1447    ///
1448    /// If the `Chunk` is a `Gap`, it returns `0` for the index.
1449    pub fn first_position(&self) -> Position {
1450        Position(self.identifier(), 0)
1451    }
1452
1453    /// Get the [`Position`] of the last item if any.
1454    ///
1455    /// If the `Chunk` is a `Gap`, it returns `0` for the index.
1456    pub fn last_position(&self) -> Position {
1457        let identifier = self.identifier();
1458
1459        match &self.content {
1460            ChunkContent::Gap(..) => Position(identifier, 0),
1461            ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1462        }
1463    }
1464
1465    /// The number of items in the linked chunk.
1466    ///
1467    /// It will always return 0 if it's a gap chunk.
1468    pub fn num_items(&self) -> usize {
1469        match &self.content {
1470            ChunkContent::Gap(..) => 0,
1471            ChunkContent::Items(items) => items.len(),
1472        }
1473    }
1474
1475    /// Push items on the current chunk.
1476    ///
1477    /// If the chunk doesn't have enough spaces to welcome `new_items`, new
1478    /// chunk will be inserted next, and correctly linked.
1479    ///
1480    /// This method returns the last inserted chunk if any, or the current
1481    /// chunk. Basically, it returns the chunk onto which new computations must
1482    /// happen.
1483    ///
1484    /// Pushing items will always create new chunks if necessary, but it will
1485    /// never merge them, so that we avoid updating too much chunks.
1486    fn push_items<I>(
1487        &mut self,
1488        mut new_items: I,
1489        chunk_identifier_generator: &ChunkIdentifierGenerator,
1490        updates: &mut Option<ObservableUpdates<Item, Gap>>,
1491    ) -> &mut Self
1492    where
1493        I: Iterator<Item = Item> + ExactSizeIterator,
1494        Item: Clone,
1495        Gap: Clone,
1496    {
1497        // A small optimisation. Skip early if there is no new items.
1498        if new_items.len() == 0 {
1499            return self;
1500        }
1501
1502        let identifier = self.identifier();
1503        let prev_num_items = self.num_items();
1504
1505        match &mut self.content {
1506            // Cannot push items on a `Gap`. Let's insert a new `Items` chunk to
1507            // push the items onto it.
1508            ChunkContent::Gap(..) => {
1509                self
1510                    // Insert a new items chunk.
1511                    .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1512                    // Now push the new items on the next chunk, and return the
1513                    // result of `push_items`.
1514                    .push_items(new_items, chunk_identifier_generator, updates)
1515            }
1516
1517            ChunkContent::Items(items) => {
1518                // Calculate the free space of the current chunk.
1519                let free_space = CAPACITY.saturating_sub(prev_num_items);
1520
1521                // There is enough space to push all the new items.
1522                if new_items.len() <= free_space {
1523                    let start = items.len();
1524                    items.extend(new_items);
1525
1526                    if let Some(updates) = updates.as_mut() {
1527                        updates.push(Update::PushItems {
1528                            at: Position(identifier, start),
1529                            items: items[start..].to_vec(),
1530                        });
1531                    }
1532
1533                    // Return the current chunk.
1534                    self
1535                } else {
1536                    if free_space > 0 {
1537                        // Take all possible items to fill the free space.
1538                        let start = items.len();
1539                        items.extend(new_items.by_ref().take(free_space));
1540
1541                        if let Some(updates) = updates.as_mut() {
1542                            updates.push(Update::PushItems {
1543                                at: Position(identifier, start),
1544                                items: items[start..].to_vec(),
1545                            });
1546                        }
1547                    }
1548
1549                    self
1550                        // Insert a new items chunk.
1551                        .insert_next(
1552                            Self::new_items_leaked(chunk_identifier_generator.next()),
1553                            updates,
1554                        )
1555                        // Now push the rest of the new items on the next chunk,
1556                        // and return the result of `push_items`.
1557                        .push_items(new_items, chunk_identifier_generator, updates)
1558                }
1559            }
1560        }
1561    }
1562
1563    /// Insert a new chunk after the current one.
1564    ///
1565    /// The respective [`Self::previous`] and [`Self::next`] of the current and
1566    /// new chunk will be updated accordingly.
1567    fn insert_next(
1568        &mut self,
1569        mut new_chunk_ptr: NonNull<Self>,
1570        updates: &mut Option<ObservableUpdates<Item, Gap>>,
1571    ) -> &mut Self
1572    where
1573        Gap: Clone,
1574    {
1575        let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1576
1577        // Update the next chunk if any.
1578        if let Some(next_chunk) = self.next_mut() {
1579            // Link back to the new chunk.
1580            next_chunk.previous = Some(new_chunk_ptr);
1581
1582            // Link the new chunk to the next chunk.
1583            new_chunk.next = self.next;
1584        }
1585
1586        // Link to the new chunk.
1587        self.next = Some(new_chunk_ptr);
1588        // Link the new chunk to this one.
1589        new_chunk.previous = Some(self.as_ptr());
1590
1591        if let Some(updates) = updates.as_mut() {
1592            let previous = new_chunk.previous().map(Chunk::identifier);
1593            let new = new_chunk.identifier();
1594            let next = new_chunk.next().map(Chunk::identifier);
1595
1596            match new_chunk.content() {
1597                ChunkContent::Gap(gap) => {
1598                    updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1599                }
1600
1601                ChunkContent::Items(..) => {
1602                    updates.push(Update::NewItemsChunk { previous, new, next })
1603                }
1604            }
1605        }
1606
1607        new_chunk
1608    }
1609
1610    /// Insert a new chunk before the current one.
1611    ///
1612    /// The respective [`Self::previous`] and [`Self::next`] of the current and
1613    /// new chunk will be updated accordingly.
1614    fn insert_before(
1615        &mut self,
1616        mut new_chunk_ptr: NonNull<Self>,
1617        updates: Option<&mut ObservableUpdates<Item, Gap>>,
1618    ) -> &mut Self
1619    where
1620        Gap: Clone,
1621    {
1622        let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1623
1624        // Update the previous chunk if any.
1625        if let Some(previous_chunk) = self.previous_mut() {
1626            // Link back to the new chunk.
1627            previous_chunk.next = Some(new_chunk_ptr);
1628
1629            // Link the new chunk to the next chunk.
1630            new_chunk.previous = self.previous;
1631        }
1632        // No previous: `self` is the first! We need to move the `lazy_previous`
1633        // from `self` to `new_chunk`.
1634        else {
1635            new_chunk.lazy_previous = self.lazy_previous.take();
1636        }
1637
1638        // Link to the new chunk.
1639        self.previous = Some(new_chunk_ptr);
1640        // Link the new chunk to this one.
1641        new_chunk.next = Some(self.as_ptr());
1642
1643        if let Some(updates) = updates {
1644            let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1645            let new = new_chunk.identifier();
1646            let next = new_chunk.next().map(Chunk::identifier);
1647
1648            match new_chunk.content() {
1649                ChunkContent::Gap(gap) => {
1650                    updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1651                }
1652
1653                ChunkContent::Items(..) => {
1654                    updates.push(Update::NewItemsChunk { previous, new, next })
1655                }
1656            }
1657        }
1658
1659        new_chunk
1660    }
1661
1662    /// Unlink this chunk.
1663    ///
1664    /// Be careful: `self` won't belong to `LinkedChunk` anymore, and should be
1665    /// dropped appropriately.
1666    fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1667        let previous_ptr = self.previous;
1668        let next_ptr = self.next;
1669        // If `self` is not the first, `lazy_previous` might be set on its
1670        // previous chunk. Otherwise, if `lazy_previous` is set on `self`, it
1671        // means it's the first chunk and it must be moved onto the next chunk.
1672        let lazy_previous = self.lazy_previous.take();
1673
1674        if let Some(previous) = self.previous_mut() {
1675            previous.next = next_ptr;
1676        }
1677
1678        if let Some(next) = self.next_mut() {
1679            next.previous = previous_ptr;
1680            next.lazy_previous = lazy_previous;
1681        }
1682
1683        if let Some(updates) = updates {
1684            updates.push(Update::RemoveChunk(self.identifier()));
1685        }
1686    }
1687
1688    /// Get a reference to the previous chunk if any.
1689    fn previous(&self) -> Option<&Self> {
1690        self.previous.map(|non_null| unsafe { non_null.as_ref() })
1691    }
1692
1693    /// Get a mutable to the previous chunk if any.
1694    fn previous_mut(&mut self) -> Option<&mut Self> {
1695        self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1696    }
1697
1698    /// Get a reference to the next chunk if any.
1699    fn next(&self) -> Option<&Self> {
1700        self.next.map(|non_null| unsafe { non_null.as_ref() })
1701    }
1702
1703    /// Get a mutable reference to the next chunk if any.
1704    fn next_mut(&mut self) -> Option<&mut Self> {
1705        self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1706    }
1707}
1708
1709impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<CAP, Item, Gap>
1710where
1711    Item: fmt::Debug,
1712    Gap: fmt::Debug,
1713{
1714    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1715        formatter
1716            .debug_struct("LinkedChunk")
1717            .field("first (deref)", self.links.first_chunk())
1718            .field("last", &self.links.last)
1719            .finish_non_exhaustive()
1720    }
1721}
1722
1723impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1724where
1725    Item: fmt::Debug,
1726    Gap: fmt::Debug,
1727{
1728    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1729        formatter
1730            .debug_struct("Chunk")
1731            .field("identifier", &self.identifier)
1732            .field("content", &self.content)
1733            .field("previous", &self.previous)
1734            .field("ptr", &std::ptr::from_ref(self))
1735            .field("next", &self.next)
1736            .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1737            .finish()
1738    }
1739}
1740
1741/// The raw representation of a linked chunk, as persisted in storage.
1742///
1743/// It may rebuilt into [`Chunk`] and shares the same internal representation,
1744/// except that links are materialized using [`ChunkIdentifier`] instead of raw
1745/// pointers to the previous and next chunks.
1746#[derive(Clone, Debug)]
1747pub struct RawChunk<Item, Gap> {
1748    /// Content section of the linked chunk.
1749    pub content: ChunkContent<Item, Gap>,
1750
1751    /// Link to the previous chunk, via its identifier.
1752    pub previous: Option<ChunkIdentifier>,
1753
1754    /// Current chunk's identifier.
1755    pub identifier: ChunkIdentifier,
1756
1757    /// Link to the next chunk, via its identifier.
1758    pub next: Option<ChunkIdentifier>,
1759}
1760
1761/// A simplified [`RawChunk`] that only contains the number of items in a chunk,
1762/// instead of its type.
1763#[derive(Clone, Debug)]
1764pub struct ChunkMetadata {
1765    /// The number of items in this chunk.
1766    ///
1767    /// By convention, a gap chunk contains 0 items.
1768    pub num_items: usize,
1769
1770    /// Link to the previous chunk, via its identifier.
1771    pub previous: Option<ChunkIdentifier>,
1772
1773    /// Current chunk's identifier.
1774    pub identifier: ChunkIdentifier,
1775
1776    /// Link to the next chunk, via its identifier.
1777    pub next: Option<ChunkIdentifier>,
1778}
1779
1780#[cfg(test)]
1781mod tests {
1782    use std::{
1783        ops::Not,
1784        sync::{Arc, atomic::Ordering},
1785    };
1786
1787    use assert_matches::assert_matches;
1788
1789    use super::{
1790        Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Error, LinkedChunk,
1791        Position, Update::*,
1792    };
1793
1794    #[test]
1795    fn test_chunk_identifier_generator() {
1796        let generator = ChunkIdentifierGenerator::new_from_scratch();
1797
1798        assert_eq!(generator.next(), ChunkIdentifier(1));
1799        assert_eq!(generator.next(), ChunkIdentifier(2));
1800        assert_eq!(generator.next(), ChunkIdentifier(3));
1801        assert_eq!(generator.next(), ChunkIdentifier(4));
1802
1803        let generator =
1804            ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1805
1806        assert_eq!(generator.next(), ChunkIdentifier(43));
1807        assert_eq!(generator.next(), ChunkIdentifier(44));
1808        assert_eq!(generator.next(), ChunkIdentifier(45));
1809        assert_eq!(generator.next(), ChunkIdentifier(46));
1810    }
1811
1812    #[test]
1813    fn test_empty() {
1814        let items = LinkedChunk::<3, char, ()>::new();
1815
1816        assert_eq!(items.num_items(), 0);
1817
1818        // This test also ensures that `Drop` for `LinkedChunk` works when there
1819        // is only one chunk.
1820    }
1821
1822    #[test]
1823    fn test_updates() {
1824        assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1825        assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1826    }
1827
1828    #[test]
1829    fn test_new_with_initial_update() {
1830        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1831
1832        // No chunk is created to start with.
1833        assert!(linked_chunk.updates().unwrap().take().is_empty());
1834
1835        // However, as soon as the first chunk is read, the chunk is created.
1836        let _ = linked_chunk.first_chunk();
1837
1838        assert_eq!(
1839            linked_chunk.updates().unwrap().take(),
1840            &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1841        );
1842    }
1843
1844    #[test]
1845    fn test_push_items() {
1846        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1847
1848        linked_chunk.push_items_back(['a']);
1849
1850        assert_items_eq!(linked_chunk, ['a']);
1851        assert_eq!(
1852            linked_chunk.updates().unwrap().take(),
1853            &[
1854                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
1855                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }
1856            ]
1857        );
1858
1859        linked_chunk.push_items_back(['b', 'c']);
1860        assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1861        assert_eq!(
1862            linked_chunk.updates().unwrap().take(),
1863            &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1864        );
1865
1866        linked_chunk.push_items_back(['d', 'e']);
1867        assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
1868        assert_eq!(
1869            linked_chunk.updates().unwrap().take(),
1870            &[
1871                NewItemsChunk {
1872                    previous: Some(ChunkIdentifier(0)),
1873                    new: ChunkIdentifier(1),
1874                    next: None
1875                },
1876                PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1877            ]
1878        );
1879
1880        linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1881        assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1882        assert_eq!(
1883            linked_chunk.updates().unwrap().take(),
1884            &[
1885                PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1886                NewItemsChunk {
1887                    previous: Some(ChunkIdentifier(1)),
1888                    new: ChunkIdentifier(2),
1889                    next: None,
1890                },
1891                PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1892                NewItemsChunk {
1893                    previous: Some(ChunkIdentifier(2)),
1894                    new: ChunkIdentifier(3),
1895                    next: None,
1896                },
1897                PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1898            ]
1899        );
1900
1901        assert_eq!(linked_chunk.num_items(), 10);
1902    }
1903
1904    #[test]
1905    fn test_push_gap() {
1906        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1907
1908        linked_chunk.push_items_back(['a']);
1909        assert_items_eq!(linked_chunk, ['a']);
1910        assert_eq!(
1911            linked_chunk.updates().unwrap().take(),
1912            &[
1913                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
1914                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }
1915            ]
1916        );
1917
1918        linked_chunk.push_gap_back(());
1919        assert_items_eq!(linked_chunk, ['a'] [-]);
1920        assert_eq!(
1921            linked_chunk.updates().unwrap().take(),
1922            &[NewGapChunk {
1923                previous: Some(ChunkIdentifier(0)),
1924                new: ChunkIdentifier(1),
1925                next: None,
1926                gap: (),
1927            }]
1928        );
1929
1930        linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1931        assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1932        assert_eq!(
1933            linked_chunk.updates().unwrap().take(),
1934            &[
1935                NewItemsChunk {
1936                    previous: Some(ChunkIdentifier(1)),
1937                    new: ChunkIdentifier(2),
1938                    next: None,
1939                },
1940                PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
1941                NewItemsChunk {
1942                    previous: Some(ChunkIdentifier(2)),
1943                    new: ChunkIdentifier(3),
1944                    next: None,
1945                },
1946                PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
1947            ]
1948        );
1949
1950        linked_chunk.push_gap_back(());
1951        linked_chunk.push_gap_back(()); // why not
1952        assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
1953        assert_eq!(
1954            linked_chunk.updates().unwrap().take(),
1955            &[
1956                NewGapChunk {
1957                    previous: Some(ChunkIdentifier(3)),
1958                    new: ChunkIdentifier(4),
1959                    next: None,
1960                    gap: (),
1961                },
1962                NewGapChunk {
1963                    previous: Some(ChunkIdentifier(4)),
1964                    new: ChunkIdentifier(5),
1965                    next: None,
1966                    gap: (),
1967                }
1968            ]
1969        );
1970
1971        linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
1972        assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
1973        assert_eq!(
1974            linked_chunk.updates().unwrap().take(),
1975            &[
1976                NewItemsChunk {
1977                    previous: Some(ChunkIdentifier(5)),
1978                    new: ChunkIdentifier(6),
1979                    next: None,
1980                },
1981                PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
1982                NewItemsChunk {
1983                    previous: Some(ChunkIdentifier(6)),
1984                    new: ChunkIdentifier(7),
1985                    next: None,
1986                },
1987                PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
1988            ]
1989        );
1990
1991        assert_eq!(linked_chunk.num_items(), 9);
1992    }
1993
1994    #[test]
1995    fn test_identifiers_and_positions() {
1996        let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
1997        linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
1998        linked_chunk.push_gap_back(());
1999        linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
2000        assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
2001
2002        assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
2003        assert_eq!(
2004            linked_chunk.item_position(|item| *item == 'e'),
2005            Some(Position(ChunkIdentifier(1), 1))
2006        );
2007    }
2008
2009    #[test]
2010    fn test_rchunks() {
2011        let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2012        linked_chunk.push_items_back(['a', 'b']);
2013        linked_chunk.push_gap_back(());
2014        linked_chunk.push_items_back(['c', 'd', 'e']);
2015
2016        let mut iterator = linked_chunk.rchunks();
2017
2018        assert_matches!(
2019            iterator.next(),
2020            Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2021                assert_eq!(items, &['e']);
2022            }
2023        );
2024        assert_matches!(
2025            iterator.next(),
2026            Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2027                assert_eq!(items, &['c', 'd']);
2028            }
2029        );
2030        assert_matches!(
2031            iterator.next(),
2032            Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2033        );
2034        assert_matches!(
2035            iterator.next(),
2036            Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2037                assert_eq!(items, &['a', 'b']);
2038            }
2039        );
2040        assert_matches!(iterator.next(), None);
2041    }
2042
2043    #[test]
2044    fn test_chunks() {
2045        let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2046        linked_chunk.push_items_back(['a', 'b']);
2047        linked_chunk.push_gap_back(());
2048        linked_chunk.push_items_back(['c', 'd', 'e']);
2049
2050        let mut iterator = linked_chunk.chunks();
2051
2052        assert_matches!(
2053            iterator.next(),
2054            Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2055                assert_eq!(items, &['a', 'b']);
2056            }
2057        );
2058        assert_matches!(
2059            iterator.next(),
2060            Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2061        );
2062        assert_matches!(
2063            iterator.next(),
2064            Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2065                assert_eq!(items, &['c', 'd']);
2066            }
2067        );
2068        assert_matches!(
2069            iterator.next(),
2070            Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2071                assert_eq!(items, &['e']);
2072            }
2073        );
2074        assert_matches!(iterator.next(), None);
2075    }
2076
2077    #[test]
2078    fn test_rchunks_from() -> Result<(), Error> {
2079        let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2080        linked_chunk.push_items_back(['a', 'b']);
2081        linked_chunk.push_gap_back(());
2082        linked_chunk.push_items_back(['c', 'd', 'e']);
2083
2084        let mut iterator = linked_chunk.rchunks_from(
2085            linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2086        )?;
2087
2088        assert_matches!(
2089            iterator.next(),
2090            Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2091                assert_eq!(items, &['c', 'd']);
2092            }
2093        );
2094        assert_matches!(
2095            iterator.next(),
2096            Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2097        );
2098        assert_matches!(
2099            iterator.next(),
2100            Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2101                assert_eq!(items, &['a', 'b']);
2102            }
2103        );
2104        assert_matches!(iterator.next(), None);
2105
2106        Ok(())
2107    }
2108
2109    #[test]
2110    fn test_chunks_from() -> Result<(), Error> {
2111        let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2112        linked_chunk.push_items_back(['a', 'b']);
2113        linked_chunk.push_gap_back(());
2114        linked_chunk.push_items_back(['c', 'd', 'e']);
2115
2116        let mut iterator = linked_chunk.chunks_from(
2117            linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2118        )?;
2119
2120        assert_matches!(
2121            iterator.next(),
2122            Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2123                assert_eq!(items, &['c', 'd']);
2124            }
2125        );
2126        assert_matches!(
2127            iterator.next(),
2128            Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2129                assert_eq!(items, &['e']);
2130            }
2131        );
2132        assert_matches!(iterator.next(), None);
2133
2134        Ok(())
2135    }
2136
2137    #[test]
2138    fn test_ritems() {
2139        let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2140        linked_chunk.push_items_back(['a', 'b']);
2141        linked_chunk.push_gap_back(());
2142        linked_chunk.push_items_back(['c', 'd', 'e']);
2143
2144        let mut iterator = linked_chunk.ritems();
2145
2146        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2147        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2148        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2149        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2150        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2151        assert_matches!(iterator.next(), None);
2152    }
2153
2154    #[test]
2155    fn test_ritems_with_final_gap() -> Result<(), Error> {
2156        let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2157        linked_chunk.push_items_back(['a', 'b']);
2158        linked_chunk.push_gap_back(());
2159        linked_chunk.push_items_back(['c', 'd', 'e']);
2160        linked_chunk.push_gap_back(());
2161
2162        let mut iterator = linked_chunk.ritems();
2163
2164        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
2165        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2166        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2167        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2168        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2169        assert_matches!(iterator.next(), None);
2170
2171        Ok(())
2172    }
2173
2174    #[test]
2175    fn test_ritems_empty() {
2176        let linked_chunk = LinkedChunk::<2, char, ()>::new();
2177        let mut iterator = linked_chunk.ritems();
2178
2179        assert_matches!(iterator.next(), None);
2180    }
2181
2182    #[test]
2183    fn test_items() {
2184        let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2185        linked_chunk.push_items_back(['a', 'b']);
2186        linked_chunk.push_gap_back(());
2187        linked_chunk.push_items_back(['c', 'd', 'e']);
2188
2189        let mut iterator = linked_chunk.items();
2190
2191        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2192        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2193        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2194        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2195        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2196        assert_matches!(iterator.next(), None);
2197    }
2198
2199    #[test]
2200    fn test_items_empty() {
2201        let linked_chunk = LinkedChunk::<2, char, ()>::new();
2202        let mut iterator = linked_chunk.items();
2203
2204        assert_matches!(iterator.next(), None);
2205    }
2206
2207    #[test]
2208    fn test_ritems_from() -> Result<(), Error> {
2209        let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2210        linked_chunk.push_items_back(['a', 'b']);
2211        linked_chunk.push_gap_back(());
2212        linked_chunk.push_items_back(['c', 'd', 'e']);
2213
2214        let mut iterator =
2215            linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2216
2217        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2218        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2219        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2220        assert_matches!(iterator.next(), None);
2221
2222        Ok(())
2223    }
2224
2225    #[test]
2226    fn test_items_from() -> Result<(), Error> {
2227        let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2228        linked_chunk.push_items_back(['a', 'b']);
2229        linked_chunk.push_gap_back(());
2230        linked_chunk.push_items_back(['c', 'd', 'e']);
2231
2232        let mut iterator =
2233            linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2234
2235        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2236        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2237        assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2238        assert_matches!(iterator.next(), None);
2239
2240        Ok(())
2241    }
2242
2243    #[test]
2244    fn test_insert_items_at() -> Result<(), Error> {
2245        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2246
2247        linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2248        assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2249        assert_eq!(
2250            linked_chunk.updates().unwrap().take(),
2251            &[
2252                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2253                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2254                NewItemsChunk {
2255                    previous: Some(ChunkIdentifier(0)),
2256                    new: ChunkIdentifier(1),
2257                    next: None,
2258                },
2259                PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2260            ]
2261        );
2262
2263        // Insert inside the last chunk.
2264        {
2265            let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2266
2267            // Insert 4 elements, so that it overflows the chunk capacity. It's
2268            // important to see whether chunks are correctly updated and linked.
2269            linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2270
2271            assert_items_eq!(
2272                linked_chunk,
2273                ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2274            );
2275            assert_eq!(linked_chunk.num_items(), 10);
2276            assert_eq!(
2277                linked_chunk.updates().unwrap().take(),
2278                &[
2279                    DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2280                    PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2281                    NewItemsChunk {
2282                        previous: Some(ChunkIdentifier(1)),
2283                        new: ChunkIdentifier(2),
2284                        next: None,
2285                    },
2286                    PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2287                    StartReattachItems,
2288                    PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2289                    NewItemsChunk {
2290                        previous: Some(ChunkIdentifier(2)),
2291                        new: ChunkIdentifier(3),
2292                        next: None,
2293                    },
2294                    PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2295                    EndReattachItems,
2296                ]
2297            );
2298        }
2299
2300        // Insert inside the first chunk.
2301        {
2302            let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2303            linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2304
2305            assert_items_eq!(
2306                linked_chunk,
2307                ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2308            );
2309            assert_eq!(linked_chunk.num_items(), 14);
2310            assert_eq!(
2311                linked_chunk.updates().unwrap().take(),
2312                &[
2313                    DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2314                    PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2315                    NewItemsChunk {
2316                        previous: Some(ChunkIdentifier(0)),
2317                        new: ChunkIdentifier(4),
2318                        next: Some(ChunkIdentifier(1)),
2319                    },
2320                    PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2321                    StartReattachItems,
2322                    PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2323                    NewItemsChunk {
2324                        previous: Some(ChunkIdentifier(4)),
2325                        new: ChunkIdentifier(5),
2326                        next: Some(ChunkIdentifier(1)),
2327                    },
2328                    PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2329                    EndReattachItems,
2330                ]
2331            );
2332        }
2333
2334        // Insert inside a middle chunk.
2335        {
2336            let pos_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2337            linked_chunk.insert_items_at(pos_c, ['r', 's'])?;
2338
2339            assert_items_eq!(
2340                linked_chunk,
2341                ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2342            );
2343            assert_eq!(linked_chunk.num_items(), 16);
2344            assert_eq!(
2345                linked_chunk.updates().unwrap().take(),
2346                &[
2347                    DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2348                    PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2349                    StartReattachItems,
2350                    PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2351                    EndReattachItems,
2352                ]
2353            );
2354        }
2355
2356        // Insert at the end of a chunk.
2357        {
2358            let pos_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2359            let pos_f = Position(pos_f.chunk_identifier(), pos_f.index() + 1);
2360
2361            linked_chunk.insert_items_at(pos_f, ['p', 'q'])?;
2362            assert_items_eq!(
2363                linked_chunk,
2364                ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2365            );
2366            assert_eq!(
2367                linked_chunk.updates().unwrap().take(),
2368                &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2369            );
2370            assert_eq!(linked_chunk.num_items(), 18);
2371        }
2372
2373        // Insert in a chunk that does not exist.
2374        {
2375            assert_matches!(
2376                linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2377                Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2378            );
2379            assert!(linked_chunk.updates().unwrap().take().is_empty());
2380        }
2381
2382        // Insert in a chunk that exists, but at an item that does not exist.
2383        {
2384            assert_matches!(
2385                linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2386                Err(Error::InvalidItemIndex { index: 128 })
2387            );
2388            assert!(linked_chunk.updates().unwrap().take().is_empty());
2389        }
2390
2391        // Insert in a gap.
2392        {
2393            // Add a gap to test the error.
2394            linked_chunk.push_gap_back(());
2395            assert_items_eq!(
2396                linked_chunk,
2397                ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2398            );
2399            assert_eq!(
2400                linked_chunk.updates().unwrap().take(),
2401                &[NewGapChunk {
2402                    previous: Some(ChunkIdentifier(3)),
2403                    new: ChunkIdentifier(6),
2404                    next: None,
2405                    gap: ()
2406                }]
2407            );
2408
2409            assert_matches!(
2410                linked_chunk.insert_items_at(Position(ChunkIdentifier(6), 0), ['u', 'v'],),
2411                Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2412            );
2413        }
2414
2415        assert_eq!(linked_chunk.num_items(), 18);
2416
2417        Ok(())
2418    }
2419
2420    #[test]
2421    fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2422        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2423
2424        linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2425        assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2426        assert_eq!(
2427            linked_chunk.updates().unwrap().take(),
2428            &[
2429                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2430                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2431                NewItemsChunk {
2432                    previous: Some(ChunkIdentifier(0)),
2433                    new: ChunkIdentifier(1),
2434                    next: None,
2435                },
2436                PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2437            ]
2438        );
2439
2440        // Insert inside the last chunk.
2441        let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2442
2443        // Insert 4 elements, so that it overflows the chunk capacity. It's
2444        // important to see whether chunks are correctly updated and linked.
2445        linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2446
2447        assert_items_eq!(
2448            linked_chunk,
2449            ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2450        );
2451        assert_eq!(linked_chunk.num_items(), 10);
2452        assert_eq!(
2453            linked_chunk.updates().unwrap().take(),
2454            &[
2455                DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2456                PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2457                NewItemsChunk {
2458                    previous: Some(ChunkIdentifier(1)),
2459                    new: ChunkIdentifier(2),
2460                    next: None,
2461                },
2462                PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2463                StartReattachItems,
2464                PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2465                NewItemsChunk {
2466                    previous: Some(ChunkIdentifier(2)),
2467                    new: ChunkIdentifier(3),
2468                    next: None,
2469                },
2470                PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2471                EndReattachItems,
2472            ]
2473        );
2474
2475        Ok(())
2476    }
2477
2478    #[test]
2479    fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2480        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2481
2482        linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2483        assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2484        assert_eq!(
2485            linked_chunk.updates().unwrap().take(),
2486            &[
2487                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2488                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2489                NewItemsChunk {
2490                    previous: Some(ChunkIdentifier(0)),
2491                    new: ChunkIdentifier(1),
2492                    next: None,
2493                },
2494                PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2495            ]
2496        );
2497
2498        // Insert inside the first chunk.
2499        let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2500        linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2501
2502        assert_items_eq!(
2503            linked_chunk,
2504            ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'e', 'f']
2505        );
2506        assert_eq!(linked_chunk.num_items(), 10);
2507        assert_eq!(
2508            linked_chunk.updates().unwrap().take(),
2509            &[
2510                DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2511                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2512                NewItemsChunk {
2513                    previous: Some(ChunkIdentifier(0)),
2514                    new: ChunkIdentifier(2),
2515                    next: Some(ChunkIdentifier(1)),
2516                },
2517                PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2518                StartReattachItems,
2519                PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2520                NewItemsChunk {
2521                    previous: Some(ChunkIdentifier(2)),
2522                    new: ChunkIdentifier(3),
2523                    next: Some(ChunkIdentifier(1)),
2524                },
2525                PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2526                EndReattachItems,
2527            ]
2528        );
2529
2530        Ok(())
2531    }
2532
2533    #[test]
2534    fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2535        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2536
2537        linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2538        assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2539        assert_eq!(
2540            linked_chunk.updates().unwrap().take(),
2541            &[
2542                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2543                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2544                NewItemsChunk {
2545                    previous: Some(ChunkIdentifier(0)),
2546                    new: ChunkIdentifier(1),
2547                    next: None,
2548                },
2549                PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2550                NewItemsChunk {
2551                    previous: Some(ChunkIdentifier(1)),
2552                    new: ChunkIdentifier(2),
2553                    next: None,
2554                },
2555                PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2556            ]
2557        );
2558
2559        let pos_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2560        linked_chunk.insert_items_at(pos_d, ['r', 's'])?;
2561
2562        assert_items_eq!(
2563            linked_chunk,
2564            ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2565        );
2566        assert_eq!(linked_chunk.num_items(), 10);
2567        assert_eq!(
2568            linked_chunk.updates().unwrap().take(),
2569            &[
2570                DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2571                PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2572                StartReattachItems,
2573                PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2574                NewItemsChunk {
2575                    previous: Some(ChunkIdentifier(1)),
2576                    new: ChunkIdentifier(3),
2577                    next: Some(ChunkIdentifier(2)),
2578                },
2579                PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2580                EndReattachItems,
2581            ]
2582        );
2583
2584        Ok(())
2585    }
2586
2587    #[test]
2588    fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2589        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2590
2591        linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2592        assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2593        assert_eq!(
2594            linked_chunk.updates().unwrap().take(),
2595            &[
2596                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2597                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2598                NewItemsChunk {
2599                    previous: Some(ChunkIdentifier(0)),
2600                    new: ChunkIdentifier(1),
2601                    next: None,
2602                },
2603                PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2604            ]
2605        );
2606
2607        // Insert at the end of a chunk.
2608        let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2609        let pos_after_e = Position(pos_e.chunk_identifier(), pos_e.index() + 1);
2610
2611        linked_chunk.insert_items_at(pos_after_e, ['p', 'q'])?;
2612        assert_items_eq!(
2613            linked_chunk,
2614            ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2615        );
2616        assert_eq!(
2617            linked_chunk.updates().unwrap().take(),
2618            &[
2619                PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2620                NewItemsChunk {
2621                    previous: Some(ChunkIdentifier(1)),
2622                    new: ChunkIdentifier(2),
2623                    next: None
2624                },
2625                PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2626            ]
2627        );
2628        assert_eq!(linked_chunk.num_items(), 7);
2629
2630        Ok(())
2631    }
2632
2633    #[test]
2634    fn test_insert_items_at_errs() -> Result<(), Error> {
2635        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2636
2637        linked_chunk.push_items_back(['a', 'b', 'c']);
2638        linked_chunk.push_gap_back(());
2639        assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2640        assert_eq!(
2641            linked_chunk.updates().unwrap().take(),
2642            &[
2643                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2644                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2645                NewGapChunk {
2646                    previous: Some(ChunkIdentifier(0)),
2647                    new: ChunkIdentifier(1),
2648                    next: None,
2649                    gap: (),
2650                },
2651            ]
2652        );
2653
2654        // Insert in a chunk that does not exist.
2655        {
2656            assert_matches!(
2657                linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2658                Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2659            );
2660            assert!(linked_chunk.updates().unwrap().take().is_empty());
2661        }
2662
2663        // Insert in a chunk that exists, but at an item that does not exist.
2664        {
2665            assert_matches!(
2666                linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2667                Err(Error::InvalidItemIndex { index: 128 })
2668            );
2669            assert!(linked_chunk.updates().unwrap().take().is_empty());
2670        }
2671
2672        // Insert in a gap.
2673        {
2674            assert_matches!(
2675                linked_chunk.insert_items_at(Position(ChunkIdentifier(1), 0), ['u', 'v'],),
2676                Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2677            );
2678        }
2679
2680        Ok(())
2681    }
2682
2683    #[test]
2684    fn test_remove_item_at() -> Result<(), Error> {
2685        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2686
2687        linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2688        assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2689        assert_eq!(linked_chunk.num_items(), 11);
2690
2691        // Ignore previous updates.
2692        let _ = linked_chunk.updates().unwrap().take();
2693
2694        // Remove the last item of the middle chunk, 3 times. The chunk is empty
2695        // after that. The chunk is removed.
2696        {
2697            let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2698            let removed_item = linked_chunk.remove_item_at(position_of_f)?;
2699
2700            assert_eq!(removed_item, 'f');
2701            assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2702            assert_eq!(linked_chunk.num_items(), 10);
2703
2704            let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2705            let removed_item = linked_chunk.remove_item_at(position_of_e)?;
2706
2707            assert_eq!(removed_item, 'e');
2708            assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2709            assert_eq!(linked_chunk.num_items(), 9);
2710
2711            let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2712            let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2713
2714            assert_eq!(removed_item, 'd');
2715            assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2716            assert_eq!(linked_chunk.num_items(), 8);
2717
2718            assert_eq!(
2719                linked_chunk.updates().unwrap().take(),
2720                &[
2721                    RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2722                    RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2723                    RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2724                    RemoveChunk(ChunkIdentifier(1)),
2725                ]
2726            );
2727        }
2728
2729        // Remove the first item of the first chunk, 3 times. The chunk is empty
2730        // after that. The chunk is NOT removed because it's the first chunk.
2731        {
2732            let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2733            let removed_item = linked_chunk.remove_item_at(first_position)?;
2734
2735            assert_eq!(removed_item, 'a');
2736            assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2737            assert_eq!(linked_chunk.num_items(), 7);
2738
2739            let removed_item = linked_chunk.remove_item_at(first_position)?;
2740
2741            assert_eq!(removed_item, 'b');
2742            assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2743            assert_eq!(linked_chunk.num_items(), 6);
2744
2745            let removed_item = linked_chunk.remove_item_at(first_position)?;
2746
2747            assert_eq!(removed_item, 'c');
2748            assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2749            assert_eq!(linked_chunk.num_items(), 5);
2750
2751            assert_eq!(
2752                linked_chunk.updates().unwrap().take(),
2753                &[
2754                    RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2755                    RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2756                    RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2757                ]
2758            );
2759        }
2760
2761        // Remove the first item of the middle chunk, 3 times. The chunk is
2762        // empty after that. The chunk is removed.
2763        {
2764            let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2765            let removed_item = linked_chunk.remove_item_at(first_position)?;
2766
2767            assert_eq!(removed_item, 'g');
2768            assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2769            assert_eq!(linked_chunk.num_items(), 4);
2770
2771            let removed_item = linked_chunk.remove_item_at(first_position)?;
2772
2773            assert_eq!(removed_item, 'h');
2774            assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2775            assert_eq!(linked_chunk.num_items(), 3);
2776
2777            let removed_item = linked_chunk.remove_item_at(first_position)?;
2778
2779            assert_eq!(removed_item, 'i');
2780            assert_items_eq!(linked_chunk, [] ['j', 'k']);
2781            assert_eq!(linked_chunk.num_items(), 2);
2782
2783            assert_eq!(
2784                linked_chunk.updates().unwrap().take(),
2785                &[
2786                    RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2787                    RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2788                    RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2789                    RemoveChunk(ChunkIdentifier(2)),
2790                ]
2791            );
2792        }
2793
2794        // Remove the last item of the last chunk, twice. The chunk is empty
2795        // after that. The chunk is removed.
2796        {
2797            let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2798            let removed_item = linked_chunk.remove_item_at(position_of_k)?;
2799
2800            assert_eq!(removed_item, 'k');
2801            #[rustfmt::skip]
2802            assert_items_eq!(linked_chunk, [] ['j']);
2803            assert_eq!(linked_chunk.num_items(), 1);
2804
2805            let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2806            let removed_item = linked_chunk.remove_item_at(position_of_j)?;
2807
2808            assert_eq!(removed_item, 'j');
2809            assert_items_eq!(linked_chunk, []);
2810            assert_eq!(linked_chunk.num_items(), 0);
2811
2812            assert_eq!(
2813                linked_chunk.updates().unwrap().take(),
2814                &[
2815                    RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2816                    RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2817                    RemoveChunk(ChunkIdentifier(3)),
2818                ]
2819            );
2820        }
2821
2822        // Add a couple more items, delete one, add a gap, and delete more
2823        // items.
2824        {
2825            linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2826
2827            #[rustfmt::skip]
2828            assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2829            assert_eq!(linked_chunk.num_items(), 4);
2830
2831            // Delete at a limit position (right after `c`), that is invalid.
2832            assert_matches!(
2833                linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 3)),
2834                Err(Error::InvalidItemIndex { index: 3 })
2835            );
2836
2837            // Delete at an out-of-bound position (way after `c`), that is
2838            // invalid.
2839            assert_matches!(
2840                linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 42)),
2841                Err(Error::InvalidItemIndex { index: 42 })
2842            );
2843
2844            let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2845            linked_chunk.insert_gap_at((), position_of_c)?;
2846
2847            assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2848            assert_eq!(linked_chunk.num_items(), 4);
2849
2850            // Ignore updates.
2851            let _ = linked_chunk.updates().unwrap().take();
2852
2853            let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2854            let removed_item = linked_chunk.remove_item_at(position_of_c)?;
2855
2856            assert_eq!(removed_item, 'c');
2857            assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2858            assert_eq!(linked_chunk.num_items(), 3);
2859
2860            let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2861            let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2862
2863            assert_eq!(removed_item, 'd');
2864            assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2865            assert_eq!(linked_chunk.num_items(), 2);
2866
2867            let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2868            let removed_item = linked_chunk.remove_item_at(first_position)?;
2869
2870            assert_eq!(removed_item, 'a');
2871            assert_items_eq!(linked_chunk, ['b'] [-]);
2872            assert_eq!(linked_chunk.num_items(), 1);
2873
2874            let removed_item = linked_chunk.remove_item_at(first_position)?;
2875
2876            assert_eq!(removed_item, 'b');
2877            assert_items_eq!(linked_chunk, [] [-]);
2878            assert_eq!(linked_chunk.num_items(), 0);
2879
2880            assert_eq!(
2881                linked_chunk.updates().unwrap().take(),
2882                &[
2883                    RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2884                    RemoveChunk(ChunkIdentifier(6)),
2885                    RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2886                    RemoveChunk(ChunkIdentifier(4)),
2887                    RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2888                    RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2889                ]
2890            );
2891        }
2892
2893        Ok(())
2894    }
2895
2896    #[test]
2897    fn test_insert_gap_at() -> Result<(), Error> {
2898        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2899
2900        linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2901        assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2902        assert_eq!(
2903            linked_chunk.updates().unwrap().take(),
2904            &[
2905                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2906                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2907                NewItemsChunk {
2908                    previous: Some(ChunkIdentifier(0)),
2909                    new: ChunkIdentifier(1),
2910                    next: None
2911                },
2912                PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2913            ]
2914        );
2915
2916        // Insert in the middle of a chunk.
2917        {
2918            let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
2919            linked_chunk.insert_gap_at((), position_of_b)?;
2920
2921            assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2922            assert_eq!(
2923                linked_chunk.updates().unwrap().take(),
2924                &[
2925                    DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
2926                    NewGapChunk {
2927                        previous: Some(ChunkIdentifier(0)),
2928                        new: ChunkIdentifier(2),
2929                        next: Some(ChunkIdentifier(1)),
2930                        gap: (),
2931                    },
2932                    StartReattachItems,
2933                    NewItemsChunk {
2934                        previous: Some(ChunkIdentifier(2)),
2935                        new: ChunkIdentifier(3),
2936                        next: Some(ChunkIdentifier(1)),
2937                    },
2938                    PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
2939                    EndReattachItems,
2940                ]
2941            );
2942        }
2943
2944        // Insert at the beginning of a chunk. The targeted chunk is the first
2945        // chunk. `Ends::first` and `Ends::last` may be updated differently.
2946        {
2947            let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2948            linked_chunk.insert_gap_at((), position_of_a)?;
2949
2950            // A new empty chunk is NOT created, i.e. `['a']` is not split into
2951            // `[]` + `['a']` because it's a waste of space.
2952            assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2953            assert_eq!(
2954                linked_chunk.updates().unwrap().take(),
2955                &[NewGapChunk {
2956                    previous: None,
2957                    new: ChunkIdentifier(4),
2958                    next: Some(ChunkIdentifier(0)),
2959                    gap: (),
2960                },]
2961            );
2962        }
2963
2964        // Insert at the beginning of a chunk. The targeted chunk is not the
2965        // first chunk. `Ends::first` and `Ends::last` may be updated
2966        // differently.
2967        {
2968            let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2969            linked_chunk.insert_gap_at((), position_of_d)?;
2970
2971            // A new empty chunk is NOT created, i.e. `['d', 'e', 'f']` is not
2972            // split into `[]` + `['d', 'e', 'f']` because it's a waste of
2973            // space.
2974            assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
2975            assert_eq!(
2976                linked_chunk.updates().unwrap().take(),
2977                &[NewGapChunk {
2978                    previous: Some(ChunkIdentifier(3)),
2979                    new: ChunkIdentifier(5),
2980                    next: Some(ChunkIdentifier(1)),
2981                    gap: (),
2982                }]
2983            );
2984        }
2985
2986        // Insert in an empty chunk.
2987        {
2988            // Replace a gap by empty items.
2989            let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
2990            let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
2991
2992            assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
2993
2994            assert_eq!(
2995                linked_chunk.updates().unwrap().take(),
2996                &[
2997                    NewItemsChunk {
2998                        previous: Some(ChunkIdentifier(5)),
2999                        new: ChunkIdentifier(6),
3000                        next: Some(ChunkIdentifier(1)),
3001                    },
3002                    RemoveChunk(ChunkIdentifier(5)),
3003                ]
3004            );
3005
3006            linked_chunk.insert_gap_at((), position)?;
3007
3008            assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
3009            assert_eq!(
3010                linked_chunk.updates().unwrap().take(),
3011                &[NewGapChunk {
3012                    previous: Some(ChunkIdentifier(3)),
3013                    new: ChunkIdentifier(7),
3014                    next: Some(ChunkIdentifier(6)),
3015                    gap: (),
3016                }]
3017            );
3018        }
3019
3020        // Insert in a chunk that does not exist.
3021        {
3022            assert_matches!(
3023                linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
3024                Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
3025            );
3026            assert!(linked_chunk.updates().unwrap().take().is_empty());
3027        }
3028
3029        // Insert in a chunk that exists, but at an item that does not exist.
3030        {
3031            assert_matches!(
3032                linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
3033                Err(Error::InvalidItemIndex { index: 128 })
3034            );
3035            assert!(linked_chunk.updates().unwrap().take().is_empty());
3036        }
3037
3038        // Insert in an existing gap.
3039        {
3040            // It is impossible to get the item position inside a gap. It's only
3041            // possible if the item position is crafted by hand or is outdated.
3042            let position_of_a_gap = Position(ChunkIdentifier(2), 0);
3043            assert_matches!(
3044                linked_chunk.insert_gap_at((), position_of_a_gap),
3045                Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
3046            );
3047            assert!(linked_chunk.updates().unwrap().take().is_empty());
3048        }
3049
3050        assert_eq!(linked_chunk.num_items(), 6);
3051
3052        Ok(())
3053    }
3054
3055    #[test]
3056    fn test_replace_gap_at_middle() -> Result<(), Error> {
3057        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3058
3059        linked_chunk.push_items_back(['a', 'b']);
3060        linked_chunk.push_gap_back(());
3061        linked_chunk.push_items_back(['l', 'm']);
3062        assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
3063        assert_eq!(
3064            linked_chunk.updates().unwrap().take(),
3065            &[
3066                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
3067                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3068                NewGapChunk {
3069                    previous: Some(ChunkIdentifier(0)),
3070                    new: ChunkIdentifier(1),
3071                    next: None,
3072                    gap: (),
3073                },
3074                NewItemsChunk {
3075                    previous: Some(ChunkIdentifier(1)),
3076                    new: ChunkIdentifier(2),
3077                    next: None,
3078                },
3079                PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
3080            ]
3081        );
3082
3083        // Replace a gap in the middle of the linked chunk.
3084        let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3085        assert_eq!(gap_identifier, ChunkIdentifier(1));
3086
3087        let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
3088        assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
3089        assert_items_eq!(
3090            linked_chunk,
3091            ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
3092        );
3093        assert_eq!(
3094            linked_chunk.updates().unwrap().take(),
3095            &[
3096                NewItemsChunk {
3097                    previous: Some(ChunkIdentifier(1)),
3098                    new: ChunkIdentifier(3),
3099                    next: Some(ChunkIdentifier(2)),
3100                },
3101                PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
3102                NewItemsChunk {
3103                    previous: Some(ChunkIdentifier(3)),
3104                    new: ChunkIdentifier(4),
3105                    next: Some(ChunkIdentifier(2)),
3106                },
3107                PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
3108                RemoveChunk(ChunkIdentifier(1)),
3109            ]
3110        );
3111
3112        assert_eq!(linked_chunk.num_items(), 9);
3113
3114        Ok(())
3115    }
3116
3117    #[test]
3118    fn test_replace_gap_at_end() -> Result<(), Error> {
3119        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3120
3121        linked_chunk.push_items_back(['a', 'b']);
3122        linked_chunk.push_gap_back(());
3123        assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3124        assert_eq!(
3125            linked_chunk.updates().unwrap().take(),
3126            &[
3127                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
3128                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3129                NewGapChunk {
3130                    previous: Some(ChunkIdentifier(0)),
3131                    new: ChunkIdentifier(1),
3132                    next: None,
3133                    gap: (),
3134                },
3135            ]
3136        );
3137
3138        // Replace a gap at the end of the linked chunk.
3139        let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3140        assert_eq!(gap_identifier, ChunkIdentifier(1));
3141
3142        let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3143        assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3144        assert_items_eq!(
3145            linked_chunk,
3146            ['a', 'b'] ['w', 'x', 'y'] ['z']
3147        );
3148        assert_eq!(
3149            linked_chunk.updates().unwrap().take(),
3150            &[
3151                NewItemsChunk {
3152                    previous: Some(ChunkIdentifier(1)),
3153                    new: ChunkIdentifier(2),
3154                    next: None,
3155                },
3156                PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3157                NewItemsChunk {
3158                    previous: Some(ChunkIdentifier(2)),
3159                    new: ChunkIdentifier(3),
3160                    next: None,
3161                },
3162                PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3163                RemoveChunk(ChunkIdentifier(1)),
3164            ]
3165        );
3166
3167        assert_eq!(linked_chunk.num_items(), 6);
3168
3169        Ok(())
3170    }
3171
3172    #[test]
3173    fn test_replace_gap_at_beginning() -> Result<(), Error> {
3174        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3175
3176        linked_chunk.push_items_back(['a', 'b']);
3177        assert_items_eq!(linked_chunk, ['a', 'b']);
3178        assert_eq!(
3179            linked_chunk.updates().unwrap().take(),
3180            &[
3181                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
3182                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3183            ]
3184        );
3185
3186        // Replace a gap at the beginning of the linked chunk.
3187        let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3188        linked_chunk.insert_gap_at((), position_of_a).unwrap();
3189        assert_items_eq!(
3190            linked_chunk,
3191            [-] ['a', 'b']
3192        );
3193        assert_eq!(
3194            linked_chunk.updates().unwrap().take(),
3195            &[NewGapChunk {
3196                previous: None,
3197                new: ChunkIdentifier(1),
3198                next: Some(ChunkIdentifier(0)),
3199                gap: (),
3200            }]
3201        );
3202
3203        let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3204        assert_eq!(gap_identifier, ChunkIdentifier(1));
3205
3206        let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3207        assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3208        assert_items_eq!(
3209            linked_chunk,
3210            ['x'] ['a', 'b']
3211        );
3212        assert_eq!(
3213            linked_chunk.updates().unwrap().take(),
3214            &[
3215                NewItemsChunk {
3216                    previous: Some(ChunkIdentifier(1)),
3217                    new: ChunkIdentifier(2),
3218                    next: Some(ChunkIdentifier(0)),
3219                },
3220                PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3221                RemoveChunk(ChunkIdentifier(1)),
3222            ]
3223        );
3224
3225        assert_eq!(linked_chunk.num_items(), 3);
3226
3227        Ok(())
3228    }
3229
3230    #[test]
3231    fn test_remove_empty_chunk_at() -> Result<(), Error> {
3232        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3233
3234        linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3235        linked_chunk.push_items_back(['a', 'b']);
3236        linked_chunk.push_gap_back(());
3237        linked_chunk.push_items_back(['l', 'm']);
3238        linked_chunk.push_gap_back(());
3239        assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3240        assert_eq!(
3241            linked_chunk.updates().unwrap().take(),
3242            &[
3243                NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
3244                NewGapChunk {
3245                    previous: None,
3246                    new: ChunkIdentifier(1),
3247                    next: Some(ChunkIdentifier(0)),
3248                    gap: (),
3249                },
3250                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3251                NewGapChunk {
3252                    previous: Some(ChunkIdentifier(0)),
3253                    new: ChunkIdentifier(2),
3254                    next: None,
3255                    gap: (),
3256                },
3257                NewItemsChunk {
3258                    previous: Some(ChunkIdentifier(2)),
3259                    new: ChunkIdentifier(3),
3260                    next: None,
3261                },
3262                PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3263                NewGapChunk {
3264                    previous: Some(ChunkIdentifier(3)),
3265                    new: ChunkIdentifier(4),
3266                    next: None,
3267                    gap: (),
3268                },
3269            ]
3270        );
3271
3272        // Try to remove a chunk that's not empty.
3273        let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3274        assert_matches!(err, Error::RemovingNonEmptyItemsChunk { .. });
3275
3276        // Try to remove an unknown gap chunk.
3277        let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(42)).unwrap_err();
3278        assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3279
3280        // Remove the gap in the middle.
3281        let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(2)).unwrap();
3282        let next = maybe_next.unwrap();
3283        // The next insert position at the start of the next chunk.
3284        assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3285        assert_eq!(next.index(), 0);
3286        assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3287        assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3288
3289        // Remove the gap at the end.
3290        let next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(4)).unwrap();
3291        // It was the last chunk, so there's no next insert position.
3292        assert!(next.is_none());
3293        assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3294        assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3295
3296        // Remove the gap at the beginning.
3297        let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(1)).unwrap();
3298        let next = maybe_next.unwrap();
3299        assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3300        assert_eq!(next.index(), 0);
3301        assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3302        assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3303
3304        Ok(())
3305    }
3306
3307    #[test]
3308    fn test_remove_empty_last_chunk() {
3309        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3310
3311        assert!(linked_chunk.updates().unwrap().take().is_empty());
3312
3313        // Try to remove the first chunk.
3314        let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3315        assert_matches!(err, Error::RemovingLastChunk);
3316    }
3317
3318    #[test]
3319    fn test_chunk_item_positions() {
3320        let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3321        linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3322        linked_chunk.push_gap_back(());
3323        linked_chunk.push_items_back(['f']);
3324
3325        assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3326
3327        let mut iterator = linked_chunk.chunks();
3328
3329        // First chunk.
3330        {
3331            let chunk = iterator.next().unwrap();
3332            assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3333            assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3334        }
3335
3336        // Second chunk.
3337        {
3338            let chunk = iterator.next().unwrap();
3339            assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3340            assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3341        }
3342
3343        // Gap.
3344        {
3345            let chunk = iterator.next().unwrap();
3346            assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3347            assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3348        }
3349
3350        // Last chunk.
3351        {
3352            let chunk = iterator.next().unwrap();
3353            assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3354            assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3355        }
3356    }
3357
3358    #[test]
3359    fn test_is_first_and_last_chunk() {
3360        let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3361
3362        let mut chunks = linked_chunk.chunks().peekable();
3363        assert!(chunks.peek().unwrap().is_first_chunk());
3364        assert!(chunks.next().unwrap().is_last_chunk());
3365        assert!(chunks.next().is_none());
3366
3367        linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3368
3369        let mut chunks = linked_chunk.chunks().peekable();
3370        assert!(chunks.next().unwrap().is_first_chunk());
3371        assert!(chunks.peek().unwrap().is_first_chunk().not());
3372        assert!(chunks.next().unwrap().is_last_chunk().not());
3373        assert!(chunks.next().unwrap().is_last_chunk());
3374        assert!(chunks.next().is_none());
3375    }
3376
3377    // Test `LinkedChunk::clear`. This test creates a `LinkedChunk` with `new`
3378    // to avoid creating too much confusion with `Update`s. The next test
3379    // `test_clear_emit_an_update_clear` uses `new_with_update_history` and only
3380    // test `Update::Clear`.
3381    #[test]
3382    fn test_clear() {
3383        let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3384
3385        let item = Arc::new('a');
3386        let gap = Arc::new(());
3387
3388        linked_chunk.push_items_back([
3389            item.clone(),
3390            item.clone(),
3391            item.clone(),
3392            item.clone(),
3393            item.clone(),
3394        ]);
3395        linked_chunk.push_gap_back(gap.clone());
3396        linked_chunk.push_items_back([item.clone()]);
3397
3398        assert_eq!(Arc::strong_count(&item), 7);
3399        assert_eq!(Arc::strong_count(&gap), 2);
3400        assert_eq!(linked_chunk.chunks().filter(|chunk| chunk.is_items()).count(), 3);
3401        assert_eq!(linked_chunk.chunks().filter(|chunk| chunk.is_gap()).count(), 1);
3402        assert_eq!(linked_chunk.num_items(), 6);
3403        assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3404
3405        // Now, we can clear the linked chunk and see what happens.
3406        linked_chunk.clear();
3407
3408        assert_eq!(Arc::strong_count(&item), 1);
3409        assert_eq!(Arc::strong_count(&gap), 1);
3410        // One chunk because the first chunk is created lazily, which happens
3411        // when iterating over the chunks.
3412        assert_eq!(linked_chunk.chunks().filter(|chunk| chunk.is_items()).count(), 1);
3413        assert_eq!(linked_chunk.chunks().filter(|chunk| chunk.is_gap()).count(), 0);
3414        assert_eq!(linked_chunk.num_items(), 0);
3415        assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3416    }
3417
3418    #[test]
3419    fn test_clear_emits_an_update_clear() {
3420        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3421
3422        // Let's push an item in it.
3423        linked_chunk.push_items_back(['a']);
3424
3425        // We see the update now.
3426        assert_eq!(
3427            linked_chunk.updates().unwrap().take(),
3428            &[
3429                NewItemsChunk {
3430                    previous: None,
3431                    new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3432                    next: None
3433                },
3434                PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }
3435            ]
3436        );
3437
3438        // When clearing…
3439        linked_chunk.clear();
3440
3441        // … we see `Clear`. All good.
3442        assert_eq!(linked_chunk.updates().unwrap().take(), &[Clear]);
3443    }
3444
3445    #[test]
3446    fn test_clear_emits_an_update_clear_and_forget_about_pending_updates() {
3447        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3448
3449        // Let's push an item in it.
3450        linked_chunk.push_items_back(['a']);
3451
3452        // When clearing…
3453        linked_chunk.clear();
3454
3455        // … we see only `Clear` without `NewItemsChunk`!
3456        assert_eq!(linked_chunk.updates().unwrap().take(), &[Clear]);
3457    }
3458
3459    #[test]
3460    fn test_clear_emits_no_new_items_chunk_if_already_clear() {
3461        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3462
3463        // When clearing an already clear linked chunk…
3464        linked_chunk.clear();
3465
3466        // … we see only `Clear` without `NewItemsChunk`, i.e. the first chunk
3467        // is NOT created lazily!
3468        assert_eq!(linked_chunk.updates().unwrap().take(), &[Clear]);
3469    }
3470
3471    #[test]
3472    fn test_replace_item() {
3473        let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3474
3475        linked_chunk.push_items_back(['a', 'b', 'c']);
3476        linked_chunk.push_gap_back(());
3477        // Sanity check.
3478        assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3479
3480        // Drain previous updates.
3481        let _ = linked_chunk.updates().unwrap().take();
3482
3483        // Replace item in bounds.
3484        linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3485        assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3486
3487        assert_eq!(
3488            linked_chunk.updates().unwrap().take(),
3489            &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3490        );
3491
3492        // Attempt to replace out-of-bounds.
3493        assert_matches!(
3494            linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3495            Err(Error::InvalidItemIndex { index: 3 })
3496        );
3497
3498        // Attempt to replace gap.
3499        assert_matches!(
3500            linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3501            Err(Error::ChunkIsAGap { .. })
3502        );
3503    }
3504
3505    #[test]
3506    fn test_lazy_previous() {
3507        use std::marker::PhantomData;
3508
3509        use super::{Ends, ObservableUpdates};
3510
3511        // Imagine the linked chunk is lazily loaded.
3512        let first_chunk_identifier = ChunkIdentifier(0);
3513        let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3514        unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3515
3516        let updates = Some(ObservableUpdates::new());
3517
3518        let mut linked_chunk = LinkedChunk::<3, char, ()> {
3519            links: Ends::new_with_first_chunk(first_loaded_chunk, &updates),
3520            chunk_identifier_generator:
3521                ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3522            updates,
3523            marker: PhantomData,
3524        };
3525
3526        // Insert items in the first loaded chunk (chunk 1), with an overflow to
3527        // a new chunk.
3528        {
3529            linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3530
3531            assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3532
3533            // Assert where `lazy_previous` is set.
3534            {
3535                let mut chunks = linked_chunk.chunks();
3536
3537                assert_matches!(chunks.next(), Some(chunk) => {
3538                    assert_eq!(chunk.identifier(), 1);
3539                    assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3540                });
3541                assert_matches!(chunks.next(), Some(chunk) => {
3542                    assert_eq!(chunk.identifier(), 2);
3543                    assert!(chunk.lazy_previous.is_none());
3544                });
3545                assert!(chunks.next().is_none());
3546            }
3547
3548            // In the updates, we observe nothing else than the usual bits.
3549            assert_eq!(
3550                linked_chunk.updates().unwrap().take(),
3551                &[
3552                    PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3553                    NewItemsChunk {
3554                        previous: Some(ChunkIdentifier(1)),
3555                        new: ChunkIdentifier(2),
3556                        next: None,
3557                    },
3558                    PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3559                ]
3560            );
3561        }
3562
3563        // Now insert a gap at the head of the loaded linked chunk.
3564        {
3565            linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3566
3567            assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3568
3569            // Assert where `lazy_previous` is set.
3570            {
3571                let mut chunks = linked_chunk.chunks();
3572
3573                assert_matches!(chunks.next(), Some(chunk) => {
3574                    assert_eq!(chunk.identifier(), 3);
3575                    // `lazy_previous` has moved here!
3576                    assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3577                });
3578                assert_matches!(chunks.next(), Some(chunk) => {
3579                    assert_eq!(chunk.identifier(), 1);
3580                    // `lazy_previous` has moved from here.
3581                    assert!(chunk.lazy_previous.is_none());
3582                });
3583                assert_matches!(chunks.next(), Some(chunk) => {
3584                    assert_eq!(chunk.identifier(), 2);
3585                    assert!(chunk.lazy_previous.is_none());
3586                });
3587                assert!(chunks.next().is_none());
3588            }
3589
3590            // In the updates, we observe that the new gap **has** a previous
3591            // chunk!
3592            assert_eq!(
3593                linked_chunk.updates().unwrap().take(),
3594                &[NewGapChunk {
3595                    // 0 is the lazy, not-loaded-yet chunk.
3596                    previous: Some(ChunkIdentifier(0)),
3597                    new: ChunkIdentifier(3),
3598                    next: Some(ChunkIdentifier(1)),
3599                    gap: ()
3600                }]
3601            );
3602        }
3603
3604        // Next, replace the gap by items to see how it reacts to unlink.
3605        {
3606            linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3607
3608            assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3609
3610            // Assert where `lazy_previous` is set.
3611            {
3612                let mut chunks = linked_chunk.chunks();
3613
3614                assert_matches!(chunks.next(), Some(chunk) => {
3615                    assert_eq!(chunk.identifier(), 4);
3616                    // `lazy_previous` has moved here!
3617                    assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3618                });
3619                assert_matches!(chunks.next(), Some(chunk) => {
3620                    assert_eq!(chunk.identifier(), 5);
3621                    assert!(chunk.lazy_previous.is_none());
3622                });
3623                assert_matches!(chunks.next(), Some(chunk) => {
3624                    assert_eq!(chunk.identifier(), 1);
3625                    assert!(chunk.lazy_previous.is_none());
3626                });
3627                assert_matches!(chunks.next(), Some(chunk) => {
3628                    assert_eq!(chunk.identifier(), 2);
3629                    assert!(chunk.lazy_previous.is_none());
3630                });
3631                assert!(chunks.next().is_none());
3632            }
3633
3634            // In the updates, we observe nothing than the usual bits.
3635            assert_eq!(
3636                linked_chunk.updates().unwrap().take(),
3637                &[
3638                    // The new chunk is inserted…
3639                    NewItemsChunk {
3640                        previous: Some(ChunkIdentifier(3)),
3641                        new: ChunkIdentifier(4),
3642                        next: Some(ChunkIdentifier(1)),
3643                    },
3644                    // … and new items are pushed in it.
3645                    PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3646                    // Another new chunk is inserted…
3647                    NewItemsChunk {
3648                        previous: Some(ChunkIdentifier(4)),
3649                        new: ChunkIdentifier(5),
3650                        next: Some(ChunkIdentifier(1)),
3651                    },
3652                    // … and new items are pushed in it.
3653                    PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3654                    // Finally, the gap is removed!
3655                    RemoveChunk(ChunkIdentifier(3)),
3656                ]
3657            );
3658        }
3659
3660        // Finally, let's re-insert a gap to ensure the lazy-previous is set
3661        // correctly. It is similar to the beginning of this test, but this is a
3662        // frequent pattern in how the linked chunk is used.
3663        {
3664            linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3665
3666            assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3667
3668            // Assert where `lazy_previous` is set.
3669            {
3670                let mut chunks = linked_chunk.chunks();
3671
3672                assert_matches!(chunks.next(), Some(chunk) => {
3673                    assert_eq!(chunk.identifier(), 6);
3674                    // `lazy_previous` has moved here!
3675                    assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3676                });
3677                assert_matches!(chunks.next(), Some(chunk) => {
3678                    assert_eq!(chunk.identifier(), 4);
3679                    // `lazy_previous` has moved from here.
3680                    assert!(chunk.lazy_previous.is_none());
3681                });
3682                assert_matches!(chunks.next(), Some(chunk) => {
3683                    assert_eq!(chunk.identifier(), 5);
3684                    assert!(chunk.lazy_previous.is_none());
3685                });
3686                assert_matches!(chunks.next(), Some(chunk) => {
3687                    assert_eq!(chunk.identifier(), 1);
3688                    assert!(chunk.lazy_previous.is_none());
3689                });
3690                assert_matches!(chunks.next(), Some(chunk) => {
3691                    assert_eq!(chunk.identifier(), 2);
3692                    assert!(chunk.lazy_previous.is_none());
3693                });
3694                assert!(chunks.next().is_none());
3695            }
3696
3697            // In the updates, we observe that the new gap **has** a previous
3698            // chunk!
3699            assert_eq!(
3700                linked_chunk.updates().unwrap().take(),
3701                &[NewGapChunk {
3702                    // 0 is the lazy, not-loaded-yet chunk.
3703                    previous: Some(ChunkIdentifier(0)),
3704                    new: ChunkIdentifier(6),
3705                    next: Some(ChunkIdentifier(4)),
3706                    gap: ()
3707                }]
3708            );
3709        }
3710    }
3711}