Skip to main content

matrix_sdk_common/linked_chunk/
mod.rs

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