1#![allow(rustdoc::private_intra_doc_links)]
16
17#[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;
96mod order_tracker;
97pub mod relational;
98mod updates;
99
100use std::{
101 fmt::{self, Display},
102 marker::PhantomData,
103 ptr::NonNull,
104 sync::atomic::{self, AtomicU64},
105};
106
107pub use as_vector::*;
108pub use order_tracker::OrderTracker;
109use ruma::{EventId, OwnedEventId, OwnedRoomId, RoomId};
110use serde::{Deserialize, Serialize};
111pub use updates::*;
112
113#[derive(Debug, Clone, Copy, PartialEq)]
115pub enum LinkedChunkId<'a> {
116 Room(&'a RoomId),
118 Thread(&'a RoomId, &'a EventId),
120 PinnedEvents(&'a RoomId),
122 EventFocused(&'a RoomId, &'a EventId),
124}
125
126impl Display for LinkedChunkId<'_> {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 match self {
129 Self::Room(room_id) => write!(f, "{room_id}"),
130 Self::Thread(room_id, thread_root) => {
131 write!(f, "{room_id}:thread:{thread_root}")
132 }
133 Self::PinnedEvents(room_id) => {
134 write!(f, "{room_id}:pinned")
135 }
136 Self::EventFocused(room_id, event_id) => {
137 write!(f, "{room_id}:event_focused:{event_id}")
138 }
139 }
140 }
141}
142
143impl LinkedChunkId<'_> {
144 pub fn storage_key(&self) -> impl '_ + AsRef<[u8]> {
145 match self {
146 LinkedChunkId::Room(room_id) => room_id.to_string(),
147 LinkedChunkId::Thread(room_id, event_id) => format!("t:{room_id}:{event_id}"),
148 LinkedChunkId::PinnedEvents(room_id) => format!("pinned:{room_id}"),
149 LinkedChunkId::EventFocused(room_id, event_id) => {
150 format!("event_focused:{room_id}:{event_id}")
151 }
152 }
153 }
154
155 pub fn to_owned(&self) -> OwnedLinkedChunkId {
156 match self {
157 LinkedChunkId::Room(room_id) => OwnedLinkedChunkId::Room((*room_id).to_owned()),
158 LinkedChunkId::Thread(room_id, event_id) => {
159 OwnedLinkedChunkId::Thread((*room_id).to_owned(), (*event_id).to_owned())
160 }
161 LinkedChunkId::PinnedEvents(room_id) => {
162 OwnedLinkedChunkId::PinnedEvents((*room_id).to_owned())
163 }
164 LinkedChunkId::EventFocused(room_id, event_id) => {
165 OwnedLinkedChunkId::EventFocused((*room_id).to_owned(), (*event_id).to_owned())
166 }
167 }
168 }
169}
170
171impl<'a> From<&'a OwnedLinkedChunkId> for LinkedChunkId<'a> {
172 fn from(value: &'a OwnedLinkedChunkId) -> Self {
173 value.as_ref()
174 }
175}
176
177impl PartialEq<&OwnedLinkedChunkId> for LinkedChunkId<'_> {
178 fn eq(&self, other: &&OwnedLinkedChunkId) -> bool {
179 match (self, other) {
180 (LinkedChunkId::Room(a), OwnedLinkedChunkId::Room(b)) => *a == b,
181 (LinkedChunkId::PinnedEvents(a), OwnedLinkedChunkId::PinnedEvents(b)) => *a == b,
182 (LinkedChunkId::Thread(r, ev), OwnedLinkedChunkId::Thread(r2, ev2)) => {
183 r == r2 && ev == ev2
184 }
185 (LinkedChunkId::EventFocused(r, ev), OwnedLinkedChunkId::EventFocused(r2, ev2)) => {
186 r == r2 && ev == ev2
187 }
188 _ => false,
189 }
190 }
191}
192
193impl PartialEq<LinkedChunkId<'_>> for OwnedLinkedChunkId {
194 fn eq(&self, other: &LinkedChunkId<'_>) -> bool {
195 other.eq(&self)
196 }
197}
198
199#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
201pub enum OwnedLinkedChunkId {
202 Room(OwnedRoomId),
203 Thread(OwnedRoomId, OwnedEventId),
204 PinnedEvents(OwnedRoomId),
205 EventFocused(OwnedRoomId, OwnedEventId),
206}
207
208impl Display for OwnedLinkedChunkId {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 self.as_ref().fmt(f)
211 }
212}
213
214impl OwnedLinkedChunkId {
215 pub fn as_ref(&self) -> LinkedChunkId<'_> {
216 match self {
217 OwnedLinkedChunkId::Room(room_id) => LinkedChunkId::Room(room_id.as_ref()),
218 OwnedLinkedChunkId::Thread(room_id, event_id) => {
219 LinkedChunkId::Thread(room_id.as_ref(), event_id.as_ref())
220 }
221 OwnedLinkedChunkId::PinnedEvents(room_id) => {
222 LinkedChunkId::PinnedEvents(room_id.as_ref())
223 }
224 OwnedLinkedChunkId::EventFocused(room_id, event_id) => {
225 LinkedChunkId::EventFocused(room_id.as_ref(), event_id.as_ref())
226 }
227 }
228 }
229
230 pub fn room_id(&self) -> &RoomId {
231 match self {
232 OwnedLinkedChunkId::Room(room_id)
233 | OwnedLinkedChunkId::Thread(room_id, ..)
234 | OwnedLinkedChunkId::PinnedEvents(room_id, ..)
235 | OwnedLinkedChunkId::EventFocused(room_id, ..) => room_id,
236 }
237 }
238}
239
240impl From<LinkedChunkId<'_>> for OwnedLinkedChunkId {
241 fn from(value: LinkedChunkId<'_>) -> Self {
242 value.to_owned()
243 }
244}
245
246#[derive(thiserror::Error, Debug)]
248pub enum Error {
249 #[error("The chunk identifier is invalid: `{identifier:?}`")]
251 InvalidChunkIdentifier {
252 identifier: ChunkIdentifier,
254 },
255
256 #[error("The chunk is a gap: `{identifier:?}`")]
258 ChunkIsAGap {
259 identifier: ChunkIdentifier,
261 },
262
263 #[error("The chunk is an item: `{identifier:?}`")]
265 ChunkIsItems {
266 identifier: ChunkIdentifier,
268 },
269
270 #[error("The chunk is a non-empty item chunk: `{identifier:?}`")]
272 RemovingNonEmptyItemsChunk {
273 identifier: ChunkIdentifier,
275 },
276
277 #[error("Trying to remove the only chunk, but a linked chunk can't be empty")]
280 RemovingLastChunk,
281
282 #[error("The item index is invalid: `{index}`")]
284 InvalidItemIndex {
285 index: usize,
287 },
288}
289
290struct Ends<const CHUNK_CAPACITY: usize, Item, Gap> {
295 first: NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>,
297 last: Option<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
299}
300
301impl<const CAP: usize, Item, Gap> Ends<CAP, Item, Gap> {
302 fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
304 unsafe { self.first.as_ref() }
305 }
306
307 fn first_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
309 unsafe { self.first.as_mut() }
310 }
311
312 fn latest_chunk(&self) -> &Chunk<CAP, Item, Gap> {
314 unsafe { self.last.unwrap_or(self.first).as_ref() }
315 }
316
317 fn latest_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
319 unsafe { self.last.as_mut().unwrap_or(&mut self.first).as_mut() }
320 }
321
322 fn chunk(&self, identifier: ChunkIdentifier) -> Option<&Chunk<CAP, Item, Gap>> {
324 let mut chunk = self.latest_chunk();
325
326 loop {
327 if chunk.identifier() == identifier {
328 return Some(chunk);
329 }
330
331 chunk = chunk.previous()?;
332 }
333 }
334
335 fn chunk_mut(&mut self, identifier: ChunkIdentifier) -> Option<&mut Chunk<CAP, Item, Gap>> {
337 let mut chunk = self.latest_chunk_mut();
338
339 loop {
340 if chunk.identifier() == identifier {
341 return Some(chunk);
342 }
343
344 chunk = chunk.previous_mut()?;
345 }
346 }
347
348 unsafe fn clear(&mut self) {
355 let mut current_chunk_ptr = self.last.or(Some(self.first));
358
359 while let Some(chunk_ptr) = current_chunk_ptr {
361 let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
363
364 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
366
367 current_chunk_ptr = previous_ptr;
369 }
370
371 self.first = NonNull::dangling();
373 self.last = None;
374 }
375
376 fn replace_with(&mut self, first_chunk: NonNull<Chunk<CAP, Item, Gap>>) {
379 unsafe {
381 self.clear();
382 }
383
384 self.first = first_chunk;
386 }
387
388 fn reset(&mut self) {
393 self.replace_with(Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER));
394 }
395}
396
397pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
404 links: Ends<CHUNK_CAPACITY, Item, Gap>,
406
407 chunk_identifier_generator: ChunkIdentifierGenerator,
409
410 updates: Option<ObservableUpdates<Item, Gap>>,
414
415 marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
417}
418
419impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
420 fn default() -> Self {
421 Self::new()
422 }
423}
424
425impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
426 pub fn new() -> Self {
428 Self {
429 links: Ends {
430 first: Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER),
431 last: None,
432 },
433 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
434 updates: None,
435 marker: PhantomData,
436 }
437 }
438
439 pub fn new_with_update_history() -> Self {
445 let first_chunk_identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
446
447 let mut updates = ObservableUpdates::new();
448 updates.push(Update::NewItemsChunk {
449 previous: None,
450 new: first_chunk_identifier,
451 next: None,
452 });
453
454 Self {
455 links: Ends { first: Chunk::new_items_leaked(first_chunk_identifier), last: None },
456 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
457 updates: Some(updates),
458 marker: PhantomData,
459 }
460 }
461
462 pub fn clear(&mut self) {
464 self.links.reset();
466
467 self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
469
470 if let Some(updates) = self.updates.as_mut() {
472 updates.clear_pending();
475 updates.push(Update::Clear);
476 updates.push(Update::NewItemsChunk {
477 previous: None,
478 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
479 next: None,
480 })
481 }
482 }
483
484 pub fn push_items_back<I>(&mut self, items: I)
490 where
491 Item: Clone,
492 Gap: Clone,
493 I: IntoIterator<Item = Item>,
494 I::IntoIter: ExactSizeIterator,
495 {
496 let items = items.into_iter();
497
498 let last_chunk = self.links.latest_chunk_mut();
499
500 let last_chunk =
502 last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
503
504 debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
505
506 if !last_chunk.is_first_chunk() {
510 self.links.last = Some(last_chunk.as_ptr());
513 }
514 }
515
516 pub fn push_gap_back(&mut self, content: Gap)
519 where
520 Item: Clone,
521 Gap: Clone,
522 {
523 let last_chunk = self.links.latest_chunk_mut();
524 last_chunk.insert_next(
525 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
526 &mut self.updates,
527 );
528
529 self.links.last = last_chunk.next;
530 }
531
532 pub fn insert_items_at<I>(&mut self, position: Position, items: I) -> Result<(), Error>
537 where
538 Item: Clone,
539 Gap: Clone,
540 I: IntoIterator<Item = Item>,
541 I::IntoIter: ExactSizeIterator,
542 {
543 let chunk_identifier = position.chunk_identifier();
544 let item_index = position.index();
545
546 let chunk = self
547 .links
548 .chunk_mut(chunk_identifier)
549 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
550
551 let chunk = match &mut chunk.content {
552 ChunkContent::Gap(..) => {
553 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
554 }
555
556 ChunkContent::Items(current_items) => {
557 let current_items_length = current_items.len();
558
559 if item_index > current_items_length {
560 return Err(Error::InvalidItemIndex { index: item_index });
561 }
562
563 let items = items.into_iter();
565
566 if item_index == current_items_length {
568 chunk
569 .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
571 }
572 else {
574 if let Some(updates) = self.updates.as_mut() {
575 updates.push(Update::DetachLastItems {
576 at: Position(chunk_identifier, item_index),
577 });
578 }
579
580 let detached_items = current_items.split_off(item_index);
582
583 let chunk = chunk
584 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
586
587 if let Some(updates) = self.updates.as_mut() {
588 updates.push(Update::StartReattachItems);
589 }
590
591 let chunk = chunk
592 .push_items(
594 detached_items.into_iter(),
595 &self.chunk_identifier_generator,
596 &mut self.updates,
597 );
598
599 if let Some(updates) = self.updates.as_mut() {
600 updates.push(Update::EndReattachItems);
601 }
602
603 chunk
604 }
605 }
606 };
607
608 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
611 self.links.last = Some(chunk.as_ptr());
614 }
615
616 Ok(())
617 }
618
619 pub fn remove_item_at(&mut self, position: Position) -> Result<Item, Error> {
627 let chunk_identifier = position.chunk_identifier();
628 let item_index = position.index();
629
630 let mut chunk_ptr = None;
631 let removed_item;
632
633 {
634 let chunk = self
635 .links
636 .chunk_mut(chunk_identifier)
637 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
638
639 let current_items = match &mut chunk.content {
640 ChunkContent::Gap(..) => {
641 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
642 }
643 ChunkContent::Items(current_items) => current_items,
644 };
645
646 if item_index >= current_items.len() {
647 return Err(Error::InvalidItemIndex { index: item_index });
648 }
649
650 removed_item = current_items.remove(item_index);
651
652 if let Some(updates) = self.updates.as_mut() {
653 updates.push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
654 }
655
656 if current_items.is_empty() && !chunk.is_first_chunk() {
658 chunk.unlink(self.updates.as_mut());
660
661 chunk_ptr = Some(chunk.as_ptr());
662
663 if chunk.is_last_chunk() {
666 self.links.last = chunk.previous;
667 }
668 }
669
670 }
672
673 if let Some(chunk_ptr) = chunk_ptr {
674 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
681 }
682
683 Ok(removed_item)
684 }
685
686 pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
691 where
692 Item: Clone,
693 {
694 let chunk_identifier = position.chunk_identifier();
695 let item_index = position.index();
696
697 let chunk = self
698 .links
699 .chunk_mut(chunk_identifier)
700 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
701
702 match &mut chunk.content {
703 ChunkContent::Gap(..) => {
704 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
705 }
706
707 ChunkContent::Items(current_items) => {
708 if item_index >= current_items.len() {
709 return Err(Error::InvalidItemIndex { index: item_index });
710 }
711
712 if let Some(updates) = self.updates.as_mut() {
714 updates.push(Update::ReplaceItem {
715 at: Position(chunk_identifier, item_index),
716 item: item.clone(),
717 });
718 }
719
720 current_items[item_index] = item;
721 }
722 }
723
724 Ok(())
725 }
726
727 pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
732 where
733 Item: Clone,
734 Gap: Clone,
735 {
736 let chunk_identifier = position.chunk_identifier();
737 let item_index = position.index();
738
739 let chunk = self
740 .links
741 .chunk_mut(chunk_identifier)
742 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
743
744 let chunk = match &mut chunk.content {
745 ChunkContent::Gap(..) => {
746 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
747 }
748
749 ChunkContent::Items(current_items) => {
750 if item_index == 0 {
754 let chunk_was_first = chunk.is_first_chunk();
755 let chunk_was_last = chunk.is_last_chunk();
756
757 let new_chunk = chunk.insert_before(
758 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
759 self.updates.as_mut(),
760 );
761
762 let new_chunk_ptr = new_chunk.as_ptr();
763 let chunk_ptr = chunk.as_ptr();
764
765 if chunk_was_first {
770 self.links.first = new_chunk_ptr;
771
772 if chunk_was_last {
774 self.links.last = Some(chunk_ptr);
775 }
776 }
777
778 return Ok(());
779 }
780
781 let current_items_length = current_items.len();
782
783 if item_index >= current_items_length {
784 return Err(Error::InvalidItemIndex { index: item_index });
785 }
786
787 if let Some(updates) = self.updates.as_mut() {
788 updates.push(Update::DetachLastItems {
789 at: Position(chunk_identifier, item_index),
790 });
791 }
792
793 let detached_items = current_items.split_off(item_index);
795
796 let chunk = chunk
797 .insert_next(
799 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
800 &mut self.updates,
801 );
802
803 if let Some(updates) = self.updates.as_mut() {
804 updates.push(Update::StartReattachItems);
805 }
806
807 let chunk = chunk
808 .insert_next(
810 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
811 &mut self.updates,
812 )
813 .push_items(
815 detached_items.into_iter(),
816 &self.chunk_identifier_generator,
817 &mut self.updates,
818 );
819
820 if let Some(updates) = self.updates.as_mut() {
821 updates.push(Update::EndReattachItems);
822 }
823
824 chunk
825 }
826 };
827
828 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
831 self.links.last = Some(chunk.as_ptr());
834 }
835
836 Ok(())
837 }
838
839 pub fn remove_empty_chunk_at(
848 &mut self,
849 chunk_identifier: ChunkIdentifier,
850 ) -> Result<Option<Position>, Error> {
851 if self.links.first_chunk().is_last_chunk() {
853 return Err(Error::RemovingLastChunk);
854 }
855
856 let chunk = self
857 .links
858 .chunk_mut(chunk_identifier)
859 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
860
861 if chunk.num_items() > 0 {
862 return Err(Error::RemovingNonEmptyItemsChunk { identifier: chunk_identifier });
863 }
864
865 let chunk_was_first = chunk.is_first_chunk();
866 let chunk_was_last = chunk.is_last_chunk();
867 let next_ptr = chunk.next;
868 let previous_ptr = chunk.previous;
869 let position_of_next = chunk.next().map(|next| next.first_position());
870
871 chunk.unlink(self.updates.as_mut());
872
873 let chunk_ptr = chunk.as_ptr();
874
875 if chunk_was_first {
877 if let Some(next_ptr) = next_ptr {
879 self.links.first = next_ptr;
880 }
881 }
882
883 if chunk_was_last {
884 self.links.last = previous_ptr;
885 }
886
887 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
890
891 Ok(position_of_next)
893 }
894
895 pub fn replace_gap_at<I>(
903 &mut self,
904 items: I,
905 chunk_identifier: ChunkIdentifier,
906 ) -> Result<&Chunk<CAP, Item, Gap>, Error>
907 where
908 Item: Clone,
909 Gap: Clone,
910 I: IntoIterator<Item = Item>,
911 I::IntoIter: ExactSizeIterator,
912 {
913 let chunk_ptr;
914 let new_chunk_ptr;
915
916 {
917 let chunk = self
918 .links
919 .chunk_mut(chunk_identifier)
920 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
921
922 if chunk.is_items() {
923 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
924 }
925
926 let chunk_was_first = chunk.is_first_chunk();
927
928 let maybe_last_chunk_ptr = {
929 let items = items.into_iter();
930
931 let last_inserted_chunk = chunk
932 .insert_next(
934 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
935 &mut self.updates,
936 )
937 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
939
940 last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
941 };
942
943 new_chunk_ptr = chunk
944 .next
945 .unwrap();
947
948 chunk.unlink(self.updates.as_mut());
950
951 chunk_ptr = chunk.as_ptr();
953
954 if chunk_was_first {
956 self.links.first = new_chunk_ptr;
957 }
958
959 if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
962 self.links.last = Some(last_chunk_ptr);
963 }
964
965 }
967
968 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
973
974 Ok(
975 unsafe { new_chunk_ptr.as_ref() },
979 )
980 }
981
982 pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
984 where
985 P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
986 {
987 self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
988 }
989
990 pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
992 where
993 P: FnMut(&'a Item) -> bool,
994 {
995 self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
996 }
997
998 pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
1002 IterBackward::new(self.links.latest_chunk())
1003 }
1004
1005 pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
1009 Iter::new(self.links.first_chunk())
1010 }
1011
1012 pub fn rchunks_from(
1017 &self,
1018 identifier: ChunkIdentifier,
1019 ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
1020 Ok(IterBackward::new(
1021 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
1022 ))
1023 }
1024
1025 pub fn chunks_from(
1030 &self,
1031 identifier: ChunkIdentifier,
1032 ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
1033 Ok(Iter::new(
1034 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
1035 ))
1036 }
1037
1038 pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
1042 self.ritems_from(self.links.latest_chunk().last_position())
1043 .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
1044 }
1045
1046 pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
1050 let first_chunk = self.links.first_chunk();
1051
1052 self.items_from(first_chunk.first_position())
1053 .expect("`items` cannot fail because at least one empty chunk must exist")
1054 }
1055
1056 pub fn ritems_from(
1060 &self,
1061 position: Position,
1062 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1063 Ok(self
1064 .rchunks_from(position.chunk_identifier())?
1065 .filter_map(|chunk| match &chunk.content {
1066 ChunkContent::Gap(..) => None,
1067 ChunkContent::Items(items) => {
1068 let identifier = chunk.identifier();
1069
1070 Some(
1071 items.iter().enumerate().rev().map(move |(item_index, item)| {
1072 (Position(identifier, item_index), item)
1073 }),
1074 )
1075 }
1076 })
1077 .flatten()
1078 .skip_while({
1079 let expected_index = position.index();
1080
1081 move |(Position(chunk_identifier, item_index), _item)| {
1082 *chunk_identifier == position.chunk_identifier()
1083 && *item_index != expected_index
1084 }
1085 }))
1086 }
1087
1088 pub fn items_from(
1092 &self,
1093 position: Position,
1094 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1095 Ok(self
1096 .chunks_from(position.chunk_identifier())?
1097 .filter_map(|chunk| match &chunk.content {
1098 ChunkContent::Gap(..) => None,
1099 ChunkContent::Items(items) => {
1100 let identifier = chunk.identifier();
1101
1102 Some(
1103 items.iter().enumerate().map(move |(item_index, item)| {
1104 (Position(identifier, item_index), item)
1105 }),
1106 )
1107 }
1108 })
1109 .flatten()
1110 .skip(position.index()))
1111 }
1112
1113 #[must_use]
1125 pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
1126 self.updates.as_mut()
1127 }
1128
1129 pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
1136 let (updates, token) = self
1137 .updates
1138 .as_mut()
1139 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1140 let chunk_iterator = self.chunks();
1141
1142 Some(AsVector::new(updates, token, chunk_iterator))
1143 }
1144
1145 pub fn order_tracker(
1153 &mut self,
1154 all_chunks: Option<Vec<ChunkMetadata>>,
1155 ) -> Option<OrderTracker<Item, Gap>>
1156 where
1157 Item: Clone,
1158 {
1159 let (updates, token) = self
1160 .updates
1161 .as_mut()
1162 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1163
1164 Some(OrderTracker::new(
1165 updates,
1166 token,
1167 all_chunks.unwrap_or_else(|| {
1168 self.chunks()
1170 .map(|chunk| ChunkMetadata {
1171 identifier: chunk.identifier(),
1172 num_items: chunk.num_items(),
1173 previous: chunk.previous().map(|prev| prev.identifier()),
1174 next: chunk.next().map(|next| next.identifier()),
1175 })
1176 .collect()
1177 }),
1178 ))
1179 }
1180
1181 pub fn num_items(&self) -> usize {
1183 self.items().count()
1184 }
1185}
1186
1187impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
1188 fn drop(&mut self) {
1189 unsafe {
1199 self.links.clear();
1200 }
1201 }
1202}
1203
1204unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1208
1209unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1213
1214#[derive(Debug)]
1224pub struct ChunkIdentifierGenerator {
1225 next: AtomicU64,
1226}
1227
1228impl ChunkIdentifierGenerator {
1229 const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1231
1232 pub fn new_from_scratch() -> Self {
1235 Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1236 }
1237
1238 pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1241 Self { next: AtomicU64::new(last_chunk_identifier.0) }
1242 }
1243
1244 fn next(&self) -> ChunkIdentifier {
1249 let previous = self.next.fetch_add(1, atomic::Ordering::Relaxed);
1250
1251 if previous == u64::MAX {
1254 panic!(
1255 "No more chunk identifiers available. Congrats, you did it. \
1256 2^64 identifiers have been consumed."
1257 )
1258 }
1259
1260 ChunkIdentifier(previous + 1)
1261 }
1262
1263 #[doc(hidden)]
1267 pub fn current(&self) -> ChunkIdentifier {
1268 ChunkIdentifier(self.next.load(atomic::Ordering::Relaxed))
1269 }
1270}
1271
1272#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1278#[repr(transparent)]
1279pub struct ChunkIdentifier(u64);
1280
1281impl ChunkIdentifier {
1282 pub fn new(identifier: u64) -> Self {
1284 Self(identifier)
1285 }
1286
1287 pub fn index(&self) -> u64 {
1289 self.0
1290 }
1291}
1292
1293impl PartialEq<u64> for ChunkIdentifier {
1294 fn eq(&self, other: &u64) -> bool {
1295 self.0 == *other
1296 }
1297}
1298
1299#[derive(Copy, Clone, Debug, PartialEq)]
1303pub struct Position(ChunkIdentifier, usize);
1304
1305impl Position {
1306 pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1308 Self(chunk_identifier, index)
1309 }
1310
1311 pub fn chunk_identifier(&self) -> ChunkIdentifier {
1313 self.0
1314 }
1315
1316 pub fn index(&self) -> usize {
1318 self.1
1319 }
1320
1321 pub fn decrement_index(&mut self) {
1327 self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1328 }
1329
1330 pub fn increment_index(&mut self) {
1337 self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1338 }
1339}
1340
1341#[derive(Debug)]
1344pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1345 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1346}
1347
1348impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1349 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1351 Self { chunk: Some(from_chunk) }
1352 }
1353}
1354
1355impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1356 type Item = &'a Chunk<CAP, Item, Gap>;
1357
1358 fn next(&mut self) -> Option<Self::Item> {
1359 self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1360 }
1361}
1362
1363#[derive(Debug)]
1366pub struct Iter<'a, const CAP: usize, Item, Gap> {
1367 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1368}
1369
1370impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1371 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1373 Self { chunk: Some(from_chunk) }
1374 }
1375}
1376
1377impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1378 type Item = &'a Chunk<CAP, Item, Gap>;
1379
1380 fn next(&mut self) -> Option<Self::Item> {
1381 self.chunk.inspect(|chunk| self.chunk = chunk.next())
1382 }
1383}
1384
1385#[derive(Clone, Debug)]
1387pub enum ChunkContent<Item, Gap> {
1388 Gap(Gap),
1391
1392 Items(Vec<Item>),
1394}
1395
1396pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1398 previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1400
1401 lazy_previous: Option<ChunkIdentifier>,
1406
1407 next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1409
1410 identifier: ChunkIdentifier,
1412
1413 content: ChunkContent<Item, Gap>,
1415}
1416
1417impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1418 fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1420 Self::new(identifier, ChunkContent::Gap(content))
1421 }
1422
1423 fn new_items(identifier: ChunkIdentifier) -> Self {
1425 Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1426 }
1427
1428 fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1429 Self { previous: None, lazy_previous: None, next: None, identifier, content }
1430 }
1431
1432 fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1434 let chunk = Self::new(identifier, content);
1435 let chunk_box = Box::new(chunk);
1436
1437 NonNull::from(Box::leak(chunk_box))
1438 }
1439
1440 fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1442 let chunk = Self::new_gap(identifier, content);
1443 let chunk_box = Box::new(chunk);
1444
1445 NonNull::from(Box::leak(chunk_box))
1446 }
1447
1448 fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1450 let chunk = Self::new_items(identifier);
1451 let chunk_box = Box::new(chunk);
1452
1453 NonNull::from(Box::leak(chunk_box))
1454 }
1455
1456 pub fn as_ptr(&self) -> NonNull<Self> {
1458 NonNull::from(self)
1459 }
1460
1461 pub fn is_gap(&self) -> bool {
1463 matches!(self.content, ChunkContent::Gap(..))
1464 }
1465
1466 pub fn is_items(&self) -> bool {
1468 !self.is_gap()
1469 }
1470
1471 pub fn is_definitive_head(&self) -> bool {
1474 self.previous.is_none() && self.lazy_previous.is_none()
1475 }
1476
1477 fn is_first_chunk(&self) -> bool {
1479 self.previous.is_none()
1480 }
1481
1482 fn is_last_chunk(&self) -> bool {
1484 self.next.is_none()
1485 }
1486
1487 #[doc(hidden)]
1491 pub fn lazy_previous(&self) -> Option<ChunkIdentifier> {
1492 self.lazy_previous
1493 }
1494
1495 pub fn identifier(&self) -> ChunkIdentifier {
1497 self.identifier
1498 }
1499
1500 pub fn content(&self) -> &ChunkContent<Item, Gap> {
1502 &self.content
1503 }
1504
1505 pub fn first_position(&self) -> Position {
1509 Position(self.identifier(), 0)
1510 }
1511
1512 pub fn last_position(&self) -> Position {
1516 let identifier = self.identifier();
1517
1518 match &self.content {
1519 ChunkContent::Gap(..) => Position(identifier, 0),
1520 ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1521 }
1522 }
1523
1524 pub fn num_items(&self) -> usize {
1528 match &self.content {
1529 ChunkContent::Gap(..) => 0,
1530 ChunkContent::Items(items) => items.len(),
1531 }
1532 }
1533
1534 fn push_items<I>(
1546 &mut self,
1547 mut new_items: I,
1548 chunk_identifier_generator: &ChunkIdentifierGenerator,
1549 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1550 ) -> &mut Self
1551 where
1552 I: Iterator<Item = Item> + ExactSizeIterator,
1553 Item: Clone,
1554 Gap: Clone,
1555 {
1556 if new_items.len() == 0 {
1558 return self;
1559 }
1560
1561 let identifier = self.identifier();
1562 let prev_num_items = self.num_items();
1563
1564 match &mut self.content {
1565 ChunkContent::Gap(..) => {
1568 self
1569 .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1571 .push_items(new_items, chunk_identifier_generator, updates)
1574 }
1575
1576 ChunkContent::Items(items) => {
1577 let free_space = CAPACITY.saturating_sub(prev_num_items);
1579
1580 if new_items.len() <= free_space {
1582 let start = items.len();
1583 items.extend(new_items);
1584
1585 if let Some(updates) = updates.as_mut() {
1586 updates.push(Update::PushItems {
1587 at: Position(identifier, start),
1588 items: items[start..].to_vec(),
1589 });
1590 }
1591
1592 self
1594 } else {
1595 if free_space > 0 {
1596 let start = items.len();
1598 items.extend(new_items.by_ref().take(free_space));
1599
1600 if let Some(updates) = updates.as_mut() {
1601 updates.push(Update::PushItems {
1602 at: Position(identifier, start),
1603 items: items[start..].to_vec(),
1604 });
1605 }
1606 }
1607
1608 self
1609 .insert_next(
1611 Self::new_items_leaked(chunk_identifier_generator.next()),
1612 updates,
1613 )
1614 .push_items(new_items, chunk_identifier_generator, updates)
1617 }
1618 }
1619 }
1620 }
1621
1622 fn insert_next(
1627 &mut self,
1628 mut new_chunk_ptr: NonNull<Self>,
1629 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1630 ) -> &mut Self
1631 where
1632 Gap: Clone,
1633 {
1634 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1635
1636 if let Some(next_chunk) = self.next_mut() {
1638 next_chunk.previous = Some(new_chunk_ptr);
1640
1641 new_chunk.next = self.next;
1643 }
1644
1645 self.next = Some(new_chunk_ptr);
1647 new_chunk.previous = Some(self.as_ptr());
1649
1650 if let Some(updates) = updates.as_mut() {
1651 let previous = new_chunk.previous().map(Chunk::identifier);
1652 let new = new_chunk.identifier();
1653 let next = new_chunk.next().map(Chunk::identifier);
1654
1655 match new_chunk.content() {
1656 ChunkContent::Gap(gap) => {
1657 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1658 }
1659
1660 ChunkContent::Items(..) => {
1661 updates.push(Update::NewItemsChunk { previous, new, next })
1662 }
1663 }
1664 }
1665
1666 new_chunk
1667 }
1668
1669 fn insert_before(
1674 &mut self,
1675 mut new_chunk_ptr: NonNull<Self>,
1676 updates: Option<&mut ObservableUpdates<Item, Gap>>,
1677 ) -> &mut Self
1678 where
1679 Gap: Clone,
1680 {
1681 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1682
1683 if let Some(previous_chunk) = self.previous_mut() {
1685 previous_chunk.next = Some(new_chunk_ptr);
1687
1688 new_chunk.previous = self.previous;
1690 }
1691 else {
1694 new_chunk.lazy_previous = self.lazy_previous.take();
1695 }
1696
1697 self.previous = Some(new_chunk_ptr);
1699 new_chunk.next = Some(self.as_ptr());
1701
1702 if let Some(updates) = updates {
1703 let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1704 let new = new_chunk.identifier();
1705 let next = new_chunk.next().map(Chunk::identifier);
1706
1707 match new_chunk.content() {
1708 ChunkContent::Gap(gap) => {
1709 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1710 }
1711
1712 ChunkContent::Items(..) => {
1713 updates.push(Update::NewItemsChunk { previous, new, next })
1714 }
1715 }
1716 }
1717
1718 new_chunk
1719 }
1720
1721 fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1726 let previous_ptr = self.previous;
1727 let next_ptr = self.next;
1728 let lazy_previous = self.lazy_previous.take();
1732
1733 if let Some(previous) = self.previous_mut() {
1734 previous.next = next_ptr;
1735 }
1736
1737 if let Some(next) = self.next_mut() {
1738 next.previous = previous_ptr;
1739 next.lazy_previous = lazy_previous;
1740 }
1741
1742 if let Some(updates) = updates {
1743 updates.push(Update::RemoveChunk(self.identifier()));
1744 }
1745 }
1746
1747 fn previous(&self) -> Option<&Self> {
1749 self.previous.map(|non_null| unsafe { non_null.as_ref() })
1750 }
1751
1752 fn previous_mut(&mut self) -> Option<&mut Self> {
1754 self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1755 }
1756
1757 fn next(&self) -> Option<&Self> {
1759 self.next.map(|non_null| unsafe { non_null.as_ref() })
1760 }
1761
1762 fn next_mut(&mut self) -> Option<&mut Self> {
1764 self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1765 }
1766}
1767
1768impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<CAP, Item, Gap>
1769where
1770 Item: fmt::Debug,
1771 Gap: fmt::Debug,
1772{
1773 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1774 formatter
1775 .debug_struct("LinkedChunk")
1776 .field("first (deref)", unsafe { self.links.first.as_ref() })
1777 .field("last", &self.links.last)
1778 .finish_non_exhaustive()
1779 }
1780}
1781
1782impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1783where
1784 Item: fmt::Debug,
1785 Gap: fmt::Debug,
1786{
1787 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1788 formatter
1789 .debug_struct("Chunk")
1790 .field("identifier", &self.identifier)
1791 .field("content", &self.content)
1792 .field("previous", &self.previous)
1793 .field("ptr", &std::ptr::from_ref(self))
1794 .field("next", &self.next)
1795 .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1796 .finish()
1797 }
1798}
1799
1800#[derive(Clone, Debug)]
1806pub struct RawChunk<Item, Gap> {
1807 pub content: ChunkContent<Item, Gap>,
1809
1810 pub previous: Option<ChunkIdentifier>,
1812
1813 pub identifier: ChunkIdentifier,
1815
1816 pub next: Option<ChunkIdentifier>,
1818}
1819
1820#[derive(Clone, Debug)]
1823pub struct ChunkMetadata {
1824 pub num_items: usize,
1828
1829 pub previous: Option<ChunkIdentifier>,
1831
1832 pub identifier: ChunkIdentifier,
1834
1835 pub next: Option<ChunkIdentifier>,
1837}
1838
1839#[cfg(test)]
1840mod tests {
1841 use std::{
1842 ops::Not,
1843 sync::{Arc, atomic::Ordering},
1844 };
1845
1846 use assert_matches::assert_matches;
1847
1848 use super::{
1849 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Error, LinkedChunk,
1850 Position,
1851 };
1852
1853 #[test]
1854 fn test_chunk_identifier_generator() {
1855 let generator = ChunkIdentifierGenerator::new_from_scratch();
1856
1857 assert_eq!(generator.next(), ChunkIdentifier(1));
1858 assert_eq!(generator.next(), ChunkIdentifier(2));
1859 assert_eq!(generator.next(), ChunkIdentifier(3));
1860 assert_eq!(generator.next(), ChunkIdentifier(4));
1861
1862 let generator =
1863 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1864
1865 assert_eq!(generator.next(), ChunkIdentifier(43));
1866 assert_eq!(generator.next(), ChunkIdentifier(44));
1867 assert_eq!(generator.next(), ChunkIdentifier(45));
1868 assert_eq!(generator.next(), ChunkIdentifier(46));
1869 }
1870
1871 #[test]
1872 fn test_empty() {
1873 let items = LinkedChunk::<3, char, ()>::new();
1874
1875 assert_eq!(items.num_items(), 0);
1876
1877 }
1880
1881 #[test]
1882 fn test_updates() {
1883 assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1884 assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1885 }
1886
1887 #[test]
1888 fn test_new_with_initial_update() {
1889 use super::Update::*;
1890
1891 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1892
1893 assert_eq!(
1894 linked_chunk.updates().unwrap().take(),
1895 &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1896 );
1897 }
1898
1899 #[test]
1900 fn test_push_items() {
1901 use super::Update::*;
1902
1903 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1904
1905 let _ = linked_chunk.updates().unwrap().take();
1907
1908 linked_chunk.push_items_back(['a']);
1909
1910 assert_items_eq!(linked_chunk, ['a']);
1911 assert_eq!(
1912 linked_chunk.updates().unwrap().take(),
1913 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1914 );
1915
1916 linked_chunk.push_items_back(['b', 'c']);
1917 assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1918 assert_eq!(
1919 linked_chunk.updates().unwrap().take(),
1920 &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1921 );
1922
1923 linked_chunk.push_items_back(['d', 'e']);
1924 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
1925 assert_eq!(
1926 linked_chunk.updates().unwrap().take(),
1927 &[
1928 NewItemsChunk {
1929 previous: Some(ChunkIdentifier(0)),
1930 new: ChunkIdentifier(1),
1931 next: None
1932 },
1933 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1934 ]
1935 );
1936
1937 linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1938 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1939 assert_eq!(
1940 linked_chunk.updates().unwrap().take(),
1941 &[
1942 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1943 NewItemsChunk {
1944 previous: Some(ChunkIdentifier(1)),
1945 new: ChunkIdentifier(2),
1946 next: None,
1947 },
1948 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1949 NewItemsChunk {
1950 previous: Some(ChunkIdentifier(2)),
1951 new: ChunkIdentifier(3),
1952 next: None,
1953 },
1954 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1955 ]
1956 );
1957
1958 assert_eq!(linked_chunk.num_items(), 10);
1959 }
1960
1961 #[test]
1962 fn test_push_gap() {
1963 use super::Update::*;
1964
1965 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1966
1967 let _ = linked_chunk.updates().unwrap().take();
1969
1970 linked_chunk.push_items_back(['a']);
1971 assert_items_eq!(linked_chunk, ['a']);
1972 assert_eq!(
1973 linked_chunk.updates().unwrap().take(),
1974 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1975 );
1976
1977 linked_chunk.push_gap_back(());
1978 assert_items_eq!(linked_chunk, ['a'] [-]);
1979 assert_eq!(
1980 linked_chunk.updates().unwrap().take(),
1981 &[NewGapChunk {
1982 previous: Some(ChunkIdentifier(0)),
1983 new: ChunkIdentifier(1),
1984 next: None,
1985 gap: (),
1986 }]
1987 );
1988
1989 linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1990 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1991 assert_eq!(
1992 linked_chunk.updates().unwrap().take(),
1993 &[
1994 NewItemsChunk {
1995 previous: Some(ChunkIdentifier(1)),
1996 new: ChunkIdentifier(2),
1997 next: None,
1998 },
1999 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
2000 NewItemsChunk {
2001 previous: Some(ChunkIdentifier(2)),
2002 new: ChunkIdentifier(3),
2003 next: None,
2004 },
2005 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
2006 ]
2007 );
2008
2009 linked_chunk.push_gap_back(());
2010 linked_chunk.push_gap_back(()); assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
2012 assert_eq!(
2013 linked_chunk.updates().unwrap().take(),
2014 &[
2015 NewGapChunk {
2016 previous: Some(ChunkIdentifier(3)),
2017 new: ChunkIdentifier(4),
2018 next: None,
2019 gap: (),
2020 },
2021 NewGapChunk {
2022 previous: Some(ChunkIdentifier(4)),
2023 new: ChunkIdentifier(5),
2024 next: None,
2025 gap: (),
2026 }
2027 ]
2028 );
2029
2030 linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
2031 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
2032 assert_eq!(
2033 linked_chunk.updates().unwrap().take(),
2034 &[
2035 NewItemsChunk {
2036 previous: Some(ChunkIdentifier(5)),
2037 new: ChunkIdentifier(6),
2038 next: None,
2039 },
2040 PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
2041 NewItemsChunk {
2042 previous: Some(ChunkIdentifier(6)),
2043 new: ChunkIdentifier(7),
2044 next: None,
2045 },
2046 PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
2047 ]
2048 );
2049
2050 assert_eq!(linked_chunk.num_items(), 9);
2051 }
2052
2053 #[test]
2054 fn test_identifiers_and_positions() {
2055 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2056 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2057 linked_chunk.push_gap_back(());
2058 linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
2059 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
2060
2061 assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
2062 assert_eq!(
2063 linked_chunk.item_position(|item| *item == 'e'),
2064 Some(Position(ChunkIdentifier(1), 1))
2065 );
2066 }
2067
2068 #[test]
2069 fn test_rchunks() {
2070 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2071 linked_chunk.push_items_back(['a', 'b']);
2072 linked_chunk.push_gap_back(());
2073 linked_chunk.push_items_back(['c', 'd', 'e']);
2074
2075 let mut iterator = linked_chunk.rchunks();
2076
2077 assert_matches!(
2078 iterator.next(),
2079 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2080 assert_eq!(items, &['e']);
2081 }
2082 );
2083 assert_matches!(
2084 iterator.next(),
2085 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2086 assert_eq!(items, &['c', 'd']);
2087 }
2088 );
2089 assert_matches!(
2090 iterator.next(),
2091 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2092 );
2093 assert_matches!(
2094 iterator.next(),
2095 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2096 assert_eq!(items, &['a', 'b']);
2097 }
2098 );
2099 assert_matches!(iterator.next(), None);
2100 }
2101
2102 #[test]
2103 fn test_chunks() {
2104 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2105 linked_chunk.push_items_back(['a', 'b']);
2106 linked_chunk.push_gap_back(());
2107 linked_chunk.push_items_back(['c', 'd', 'e']);
2108
2109 let mut iterator = linked_chunk.chunks();
2110
2111 assert_matches!(
2112 iterator.next(),
2113 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2114 assert_eq!(items, &['a', 'b']);
2115 }
2116 );
2117 assert_matches!(
2118 iterator.next(),
2119 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2120 );
2121 assert_matches!(
2122 iterator.next(),
2123 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2124 assert_eq!(items, &['c', 'd']);
2125 }
2126 );
2127 assert_matches!(
2128 iterator.next(),
2129 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2130 assert_eq!(items, &['e']);
2131 }
2132 );
2133 assert_matches!(iterator.next(), None);
2134 }
2135
2136 #[test]
2137 fn test_rchunks_from() -> Result<(), Error> {
2138 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2139 linked_chunk.push_items_back(['a', 'b']);
2140 linked_chunk.push_gap_back(());
2141 linked_chunk.push_items_back(['c', 'd', 'e']);
2142
2143 let mut iterator = linked_chunk.rchunks_from(
2144 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2145 )?;
2146
2147 assert_matches!(
2148 iterator.next(),
2149 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2150 assert_eq!(items, &['c', 'd']);
2151 }
2152 );
2153 assert_matches!(
2154 iterator.next(),
2155 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2156 );
2157 assert_matches!(
2158 iterator.next(),
2159 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2160 assert_eq!(items, &['a', 'b']);
2161 }
2162 );
2163 assert_matches!(iterator.next(), None);
2164
2165 Ok(())
2166 }
2167
2168 #[test]
2169 fn test_chunks_from() -> Result<(), Error> {
2170 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2171 linked_chunk.push_items_back(['a', 'b']);
2172 linked_chunk.push_gap_back(());
2173 linked_chunk.push_items_back(['c', 'd', 'e']);
2174
2175 let mut iterator = linked_chunk.chunks_from(
2176 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2177 )?;
2178
2179 assert_matches!(
2180 iterator.next(),
2181 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2182 assert_eq!(items, &['c', 'd']);
2183 }
2184 );
2185 assert_matches!(
2186 iterator.next(),
2187 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2188 assert_eq!(items, &['e']);
2189 }
2190 );
2191 assert_matches!(iterator.next(), None);
2192
2193 Ok(())
2194 }
2195
2196 #[test]
2197 fn test_ritems() {
2198 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2199 linked_chunk.push_items_back(['a', 'b']);
2200 linked_chunk.push_gap_back(());
2201 linked_chunk.push_items_back(['c', 'd', 'e']);
2202
2203 let mut iterator = linked_chunk.ritems();
2204
2205 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2206 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2207 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2208 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2209 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2210 assert_matches!(iterator.next(), None);
2211 }
2212
2213 #[test]
2214 fn test_ritems_with_final_gap() -> Result<(), Error> {
2215 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2216 linked_chunk.push_items_back(['a', 'b']);
2217 linked_chunk.push_gap_back(());
2218 linked_chunk.push_items_back(['c', 'd', 'e']);
2219 linked_chunk.push_gap_back(());
2220
2221 let mut iterator = linked_chunk.ritems();
2222
2223 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
2224 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2225 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2226 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2227 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2228 assert_matches!(iterator.next(), None);
2229
2230 Ok(())
2231 }
2232
2233 #[test]
2234 fn test_ritems_empty() {
2235 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2236 let mut iterator = linked_chunk.ritems();
2237
2238 assert_matches!(iterator.next(), None);
2239 }
2240
2241 #[test]
2242 fn test_items() {
2243 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2244 linked_chunk.push_items_back(['a', 'b']);
2245 linked_chunk.push_gap_back(());
2246 linked_chunk.push_items_back(['c', 'd', 'e']);
2247
2248 let mut iterator = linked_chunk.items();
2249
2250 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2251 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2252 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2253 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2254 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2255 assert_matches!(iterator.next(), None);
2256 }
2257
2258 #[test]
2259 fn test_items_empty() {
2260 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2261 let mut iterator = linked_chunk.items();
2262
2263 assert_matches!(iterator.next(), None);
2264 }
2265
2266 #[test]
2267 fn test_ritems_from() -> Result<(), Error> {
2268 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2269 linked_chunk.push_items_back(['a', 'b']);
2270 linked_chunk.push_gap_back(());
2271 linked_chunk.push_items_back(['c', 'd', 'e']);
2272
2273 let mut iterator =
2274 linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2275
2276 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2277 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2278 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2279 assert_matches!(iterator.next(), None);
2280
2281 Ok(())
2282 }
2283
2284 #[test]
2285 fn test_items_from() -> Result<(), Error> {
2286 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2287 linked_chunk.push_items_back(['a', 'b']);
2288 linked_chunk.push_gap_back(());
2289 linked_chunk.push_items_back(['c', 'd', 'e']);
2290
2291 let mut iterator =
2292 linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2293
2294 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2295 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2296 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2297 assert_matches!(iterator.next(), None);
2298
2299 Ok(())
2300 }
2301
2302 #[test]
2303 fn test_insert_items_at() -> Result<(), Error> {
2304 use super::Update::*;
2305
2306 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2307
2308 let _ = linked_chunk.updates().unwrap().take();
2310
2311 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2312 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2313 assert_eq!(
2314 linked_chunk.updates().unwrap().take(),
2315 &[
2316 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2317 NewItemsChunk {
2318 previous: Some(ChunkIdentifier(0)),
2319 new: ChunkIdentifier(1),
2320 next: None,
2321 },
2322 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2323 ]
2324 );
2325
2326 {
2328 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2329
2330 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2333
2334 assert_items_eq!(
2335 linked_chunk,
2336 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2337 );
2338 assert_eq!(linked_chunk.num_items(), 10);
2339 assert_eq!(
2340 linked_chunk.updates().unwrap().take(),
2341 &[
2342 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2343 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2344 NewItemsChunk {
2345 previous: Some(ChunkIdentifier(1)),
2346 new: ChunkIdentifier(2),
2347 next: None,
2348 },
2349 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2350 StartReattachItems,
2351 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2352 NewItemsChunk {
2353 previous: Some(ChunkIdentifier(2)),
2354 new: ChunkIdentifier(3),
2355 next: None,
2356 },
2357 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2358 EndReattachItems,
2359 ]
2360 );
2361 }
2362
2363 {
2365 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2366 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2367
2368 assert_items_eq!(
2369 linked_chunk,
2370 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2371 );
2372 assert_eq!(linked_chunk.num_items(), 14);
2373 assert_eq!(
2374 linked_chunk.updates().unwrap().take(),
2375 &[
2376 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2377 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2378 NewItemsChunk {
2379 previous: Some(ChunkIdentifier(0)),
2380 new: ChunkIdentifier(4),
2381 next: Some(ChunkIdentifier(1)),
2382 },
2383 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2384 StartReattachItems,
2385 PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2386 NewItemsChunk {
2387 previous: Some(ChunkIdentifier(4)),
2388 new: ChunkIdentifier(5),
2389 next: Some(ChunkIdentifier(1)),
2390 },
2391 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2392 EndReattachItems,
2393 ]
2394 );
2395 }
2396
2397 {
2399 let pos_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2400 linked_chunk.insert_items_at(pos_c, ['r', 's'])?;
2401
2402 assert_items_eq!(
2403 linked_chunk,
2404 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2405 );
2406 assert_eq!(linked_chunk.num_items(), 16);
2407 assert_eq!(
2408 linked_chunk.updates().unwrap().take(),
2409 &[
2410 DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2411 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2412 StartReattachItems,
2413 PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2414 EndReattachItems,
2415 ]
2416 );
2417 }
2418
2419 {
2421 let pos_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2422 let pos_f = Position(pos_f.chunk_identifier(), pos_f.index() + 1);
2423
2424 linked_chunk.insert_items_at(pos_f, ['p', 'q'])?;
2425 assert_items_eq!(
2426 linked_chunk,
2427 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2428 );
2429 assert_eq!(
2430 linked_chunk.updates().unwrap().take(),
2431 &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2432 );
2433 assert_eq!(linked_chunk.num_items(), 18);
2434 }
2435
2436 {
2438 assert_matches!(
2439 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2440 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2441 );
2442 assert!(linked_chunk.updates().unwrap().take().is_empty());
2443 }
2444
2445 {
2447 assert_matches!(
2448 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2449 Err(Error::InvalidItemIndex { index: 128 })
2450 );
2451 assert!(linked_chunk.updates().unwrap().take().is_empty());
2452 }
2453
2454 {
2456 linked_chunk.push_gap_back(());
2458 assert_items_eq!(
2459 linked_chunk,
2460 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2461 );
2462 assert_eq!(
2463 linked_chunk.updates().unwrap().take(),
2464 &[NewGapChunk {
2465 previous: Some(ChunkIdentifier(3)),
2466 new: ChunkIdentifier(6),
2467 next: None,
2468 gap: ()
2469 }]
2470 );
2471
2472 assert_matches!(
2473 linked_chunk.insert_items_at(Position(ChunkIdentifier(6), 0), ['u', 'v'],),
2474 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2475 );
2476 }
2477
2478 assert_eq!(linked_chunk.num_items(), 18);
2479
2480 Ok(())
2481 }
2482
2483 #[test]
2484 fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2485 use super::Update::*;
2486
2487 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2488
2489 let _ = linked_chunk.updates().unwrap().take();
2491
2492 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2493 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2494 assert_eq!(
2495 linked_chunk.updates().unwrap().take(),
2496 &[
2497 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2498 NewItemsChunk {
2499 previous: Some(ChunkIdentifier(0)),
2500 new: ChunkIdentifier(1),
2501 next: None,
2502 },
2503 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2504 ]
2505 );
2506
2507 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2509
2510 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2513
2514 assert_items_eq!(
2515 linked_chunk,
2516 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2517 );
2518 assert_eq!(linked_chunk.num_items(), 10);
2519 assert_eq!(
2520 linked_chunk.updates().unwrap().take(),
2521 &[
2522 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2523 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2524 NewItemsChunk {
2525 previous: Some(ChunkIdentifier(1)),
2526 new: ChunkIdentifier(2),
2527 next: None,
2528 },
2529 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2530 StartReattachItems,
2531 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2532 NewItemsChunk {
2533 previous: Some(ChunkIdentifier(2)),
2534 new: ChunkIdentifier(3),
2535 next: None,
2536 },
2537 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2538 EndReattachItems,
2539 ]
2540 );
2541
2542 Ok(())
2543 }
2544
2545 #[test]
2546 fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2547 use super::Update::*;
2548
2549 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2550
2551 let _ = linked_chunk.updates().unwrap().take();
2553
2554 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2555 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2556 assert_eq!(
2557 linked_chunk.updates().unwrap().take(),
2558 &[
2559 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2560 NewItemsChunk {
2561 previous: Some(ChunkIdentifier(0)),
2562 new: ChunkIdentifier(1),
2563 next: None,
2564 },
2565 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2566 ]
2567 );
2568
2569 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2571 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2572
2573 assert_items_eq!(
2574 linked_chunk,
2575 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'e', 'f']
2576 );
2577 assert_eq!(linked_chunk.num_items(), 10);
2578 assert_eq!(
2579 linked_chunk.updates().unwrap().take(),
2580 &[
2581 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2582 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2583 NewItemsChunk {
2584 previous: Some(ChunkIdentifier(0)),
2585 new: ChunkIdentifier(2),
2586 next: Some(ChunkIdentifier(1)),
2587 },
2588 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2589 StartReattachItems,
2590 PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2591 NewItemsChunk {
2592 previous: Some(ChunkIdentifier(2)),
2593 new: ChunkIdentifier(3),
2594 next: Some(ChunkIdentifier(1)),
2595 },
2596 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2597 EndReattachItems,
2598 ]
2599 );
2600
2601 Ok(())
2602 }
2603
2604 #[test]
2605 fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2606 use super::Update::*;
2607
2608 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2609
2610 let _ = linked_chunk.updates().unwrap().take();
2612
2613 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2614 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2615 assert_eq!(
2616 linked_chunk.updates().unwrap().take(),
2617 &[
2618 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2619 NewItemsChunk {
2620 previous: Some(ChunkIdentifier(0)),
2621 new: ChunkIdentifier(1),
2622 next: None,
2623 },
2624 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2625 NewItemsChunk {
2626 previous: Some(ChunkIdentifier(1)),
2627 new: ChunkIdentifier(2),
2628 next: None,
2629 },
2630 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2631 ]
2632 );
2633
2634 let pos_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2635 linked_chunk.insert_items_at(pos_d, ['r', 's'])?;
2636
2637 assert_items_eq!(
2638 linked_chunk,
2639 ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2640 );
2641 assert_eq!(linked_chunk.num_items(), 10);
2642 assert_eq!(
2643 linked_chunk.updates().unwrap().take(),
2644 &[
2645 DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2646 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2647 StartReattachItems,
2648 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2649 NewItemsChunk {
2650 previous: Some(ChunkIdentifier(1)),
2651 new: ChunkIdentifier(3),
2652 next: Some(ChunkIdentifier(2)),
2653 },
2654 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2655 EndReattachItems,
2656 ]
2657 );
2658
2659 Ok(())
2660 }
2661
2662 #[test]
2663 fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2664 use super::Update::*;
2665
2666 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2667
2668 let _ = linked_chunk.updates().unwrap().take();
2670
2671 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2672 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2673 assert_eq!(
2674 linked_chunk.updates().unwrap().take(),
2675 &[
2676 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2677 NewItemsChunk {
2678 previous: Some(ChunkIdentifier(0)),
2679 new: ChunkIdentifier(1),
2680 next: None,
2681 },
2682 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2683 ]
2684 );
2685
2686 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2688 let pos_after_e = Position(pos_e.chunk_identifier(), pos_e.index() + 1);
2689
2690 linked_chunk.insert_items_at(pos_after_e, ['p', 'q'])?;
2691 assert_items_eq!(
2692 linked_chunk,
2693 ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2694 );
2695 assert_eq!(
2696 linked_chunk.updates().unwrap().take(),
2697 &[
2698 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2699 NewItemsChunk {
2700 previous: Some(ChunkIdentifier(1)),
2701 new: ChunkIdentifier(2),
2702 next: None
2703 },
2704 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2705 ]
2706 );
2707 assert_eq!(linked_chunk.num_items(), 7);
2708
2709 Ok(())
2710 }
2711
2712 #[test]
2713 fn test_insert_items_at_errs() -> Result<(), Error> {
2714 use super::Update::*;
2715
2716 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2717
2718 let _ = linked_chunk.updates().unwrap().take();
2720
2721 linked_chunk.push_items_back(['a', 'b', 'c']);
2722 linked_chunk.push_gap_back(());
2723 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2724 assert_eq!(
2725 linked_chunk.updates().unwrap().take(),
2726 &[
2727 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2728 NewGapChunk {
2729 previous: Some(ChunkIdentifier(0)),
2730 new: ChunkIdentifier(1),
2731 next: None,
2732 gap: (),
2733 },
2734 ]
2735 );
2736
2737 {
2739 assert_matches!(
2740 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2741 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2742 );
2743 assert!(linked_chunk.updates().unwrap().take().is_empty());
2744 }
2745
2746 {
2748 assert_matches!(
2749 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2750 Err(Error::InvalidItemIndex { index: 128 })
2751 );
2752 assert!(linked_chunk.updates().unwrap().take().is_empty());
2753 }
2754
2755 {
2757 assert_matches!(
2758 linked_chunk.insert_items_at(Position(ChunkIdentifier(1), 0), ['u', 'v'],),
2759 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2760 );
2761 }
2762
2763 Ok(())
2764 }
2765
2766 #[test]
2767 fn test_remove_item_at() -> Result<(), Error> {
2768 use super::Update::*;
2769
2770 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2771
2772 let _ = linked_chunk.updates().unwrap().take();
2774
2775 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2776 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2777 assert_eq!(linked_chunk.num_items(), 11);
2778
2779 let _ = linked_chunk.updates().unwrap().take();
2781
2782 {
2785 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2786 let removed_item = linked_chunk.remove_item_at(position_of_f)?;
2787
2788 assert_eq!(removed_item, 'f');
2789 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2790 assert_eq!(linked_chunk.num_items(), 10);
2791
2792 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2793 let removed_item = linked_chunk.remove_item_at(position_of_e)?;
2794
2795 assert_eq!(removed_item, 'e');
2796 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2797 assert_eq!(linked_chunk.num_items(), 9);
2798
2799 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2800 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2801
2802 assert_eq!(removed_item, 'd');
2803 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2804 assert_eq!(linked_chunk.num_items(), 8);
2805
2806 assert_eq!(
2807 linked_chunk.updates().unwrap().take(),
2808 &[
2809 RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2810 RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2811 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2812 RemoveChunk(ChunkIdentifier(1)),
2813 ]
2814 );
2815 }
2816
2817 {
2820 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2821 let removed_item = linked_chunk.remove_item_at(first_position)?;
2822
2823 assert_eq!(removed_item, 'a');
2824 assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2825 assert_eq!(linked_chunk.num_items(), 7);
2826
2827 let removed_item = linked_chunk.remove_item_at(first_position)?;
2828
2829 assert_eq!(removed_item, 'b');
2830 assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2831 assert_eq!(linked_chunk.num_items(), 6);
2832
2833 let removed_item = linked_chunk.remove_item_at(first_position)?;
2834
2835 assert_eq!(removed_item, 'c');
2836 assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2837 assert_eq!(linked_chunk.num_items(), 5);
2838
2839 assert_eq!(
2840 linked_chunk.updates().unwrap().take(),
2841 &[
2842 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2843 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2844 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2845 ]
2846 );
2847 }
2848
2849 {
2852 let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2853 let removed_item = linked_chunk.remove_item_at(first_position)?;
2854
2855 assert_eq!(removed_item, 'g');
2856 assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2857 assert_eq!(linked_chunk.num_items(), 4);
2858
2859 let removed_item = linked_chunk.remove_item_at(first_position)?;
2860
2861 assert_eq!(removed_item, 'h');
2862 assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2863 assert_eq!(linked_chunk.num_items(), 3);
2864
2865 let removed_item = linked_chunk.remove_item_at(first_position)?;
2866
2867 assert_eq!(removed_item, 'i');
2868 assert_items_eq!(linked_chunk, [] ['j', 'k']);
2869 assert_eq!(linked_chunk.num_items(), 2);
2870
2871 assert_eq!(
2872 linked_chunk.updates().unwrap().take(),
2873 &[
2874 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2875 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2876 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2877 RemoveChunk(ChunkIdentifier(2)),
2878 ]
2879 );
2880 }
2881
2882 {
2885 let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2886 let removed_item = linked_chunk.remove_item_at(position_of_k)?;
2887
2888 assert_eq!(removed_item, 'k');
2889 #[rustfmt::skip]
2890 assert_items_eq!(linked_chunk, [] ['j']);
2891 assert_eq!(linked_chunk.num_items(), 1);
2892
2893 let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2894 let removed_item = linked_chunk.remove_item_at(position_of_j)?;
2895
2896 assert_eq!(removed_item, 'j');
2897 assert_items_eq!(linked_chunk, []);
2898 assert_eq!(linked_chunk.num_items(), 0);
2899
2900 assert_eq!(
2901 linked_chunk.updates().unwrap().take(),
2902 &[
2903 RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2904 RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2905 RemoveChunk(ChunkIdentifier(3)),
2906 ]
2907 );
2908 }
2909
2910 {
2912 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2913
2914 #[rustfmt::skip]
2915 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2916 assert_eq!(linked_chunk.num_items(), 4);
2917
2918 assert_matches!(
2920 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 3)),
2921 Err(Error::InvalidItemIndex { index: 3 })
2922 );
2923
2924 assert_matches!(
2926 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 42)),
2927 Err(Error::InvalidItemIndex { index: 42 })
2928 );
2929
2930 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2931 linked_chunk.insert_gap_at((), position_of_c)?;
2932
2933 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2934 assert_eq!(linked_chunk.num_items(), 4);
2935
2936 let _ = linked_chunk.updates().unwrap().take();
2938
2939 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2940 let removed_item = linked_chunk.remove_item_at(position_of_c)?;
2941
2942 assert_eq!(removed_item, 'c');
2943 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2944 assert_eq!(linked_chunk.num_items(), 3);
2945
2946 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2947 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2948
2949 assert_eq!(removed_item, 'd');
2950 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2951 assert_eq!(linked_chunk.num_items(), 2);
2952
2953 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2954 let removed_item = linked_chunk.remove_item_at(first_position)?;
2955
2956 assert_eq!(removed_item, 'a');
2957 assert_items_eq!(linked_chunk, ['b'] [-]);
2958 assert_eq!(linked_chunk.num_items(), 1);
2959
2960 let removed_item = linked_chunk.remove_item_at(first_position)?;
2961
2962 assert_eq!(removed_item, 'b');
2963 assert_items_eq!(linked_chunk, [] [-]);
2964 assert_eq!(linked_chunk.num_items(), 0);
2965
2966 assert_eq!(
2967 linked_chunk.updates().unwrap().take(),
2968 &[
2969 RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2970 RemoveChunk(ChunkIdentifier(6)),
2971 RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2972 RemoveChunk(ChunkIdentifier(4)),
2973 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2974 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2975 ]
2976 );
2977 }
2978
2979 Ok(())
2980 }
2981
2982 #[test]
2983 fn test_insert_gap_at() -> Result<(), Error> {
2984 use super::Update::*;
2985
2986 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2987
2988 let _ = linked_chunk.updates().unwrap().take();
2990
2991 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2992 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2993 assert_eq!(
2994 linked_chunk.updates().unwrap().take(),
2995 &[
2996 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2997 NewItemsChunk {
2998 previous: Some(ChunkIdentifier(0)),
2999 new: ChunkIdentifier(1),
3000 next: None
3001 },
3002 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
3003 ]
3004 );
3005
3006 {
3008 let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
3009 linked_chunk.insert_gap_at((), position_of_b)?;
3010
3011 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
3012 assert_eq!(
3013 linked_chunk.updates().unwrap().take(),
3014 &[
3015 DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
3016 NewGapChunk {
3017 previous: Some(ChunkIdentifier(0)),
3018 new: ChunkIdentifier(2),
3019 next: Some(ChunkIdentifier(1)),
3020 gap: (),
3021 },
3022 StartReattachItems,
3023 NewItemsChunk {
3024 previous: Some(ChunkIdentifier(2)),
3025 new: ChunkIdentifier(3),
3026 next: Some(ChunkIdentifier(1)),
3027 },
3028 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
3029 EndReattachItems,
3030 ]
3031 );
3032 }
3033
3034 {
3037 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3038 linked_chunk.insert_gap_at((), position_of_a)?;
3039
3040 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
3043 assert_eq!(
3044 linked_chunk.updates().unwrap().take(),
3045 &[NewGapChunk {
3046 previous: None,
3047 new: ChunkIdentifier(4),
3048 next: Some(ChunkIdentifier(0)),
3049 gap: (),
3050 },]
3051 );
3052 }
3053
3054 {
3057 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
3058 linked_chunk.insert_gap_at((), position_of_d)?;
3059
3060 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
3064 assert_eq!(
3065 linked_chunk.updates().unwrap().take(),
3066 &[NewGapChunk {
3067 previous: Some(ChunkIdentifier(3)),
3068 new: ChunkIdentifier(5),
3069 next: Some(ChunkIdentifier(1)),
3070 gap: (),
3071 }]
3072 );
3073 }
3074
3075 {
3077 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3079 let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
3080
3081 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
3082
3083 assert_eq!(
3084 linked_chunk.updates().unwrap().take(),
3085 &[
3086 NewItemsChunk {
3087 previous: Some(ChunkIdentifier(5)),
3088 new: ChunkIdentifier(6),
3089 next: Some(ChunkIdentifier(1)),
3090 },
3091 RemoveChunk(ChunkIdentifier(5)),
3092 ]
3093 );
3094
3095 linked_chunk.insert_gap_at((), position)?;
3096
3097 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
3098 assert_eq!(
3099 linked_chunk.updates().unwrap().take(),
3100 &[NewGapChunk {
3101 previous: Some(ChunkIdentifier(3)),
3102 new: ChunkIdentifier(7),
3103 next: Some(ChunkIdentifier(6)),
3104 gap: (),
3105 }]
3106 );
3107 }
3108
3109 {
3111 assert_matches!(
3112 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
3113 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
3114 );
3115 assert!(linked_chunk.updates().unwrap().take().is_empty());
3116 }
3117
3118 {
3120 assert_matches!(
3121 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
3122 Err(Error::InvalidItemIndex { index: 128 })
3123 );
3124 assert!(linked_chunk.updates().unwrap().take().is_empty());
3125 }
3126
3127 {
3129 let position_of_a_gap = Position(ChunkIdentifier(2), 0);
3132 assert_matches!(
3133 linked_chunk.insert_gap_at((), position_of_a_gap),
3134 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
3135 );
3136 assert!(linked_chunk.updates().unwrap().take().is_empty());
3137 }
3138
3139 assert_eq!(linked_chunk.num_items(), 6);
3140
3141 Ok(())
3142 }
3143
3144 #[test]
3145 fn test_replace_gap_at_middle() -> Result<(), Error> {
3146 use super::Update::*;
3147
3148 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3149
3150 let _ = linked_chunk.updates().unwrap().take();
3152
3153 linked_chunk.push_items_back(['a', 'b']);
3154 linked_chunk.push_gap_back(());
3155 linked_chunk.push_items_back(['l', 'm']);
3156 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
3157 assert_eq!(
3158 linked_chunk.updates().unwrap().take(),
3159 &[
3160 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3161 NewGapChunk {
3162 previous: Some(ChunkIdentifier(0)),
3163 new: ChunkIdentifier(1),
3164 next: None,
3165 gap: (),
3166 },
3167 NewItemsChunk {
3168 previous: Some(ChunkIdentifier(1)),
3169 new: ChunkIdentifier(2),
3170 next: None,
3171 },
3172 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
3173 ]
3174 );
3175
3176 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3178 assert_eq!(gap_identifier, ChunkIdentifier(1));
3179
3180 let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
3181 assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
3182 assert_items_eq!(
3183 linked_chunk,
3184 ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
3185 );
3186 assert_eq!(
3187 linked_chunk.updates().unwrap().take(),
3188 &[
3189 NewItemsChunk {
3190 previous: Some(ChunkIdentifier(1)),
3191 new: ChunkIdentifier(3),
3192 next: Some(ChunkIdentifier(2)),
3193 },
3194 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
3195 NewItemsChunk {
3196 previous: Some(ChunkIdentifier(3)),
3197 new: ChunkIdentifier(4),
3198 next: Some(ChunkIdentifier(2)),
3199 },
3200 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
3201 RemoveChunk(ChunkIdentifier(1)),
3202 ]
3203 );
3204
3205 assert_eq!(linked_chunk.num_items(), 9);
3206
3207 Ok(())
3208 }
3209
3210 #[test]
3211 fn test_replace_gap_at_end() -> Result<(), Error> {
3212 use super::Update::*;
3213
3214 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3215
3216 let _ = linked_chunk.updates().unwrap().take();
3218
3219 linked_chunk.push_items_back(['a', 'b']);
3220 linked_chunk.push_gap_back(());
3221 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3222 assert_eq!(
3223 linked_chunk.updates().unwrap().take(),
3224 &[
3225 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3226 NewGapChunk {
3227 previous: Some(ChunkIdentifier(0)),
3228 new: ChunkIdentifier(1),
3229 next: None,
3230 gap: (),
3231 },
3232 ]
3233 );
3234
3235 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3237 assert_eq!(gap_identifier, ChunkIdentifier(1));
3238
3239 let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3240 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3241 assert_items_eq!(
3242 linked_chunk,
3243 ['a', 'b'] ['w', 'x', 'y'] ['z']
3244 );
3245 assert_eq!(
3246 linked_chunk.updates().unwrap().take(),
3247 &[
3248 NewItemsChunk {
3249 previous: Some(ChunkIdentifier(1)),
3250 new: ChunkIdentifier(2),
3251 next: None,
3252 },
3253 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3254 NewItemsChunk {
3255 previous: Some(ChunkIdentifier(2)),
3256 new: ChunkIdentifier(3),
3257 next: None,
3258 },
3259 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3260 RemoveChunk(ChunkIdentifier(1)),
3261 ]
3262 );
3263
3264 assert_eq!(linked_chunk.num_items(), 6);
3265
3266 Ok(())
3267 }
3268
3269 #[test]
3270 fn test_replace_gap_at_beginning() -> Result<(), Error> {
3271 use super::Update::*;
3272
3273 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3274
3275 let _ = linked_chunk.updates().unwrap().take();
3277
3278 linked_chunk.push_items_back(['a', 'b']);
3279 assert_items_eq!(linked_chunk, ['a', 'b']);
3280 assert_eq!(
3281 linked_chunk.updates().unwrap().take(),
3282 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },]
3283 );
3284
3285 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3287 linked_chunk.insert_gap_at((), position_of_a).unwrap();
3288 assert_items_eq!(
3289 linked_chunk,
3290 [-] ['a', 'b']
3291 );
3292 assert_eq!(
3293 linked_chunk.updates().unwrap().take(),
3294 &[NewGapChunk {
3295 previous: None,
3296 new: ChunkIdentifier(1),
3297 next: Some(ChunkIdentifier(0)),
3298 gap: (),
3299 }]
3300 );
3301
3302 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3303 assert_eq!(gap_identifier, ChunkIdentifier(1));
3304
3305 let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3306 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3307 assert_items_eq!(
3308 linked_chunk,
3309 ['x'] ['a', 'b']
3310 );
3311 assert_eq!(
3312 linked_chunk.updates().unwrap().take(),
3313 &[
3314 NewItemsChunk {
3315 previous: Some(ChunkIdentifier(1)),
3316 new: ChunkIdentifier(2),
3317 next: Some(ChunkIdentifier(0)),
3318 },
3319 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3320 RemoveChunk(ChunkIdentifier(1)),
3321 ]
3322 );
3323
3324 assert_eq!(linked_chunk.num_items(), 3);
3325
3326 Ok(())
3327 }
3328
3329 #[test]
3330 fn test_remove_empty_chunk_at() -> Result<(), Error> {
3331 use super::Update::*;
3332
3333 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3334
3335 let _ = linked_chunk.updates().unwrap().take();
3337
3338 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3339 linked_chunk.push_items_back(['a', 'b']);
3340 linked_chunk.push_gap_back(());
3341 linked_chunk.push_items_back(['l', 'm']);
3342 linked_chunk.push_gap_back(());
3343 assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3344 assert_eq!(
3345 linked_chunk.updates().unwrap().take(),
3346 &[
3347 NewGapChunk {
3348 previous: None,
3349 new: ChunkIdentifier(1),
3350 next: Some(ChunkIdentifier(0)),
3351 gap: (),
3352 },
3353 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3354 NewGapChunk {
3355 previous: Some(ChunkIdentifier(0)),
3356 new: ChunkIdentifier(2),
3357 next: None,
3358 gap: (),
3359 },
3360 NewItemsChunk {
3361 previous: Some(ChunkIdentifier(2)),
3362 new: ChunkIdentifier(3),
3363 next: None,
3364 },
3365 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3366 NewGapChunk {
3367 previous: Some(ChunkIdentifier(3)),
3368 new: ChunkIdentifier(4),
3369 next: None,
3370 gap: (),
3371 },
3372 ]
3373 );
3374
3375 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3377 assert_matches!(err, Error::RemovingNonEmptyItemsChunk { .. });
3378
3379 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(42)).unwrap_err();
3381 assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3382
3383 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(2)).unwrap();
3385 let next = maybe_next.unwrap();
3386 assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3388 assert_eq!(next.index(), 0);
3389 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3390 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3391
3392 let next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(4)).unwrap();
3394 assert!(next.is_none());
3396 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3397 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3398
3399 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(1)).unwrap();
3401 let next = maybe_next.unwrap();
3402 assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3403 assert_eq!(next.index(), 0);
3404 assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3405 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3406
3407 Ok(())
3408 }
3409
3410 #[test]
3411 fn test_remove_empty_last_chunk() {
3412 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3413
3414 let _ = linked_chunk.updates().unwrap().take();
3416
3417 assert_items_eq!(linked_chunk, []);
3418 assert!(linked_chunk.updates().unwrap().take().is_empty());
3419
3420 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3422 assert_matches!(err, Error::RemovingLastChunk);
3423 }
3424
3425 #[test]
3426 fn test_chunk_item_positions() {
3427 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3428 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3429 linked_chunk.push_gap_back(());
3430 linked_chunk.push_items_back(['f']);
3431
3432 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3433
3434 let mut iterator = linked_chunk.chunks();
3435
3436 {
3438 let chunk = iterator.next().unwrap();
3439 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3440 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3441 }
3442
3443 {
3445 let chunk = iterator.next().unwrap();
3446 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3447 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3448 }
3449
3450 {
3452 let chunk = iterator.next().unwrap();
3453 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3454 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3455 }
3456
3457 {
3459 let chunk = iterator.next().unwrap();
3460 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3461 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3462 }
3463 }
3464
3465 #[test]
3466 fn test_is_first_and_last_chunk() {
3467 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3468
3469 let mut chunks = linked_chunk.chunks().peekable();
3470 assert!(chunks.peek().unwrap().is_first_chunk());
3471 assert!(chunks.next().unwrap().is_last_chunk());
3472 assert!(chunks.next().is_none());
3473
3474 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3475
3476 let mut chunks = linked_chunk.chunks().peekable();
3477 assert!(chunks.next().unwrap().is_first_chunk());
3478 assert!(chunks.peek().unwrap().is_first_chunk().not());
3479 assert!(chunks.next().unwrap().is_last_chunk().not());
3480 assert!(chunks.next().unwrap().is_last_chunk());
3481 assert!(chunks.next().is_none());
3482 }
3483
3484 #[test]
3489 fn test_clear() {
3490 let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3491
3492 let item = Arc::new('a');
3493 let gap = Arc::new(());
3494
3495 linked_chunk.push_items_back([
3496 item.clone(),
3497 item.clone(),
3498 item.clone(),
3499 item.clone(),
3500 item.clone(),
3501 ]);
3502 linked_chunk.push_gap_back(gap.clone());
3503 linked_chunk.push_items_back([item.clone()]);
3504
3505 assert_eq!(Arc::strong_count(&item), 7);
3506 assert_eq!(Arc::strong_count(&gap), 2);
3507 assert_eq!(linked_chunk.num_items(), 6);
3508 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3509
3510 linked_chunk.clear();
3512
3513 assert_eq!(Arc::strong_count(&item), 1);
3514 assert_eq!(Arc::strong_count(&gap), 1);
3515 assert_eq!(linked_chunk.num_items(), 0);
3516 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3517 }
3518
3519 #[test]
3520 fn test_clear_emit_an_update_clear() {
3521 use super::Update::*;
3522
3523 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3524
3525 assert_eq!(
3526 linked_chunk.updates().unwrap().take(),
3527 &[NewItemsChunk {
3528 previous: None,
3529 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3530 next: None
3531 }]
3532 );
3533
3534 linked_chunk.clear();
3535
3536 assert_eq!(
3537 linked_chunk.updates().unwrap().take(),
3538 &[
3539 Clear,
3540 NewItemsChunk {
3541 previous: None,
3542 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3543 next: None
3544 }
3545 ]
3546 );
3547 }
3548
3549 #[test]
3550 fn test_replace_item() {
3551 use super::Update::*;
3552
3553 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3554
3555 linked_chunk.push_items_back(['a', 'b', 'c']);
3556 linked_chunk.push_gap_back(());
3557 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3559
3560 let _ = linked_chunk.updates().unwrap().take();
3562
3563 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3565 assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3566
3567 assert_eq!(
3568 linked_chunk.updates().unwrap().take(),
3569 &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3570 );
3571
3572 assert_matches!(
3574 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3575 Err(Error::InvalidItemIndex { index: 3 })
3576 );
3577
3578 assert_matches!(
3580 linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3581 Err(Error::ChunkIsAGap { .. })
3582 );
3583 }
3584
3585 #[test]
3586 fn test_lazy_previous() {
3587 use std::marker::PhantomData;
3588
3589 use super::{Ends, ObservableUpdates, Update::*};
3590
3591 let first_chunk_identifier = ChunkIdentifier(0);
3593 let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3594 unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3595
3596 let mut linked_chunk = LinkedChunk::<3, char, ()> {
3597 links: Ends { first: first_loaded_chunk, last: None },
3598 chunk_identifier_generator:
3599 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3600 updates: Some(ObservableUpdates::new()),
3601 marker: PhantomData,
3602 };
3603
3604 {
3607 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3608
3609 assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3610
3611 {
3613 let mut chunks = linked_chunk.chunks();
3614
3615 assert_matches!(chunks.next(), Some(chunk) => {
3616 assert_eq!(chunk.identifier(), 1);
3617 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3618 });
3619 assert_matches!(chunks.next(), Some(chunk) => {
3620 assert_eq!(chunk.identifier(), 2);
3621 assert!(chunk.lazy_previous.is_none());
3622 });
3623 assert!(chunks.next().is_none());
3624 }
3625
3626 assert_eq!(
3628 linked_chunk.updates().unwrap().take(),
3629 &[
3630 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3631 NewItemsChunk {
3632 previous: Some(ChunkIdentifier(1)),
3633 new: ChunkIdentifier(2),
3634 next: None,
3635 },
3636 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3637 ]
3638 );
3639 }
3640
3641 {
3643 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3644
3645 assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3646
3647 {
3649 let mut chunks = linked_chunk.chunks();
3650
3651 assert_matches!(chunks.next(), Some(chunk) => {
3652 assert_eq!(chunk.identifier(), 3);
3653 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3655 });
3656 assert_matches!(chunks.next(), Some(chunk) => {
3657 assert_eq!(chunk.identifier(), 1);
3658 assert!(chunk.lazy_previous.is_none());
3660 });
3661 assert_matches!(chunks.next(), Some(chunk) => {
3662 assert_eq!(chunk.identifier(), 2);
3663 assert!(chunk.lazy_previous.is_none());
3664 });
3665 assert!(chunks.next().is_none());
3666 }
3667
3668 assert_eq!(
3670 linked_chunk.updates().unwrap().take(),
3671 &[NewGapChunk {
3672 previous: Some(ChunkIdentifier(0)),
3674 new: ChunkIdentifier(3),
3675 next: Some(ChunkIdentifier(1)),
3676 gap: ()
3677 }]
3678 );
3679 }
3680
3681 {
3683 linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3684
3685 assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3686
3687 {
3689 let mut chunks = linked_chunk.chunks();
3690
3691 assert_matches!(chunks.next(), Some(chunk) => {
3692 assert_eq!(chunk.identifier(), 4);
3693 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3695 });
3696 assert_matches!(chunks.next(), Some(chunk) => {
3697 assert_eq!(chunk.identifier(), 5);
3698 assert!(chunk.lazy_previous.is_none());
3699 });
3700 assert_matches!(chunks.next(), Some(chunk) => {
3701 assert_eq!(chunk.identifier(), 1);
3702 assert!(chunk.lazy_previous.is_none());
3703 });
3704 assert_matches!(chunks.next(), Some(chunk) => {
3705 assert_eq!(chunk.identifier(), 2);
3706 assert!(chunk.lazy_previous.is_none());
3707 });
3708 assert!(chunks.next().is_none());
3709 }
3710
3711 assert_eq!(
3713 linked_chunk.updates().unwrap().take(),
3714 &[
3715 NewItemsChunk {
3717 previous: Some(ChunkIdentifier(3)),
3718 new: ChunkIdentifier(4),
3719 next: Some(ChunkIdentifier(1)),
3720 },
3721 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3723 NewItemsChunk {
3725 previous: Some(ChunkIdentifier(4)),
3726 new: ChunkIdentifier(5),
3727 next: Some(ChunkIdentifier(1)),
3728 },
3729 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3731 RemoveChunk(ChunkIdentifier(3)),
3733 ]
3734 );
3735 }
3736
3737 {
3741 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3742
3743 assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3744
3745 {
3747 let mut chunks = linked_chunk.chunks();
3748
3749 assert_matches!(chunks.next(), Some(chunk) => {
3750 assert_eq!(chunk.identifier(), 6);
3751 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3753 });
3754 assert_matches!(chunks.next(), Some(chunk) => {
3755 assert_eq!(chunk.identifier(), 4);
3756 assert!(chunk.lazy_previous.is_none());
3758 });
3759 assert_matches!(chunks.next(), Some(chunk) => {
3760 assert_eq!(chunk.identifier(), 5);
3761 assert!(chunk.lazy_previous.is_none());
3762 });
3763 assert_matches!(chunks.next(), Some(chunk) => {
3764 assert_eq!(chunk.identifier(), 1);
3765 assert!(chunk.lazy_previous.is_none());
3766 });
3767 assert_matches!(chunks.next(), Some(chunk) => {
3768 assert_eq!(chunk.identifier(), 2);
3769 assert!(chunk.lazy_previous.is_none());
3770 });
3771 assert!(chunks.next().is_none());
3772 }
3773
3774 assert_eq!(
3776 linked_chunk.updates().unwrap().take(),
3777 &[NewGapChunk {
3778 previous: Some(ChunkIdentifier(0)),
3780 new: ChunkIdentifier(6),
3781 next: Some(ChunkIdentifier(4)),
3782 gap: ()
3783 }]
3784 );
3785 }
3786 }
3787}