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