matrix_sdk_common/linked_chunk/
mod.rs

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