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