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