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