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