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