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