Skip to main content

matrix_sdk_common/linked_chunk/
mod.rs

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