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