matrix_sdk_common/linked_chunk/
mod.rs

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