matrix_sdk_common/linked_chunk/
mod.rs

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