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) {
356 let mut current_chunk_ptr = self.last.or(Some(self.first));
359
360 while let Some(chunk_ptr) = current_chunk_ptr {
362 let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
364
365 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
367
368 current_chunk_ptr = previous_ptr;
370 }
371
372 self.first = NonNull::dangling();
374 self.last = None;
375 }
376
377 fn replace_with(&mut self, first_chunk: NonNull<Chunk<CAP, Item, Gap>>) {
380 unsafe {
382 self.clear();
383 }
384
385 self.first = first_chunk;
387 }
388
389 fn reset(&mut self) {
394 self.replace_with(Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER));
395 }
396}
397
398pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
405 links: Ends<CHUNK_CAPACITY, Item, Gap>,
407
408 chunk_identifier_generator: ChunkIdentifierGenerator,
410
411 updates: Option<ObservableUpdates<Item, Gap>>,
415
416 marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
418}
419
420impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
421 fn default() -> Self {
422 Self::new()
423 }
424}
425
426impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
427 pub fn new() -> Self {
429 Self {
430 links: Ends {
431 first: Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER),
432 last: None,
433 },
434 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
435 updates: None,
436 marker: PhantomData,
437 }
438 }
439
440 pub fn new_with_update_history() -> Self {
446 let first_chunk_identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
447
448 let mut updates = ObservableUpdates::new();
449 updates.push(Update::NewItemsChunk {
450 previous: None,
451 new: first_chunk_identifier,
452 next: None,
453 });
454
455 Self {
456 links: Ends { first: Chunk::new_items_leaked(first_chunk_identifier), last: None },
457 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
458 updates: Some(updates),
459 marker: PhantomData,
460 }
461 }
462
463 pub fn clear(&mut self) {
465 self.links.reset();
467
468 self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
470
471 if let Some(updates) = self.updates.as_mut() {
473 updates.clear_pending();
476 updates.push(Update::Clear);
477 updates.push(Update::NewItemsChunk {
478 previous: None,
479 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
480 next: None,
481 })
482 }
483 }
484
485 pub fn push_items_back<I>(&mut self, items: I)
491 where
492 Item: Clone,
493 Gap: Clone,
494 I: IntoIterator<Item = Item>,
495 I::IntoIter: ExactSizeIterator,
496 {
497 let items = items.into_iter();
498
499 let last_chunk = self.links.latest_chunk_mut();
500
501 let last_chunk =
503 last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
504
505 debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
506
507 if !last_chunk.is_first_chunk() {
511 self.links.last = Some(last_chunk.as_ptr());
514 }
515 }
516
517 pub fn push_gap_back(&mut self, content: Gap)
520 where
521 Item: Clone,
522 Gap: Clone,
523 {
524 let last_chunk = self.links.latest_chunk_mut();
525 last_chunk.insert_next(
526 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
527 &mut self.updates,
528 );
529
530 self.links.last = last_chunk.next;
531 }
532
533 pub fn insert_items_at<I>(&mut self, position: Position, items: I) -> Result<(), Error>
538 where
539 Item: Clone,
540 Gap: Clone,
541 I: IntoIterator<Item = Item>,
542 I::IntoIter: ExactSizeIterator,
543 {
544 let chunk_identifier = position.chunk_identifier();
545 let item_index = position.index();
546
547 let chunk = self
548 .links
549 .chunk_mut(chunk_identifier)
550 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
551
552 let chunk = match &mut chunk.content {
553 ChunkContent::Gap(..) => {
554 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
555 }
556
557 ChunkContent::Items(current_items) => {
558 let current_items_length = current_items.len();
559
560 if item_index > current_items_length {
561 return Err(Error::InvalidItemIndex { index: item_index });
562 }
563
564 let items = items.into_iter();
566
567 if item_index == current_items_length {
569 chunk
570 .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
572 }
573 else {
575 if let Some(updates) = self.updates.as_mut() {
576 updates.push(Update::DetachLastItems {
577 at: Position(chunk_identifier, item_index),
578 });
579 }
580
581 let detached_items = current_items.split_off(item_index);
583
584 let chunk = chunk
585 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
587
588 if let Some(updates) = self.updates.as_mut() {
589 updates.push(Update::StartReattachItems);
590 }
591
592 let chunk = chunk
593 .push_items(
595 detached_items.into_iter(),
596 &self.chunk_identifier_generator,
597 &mut self.updates,
598 );
599
600 if let Some(updates) = self.updates.as_mut() {
601 updates.push(Update::EndReattachItems);
602 }
603
604 chunk
605 }
606 }
607 };
608
609 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
612 self.links.last = Some(chunk.as_ptr());
615 }
616
617 Ok(())
618 }
619
620 pub fn remove_item_at(&mut self, position: Position) -> Result<Item, Error> {
628 let chunk_identifier = position.chunk_identifier();
629 let item_index = position.index();
630
631 let mut chunk_ptr = None;
632 let removed_item;
633
634 {
635 let chunk = self
636 .links
637 .chunk_mut(chunk_identifier)
638 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
639
640 let current_items = match &mut chunk.content {
641 ChunkContent::Gap(..) => {
642 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
643 }
644 ChunkContent::Items(current_items) => current_items,
645 };
646
647 if item_index >= current_items.len() {
648 return Err(Error::InvalidItemIndex { index: item_index });
649 }
650
651 removed_item = current_items.remove(item_index);
652
653 if let Some(updates) = self.updates.as_mut() {
654 updates.push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
655 }
656
657 if current_items.is_empty() && !chunk.is_first_chunk() {
659 chunk.unlink(self.updates.as_mut());
661
662 chunk_ptr = Some(chunk.as_ptr());
663
664 if chunk.is_last_chunk() {
667 self.links.last = chunk.previous;
668 }
669 }
670
671 }
673
674 if let Some(chunk_ptr) = chunk_ptr {
675 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
682 }
683
684 Ok(removed_item)
685 }
686
687 pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
692 where
693 Item: Clone,
694 {
695 let chunk_identifier = position.chunk_identifier();
696 let item_index = position.index();
697
698 let chunk = self
699 .links
700 .chunk_mut(chunk_identifier)
701 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
702
703 match &mut chunk.content {
704 ChunkContent::Gap(..) => {
705 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
706 }
707
708 ChunkContent::Items(current_items) => {
709 if item_index >= current_items.len() {
710 return Err(Error::InvalidItemIndex { index: item_index });
711 }
712
713 if let Some(updates) = self.updates.as_mut() {
715 updates.push(Update::ReplaceItem {
716 at: Position(chunk_identifier, item_index),
717 item: item.clone(),
718 });
719 }
720
721 current_items[item_index] = item;
722 }
723 }
724
725 Ok(())
726 }
727
728 pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
733 where
734 Item: Clone,
735 Gap: Clone,
736 {
737 let chunk_identifier = position.chunk_identifier();
738 let item_index = position.index();
739
740 let chunk = self
741 .links
742 .chunk_mut(chunk_identifier)
743 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
744
745 let chunk = match &mut chunk.content {
746 ChunkContent::Gap(..) => {
747 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
748 }
749
750 ChunkContent::Items(current_items) => {
751 if item_index == 0 {
755 let chunk_was_first = chunk.is_first_chunk();
756 let chunk_was_last = chunk.is_last_chunk();
757
758 let new_chunk = chunk.insert_before(
759 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
760 self.updates.as_mut(),
761 );
762
763 let new_chunk_ptr = new_chunk.as_ptr();
764 let chunk_ptr = chunk.as_ptr();
765
766 if chunk_was_first {
771 self.links.first = new_chunk_ptr;
772
773 if chunk_was_last {
775 self.links.last = Some(chunk_ptr);
776 }
777 }
778
779 return Ok(());
780 }
781
782 let current_items_length = current_items.len();
783
784 if item_index >= current_items_length {
785 return Err(Error::InvalidItemIndex { index: item_index });
786 }
787
788 if let Some(updates) = self.updates.as_mut() {
789 updates.push(Update::DetachLastItems {
790 at: Position(chunk_identifier, item_index),
791 });
792 }
793
794 let detached_items = current_items.split_off(item_index);
796
797 let chunk = chunk
798 .insert_next(
800 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
801 &mut self.updates,
802 );
803
804 if let Some(updates) = self.updates.as_mut() {
805 updates.push(Update::StartReattachItems);
806 }
807
808 let chunk = chunk
809 .insert_next(
811 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
812 &mut self.updates,
813 )
814 .push_items(
816 detached_items.into_iter(),
817 &self.chunk_identifier_generator,
818 &mut self.updates,
819 );
820
821 if let Some(updates) = self.updates.as_mut() {
822 updates.push(Update::EndReattachItems);
823 }
824
825 chunk
826 }
827 };
828
829 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
832 self.links.last = Some(chunk.as_ptr());
835 }
836
837 Ok(())
838 }
839
840 pub fn remove_empty_chunk_at(
849 &mut self,
850 chunk_identifier: ChunkIdentifier,
851 ) -> Result<Option<Position>, Error> {
852 if self.links.first_chunk().is_last_chunk() {
854 return Err(Error::RemovingLastChunk);
855 }
856
857 let chunk = self
858 .links
859 .chunk_mut(chunk_identifier)
860 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
861
862 if chunk.num_items() > 0 {
863 return Err(Error::RemovingNonEmptyItemsChunk { identifier: chunk_identifier });
864 }
865
866 let chunk_was_first = chunk.is_first_chunk();
867 let chunk_was_last = chunk.is_last_chunk();
868 let next_ptr = chunk.next;
869 let previous_ptr = chunk.previous;
870 let position_of_next = chunk.next().map(|next| next.first_position());
871
872 chunk.unlink(self.updates.as_mut());
873
874 let chunk_ptr = chunk.as_ptr();
875
876 if chunk_was_first {
878 if let Some(next_ptr) = next_ptr {
880 self.links.first = next_ptr;
881 }
882 }
883
884 if chunk_was_last {
885 self.links.last = previous_ptr;
886 }
887
888 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
891
892 Ok(position_of_next)
894 }
895
896 pub fn replace_gap_at<I>(
904 &mut self,
905 items: I,
906 chunk_identifier: ChunkIdentifier,
907 ) -> Result<&Chunk<CAP, Item, Gap>, Error>
908 where
909 Item: Clone,
910 Gap: Clone,
911 I: IntoIterator<Item = Item>,
912 I::IntoIter: ExactSizeIterator,
913 {
914 let chunk_ptr;
915 let new_chunk_ptr;
916
917 {
918 let chunk = self
919 .links
920 .chunk_mut(chunk_identifier)
921 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
922
923 if chunk.is_items() {
924 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
925 }
926
927 let chunk_was_first = chunk.is_first_chunk();
928
929 let maybe_last_chunk_ptr = {
930 let items = items.into_iter();
931
932 let last_inserted_chunk = chunk
933 .insert_next(
935 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
936 &mut self.updates,
937 )
938 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
940
941 last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
942 };
943
944 new_chunk_ptr = chunk
945 .next
946 .unwrap();
948
949 chunk.unlink(self.updates.as_mut());
951
952 chunk_ptr = chunk.as_ptr();
954
955 if chunk_was_first {
957 self.links.first = new_chunk_ptr;
958 }
959
960 if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
963 self.links.last = Some(last_chunk_ptr);
964 }
965
966 }
968
969 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
974
975 Ok(
976 unsafe { new_chunk_ptr.as_ref() },
980 )
981 }
982
983 pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
985 where
986 P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
987 {
988 self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
989 }
990
991 pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
993 where
994 P: FnMut(&'a Item) -> bool,
995 {
996 self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
997 }
998
999 pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
1003 IterBackward::new(self.links.latest_chunk())
1004 }
1005
1006 pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
1010 Iter::new(self.links.first_chunk())
1011 }
1012
1013 pub fn rchunks_from(
1018 &self,
1019 identifier: ChunkIdentifier,
1020 ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
1021 Ok(IterBackward::new(
1022 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
1023 ))
1024 }
1025
1026 pub fn chunks_from(
1031 &self,
1032 identifier: ChunkIdentifier,
1033 ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
1034 Ok(Iter::new(
1035 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
1036 ))
1037 }
1038
1039 pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
1043 self.ritems_from(self.links.latest_chunk().last_position())
1044 .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
1045 }
1046
1047 pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
1051 let first_chunk = self.links.first_chunk();
1052
1053 self.items_from(first_chunk.first_position())
1054 .expect("`items` cannot fail because at least one empty chunk must exist")
1055 }
1056
1057 pub fn ritems_from(
1061 &self,
1062 position: Position,
1063 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1064 Ok(self
1065 .rchunks_from(position.chunk_identifier())?
1066 .filter_map(|chunk| match &chunk.content {
1067 ChunkContent::Gap(..) => None,
1068 ChunkContent::Items(items) => {
1069 let identifier = chunk.identifier();
1070
1071 Some(
1072 items.iter().enumerate().rev().map(move |(item_index, item)| {
1073 (Position(identifier, item_index), item)
1074 }),
1075 )
1076 }
1077 })
1078 .flatten()
1079 .skip_while({
1080 let expected_index = position.index();
1081
1082 move |(Position(chunk_identifier, item_index), _item)| {
1083 *chunk_identifier == position.chunk_identifier()
1084 && *item_index != expected_index
1085 }
1086 }))
1087 }
1088
1089 pub fn items_from(
1093 &self,
1094 position: Position,
1095 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1096 Ok(self
1097 .chunks_from(position.chunk_identifier())?
1098 .filter_map(|chunk| match &chunk.content {
1099 ChunkContent::Gap(..) => None,
1100 ChunkContent::Items(items) => {
1101 let identifier = chunk.identifier();
1102
1103 Some(
1104 items.iter().enumerate().map(move |(item_index, item)| {
1105 (Position(identifier, item_index), item)
1106 }),
1107 )
1108 }
1109 })
1110 .flatten()
1111 .skip(position.index()))
1112 }
1113
1114 pub fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
1116 self.links.first_chunk()
1117 }
1118
1119 #[must_use]
1131 pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
1132 self.updates.as_mut()
1133 }
1134
1135 pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
1142 let (updates, token) = self
1143 .updates
1144 .as_mut()
1145 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1146 let chunk_iterator = self.chunks();
1147
1148 Some(AsVector::new(updates, token, chunk_iterator))
1149 }
1150
1151 pub fn order_tracker(
1159 &mut self,
1160 all_chunks: Option<Vec<ChunkMetadata>>,
1161 ) -> Option<OrderTracker<Item, Gap>>
1162 where
1163 Item: Clone,
1164 {
1165 let (updates, token) = self
1166 .updates
1167 .as_mut()
1168 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1169
1170 Some(OrderTracker::new(
1171 updates,
1172 token,
1173 all_chunks.unwrap_or_else(|| {
1174 self.chunks()
1176 .map(|chunk| ChunkMetadata {
1177 identifier: chunk.identifier(),
1178 num_items: chunk.num_items(),
1179 previous: chunk.previous().map(|prev| prev.identifier()),
1180 next: chunk.next().map(|next| next.identifier()),
1181 })
1182 .collect()
1183 }),
1184 ))
1185 }
1186
1187 pub fn num_items(&self) -> usize {
1189 self.items().count()
1190 }
1191}
1192
1193impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
1194 fn drop(&mut self) {
1195 unsafe {
1205 self.links.clear();
1206 }
1207 }
1208}
1209
1210unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1214
1215unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1219
1220#[derive(Debug)]
1230pub struct ChunkIdentifierGenerator {
1231 next: AtomicU64,
1232}
1233
1234impl ChunkIdentifierGenerator {
1235 const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1237
1238 pub fn new_from_scratch() -> Self {
1241 Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1242 }
1243
1244 pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1247 Self { next: AtomicU64::new(last_chunk_identifier.0) }
1248 }
1249
1250 fn next(&self) -> ChunkIdentifier {
1255 let previous = self.next.fetch_add(1, atomic::Ordering::Relaxed);
1256
1257 if previous == u64::MAX {
1260 panic!(
1261 "No more chunk identifiers available. Congrats, you did it. \
1262 2^64 identifiers have been consumed."
1263 )
1264 }
1265
1266 ChunkIdentifier(previous + 1)
1267 }
1268
1269 #[doc(hidden)]
1273 pub fn current(&self) -> ChunkIdentifier {
1274 ChunkIdentifier(self.next.load(atomic::Ordering::Relaxed))
1275 }
1276}
1277
1278#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1284#[repr(transparent)]
1285pub struct ChunkIdentifier(u64);
1286
1287impl ChunkIdentifier {
1288 pub fn new(identifier: u64) -> Self {
1290 Self(identifier)
1291 }
1292
1293 pub fn index(&self) -> u64 {
1295 self.0
1296 }
1297}
1298
1299impl PartialEq<u64> for ChunkIdentifier {
1300 fn eq(&self, other: &u64) -> bool {
1301 self.0 == *other
1302 }
1303}
1304
1305#[derive(Copy, Clone, Debug, PartialEq)]
1309pub struct Position(ChunkIdentifier, usize);
1310
1311impl Position {
1312 pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1314 Self(chunk_identifier, index)
1315 }
1316
1317 pub fn chunk_identifier(&self) -> ChunkIdentifier {
1319 self.0
1320 }
1321
1322 pub fn index(&self) -> usize {
1324 self.1
1325 }
1326
1327 pub fn decrement_index(&mut self) {
1333 self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1334 }
1335
1336 pub fn increment_index(&mut self) {
1343 self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1344 }
1345}
1346
1347#[derive(Debug)]
1350pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1351 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1352}
1353
1354impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1355 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1357 Self { chunk: Some(from_chunk) }
1358 }
1359}
1360
1361impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1362 type Item = &'a Chunk<CAP, Item, Gap>;
1363
1364 fn next(&mut self) -> Option<Self::Item> {
1365 self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1366 }
1367}
1368
1369#[derive(Debug)]
1372pub struct Iter<'a, const CAP: usize, Item, Gap> {
1373 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1374}
1375
1376impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1377 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1379 Self { chunk: Some(from_chunk) }
1380 }
1381}
1382
1383impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1384 type Item = &'a Chunk<CAP, Item, Gap>;
1385
1386 fn next(&mut self) -> Option<Self::Item> {
1387 self.chunk.inspect(|chunk| self.chunk = chunk.next())
1388 }
1389}
1390
1391#[derive(Clone, Debug)]
1393pub enum ChunkContent<Item, Gap> {
1394 Gap(Gap),
1397
1398 Items(Vec<Item>),
1400}
1401
1402pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1404 previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1406
1407 lazy_previous: Option<ChunkIdentifier>,
1412
1413 next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1415
1416 identifier: ChunkIdentifier,
1418
1419 content: ChunkContent<Item, Gap>,
1421}
1422
1423impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1424 fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1426 Self::new(identifier, ChunkContent::Gap(content))
1427 }
1428
1429 fn new_items(identifier: ChunkIdentifier) -> Self {
1431 Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1432 }
1433
1434 fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1435 Self { previous: None, lazy_previous: None, next: None, identifier, content }
1436 }
1437
1438 fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1440 let chunk = Self::new(identifier, content);
1441 let chunk_box = Box::new(chunk);
1442
1443 NonNull::from(Box::leak(chunk_box))
1444 }
1445
1446 fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1448 let chunk = Self::new_gap(identifier, content);
1449 let chunk_box = Box::new(chunk);
1450
1451 NonNull::from(Box::leak(chunk_box))
1452 }
1453
1454 fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1456 let chunk = Self::new_items(identifier);
1457 let chunk_box = Box::new(chunk);
1458
1459 NonNull::from(Box::leak(chunk_box))
1460 }
1461
1462 pub fn as_ptr(&self) -> NonNull<Self> {
1464 NonNull::from(self)
1465 }
1466
1467 pub fn is_gap(&self) -> bool {
1469 matches!(self.content, ChunkContent::Gap(..))
1470 }
1471
1472 pub fn is_items(&self) -> bool {
1474 !self.is_gap()
1475 }
1476
1477 pub fn is_definitive_head(&self) -> bool {
1480 self.previous.is_none() && self.lazy_previous.is_none()
1481 }
1482
1483 fn is_first_chunk(&self) -> bool {
1485 self.previous.is_none()
1486 }
1487
1488 fn is_last_chunk(&self) -> bool {
1490 self.next.is_none()
1491 }
1492
1493 #[doc(hidden)]
1497 pub fn lazy_previous(&self) -> Option<ChunkIdentifier> {
1498 self.lazy_previous
1499 }
1500
1501 pub fn identifier(&self) -> ChunkIdentifier {
1503 self.identifier
1504 }
1505
1506 pub fn content(&self) -> &ChunkContent<Item, Gap> {
1508 &self.content
1509 }
1510
1511 pub fn first_position(&self) -> Position {
1515 Position(self.identifier(), 0)
1516 }
1517
1518 pub fn last_position(&self) -> Position {
1522 let identifier = self.identifier();
1523
1524 match &self.content {
1525 ChunkContent::Gap(..) => Position(identifier, 0),
1526 ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1527 }
1528 }
1529
1530 pub fn num_items(&self) -> usize {
1534 match &self.content {
1535 ChunkContent::Gap(..) => 0,
1536 ChunkContent::Items(items) => items.len(),
1537 }
1538 }
1539
1540 fn push_items<I>(
1552 &mut self,
1553 mut new_items: I,
1554 chunk_identifier_generator: &ChunkIdentifierGenerator,
1555 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1556 ) -> &mut Self
1557 where
1558 I: Iterator<Item = Item> + ExactSizeIterator,
1559 Item: Clone,
1560 Gap: Clone,
1561 {
1562 if new_items.len() == 0 {
1564 return self;
1565 }
1566
1567 let identifier = self.identifier();
1568 let prev_num_items = self.num_items();
1569
1570 match &mut self.content {
1571 ChunkContent::Gap(..) => {
1574 self
1575 .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1577 .push_items(new_items, chunk_identifier_generator, updates)
1580 }
1581
1582 ChunkContent::Items(items) => {
1583 let free_space = CAPACITY.saturating_sub(prev_num_items);
1585
1586 if new_items.len() <= free_space {
1588 let start = items.len();
1589 items.extend(new_items);
1590
1591 if let Some(updates) = updates.as_mut() {
1592 updates.push(Update::PushItems {
1593 at: Position(identifier, start),
1594 items: items[start..].to_vec(),
1595 });
1596 }
1597
1598 self
1600 } else {
1601 if free_space > 0 {
1602 let start = items.len();
1604 items.extend(new_items.by_ref().take(free_space));
1605
1606 if let Some(updates) = updates.as_mut() {
1607 updates.push(Update::PushItems {
1608 at: Position(identifier, start),
1609 items: items[start..].to_vec(),
1610 });
1611 }
1612 }
1613
1614 self
1615 .insert_next(
1617 Self::new_items_leaked(chunk_identifier_generator.next()),
1618 updates,
1619 )
1620 .push_items(new_items, chunk_identifier_generator, updates)
1623 }
1624 }
1625 }
1626 }
1627
1628 fn insert_next(
1633 &mut self,
1634 mut new_chunk_ptr: NonNull<Self>,
1635 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1636 ) -> &mut Self
1637 where
1638 Gap: Clone,
1639 {
1640 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1641
1642 if let Some(next_chunk) = self.next_mut() {
1644 next_chunk.previous = Some(new_chunk_ptr);
1646
1647 new_chunk.next = self.next;
1649 }
1650
1651 self.next = Some(new_chunk_ptr);
1653 new_chunk.previous = Some(self.as_ptr());
1655
1656 if let Some(updates) = updates.as_mut() {
1657 let previous = new_chunk.previous().map(Chunk::identifier);
1658 let new = new_chunk.identifier();
1659 let next = new_chunk.next().map(Chunk::identifier);
1660
1661 match new_chunk.content() {
1662 ChunkContent::Gap(gap) => {
1663 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1664 }
1665
1666 ChunkContent::Items(..) => {
1667 updates.push(Update::NewItemsChunk { previous, new, next })
1668 }
1669 }
1670 }
1671
1672 new_chunk
1673 }
1674
1675 fn insert_before(
1680 &mut self,
1681 mut new_chunk_ptr: NonNull<Self>,
1682 updates: Option<&mut ObservableUpdates<Item, Gap>>,
1683 ) -> &mut Self
1684 where
1685 Gap: Clone,
1686 {
1687 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1688
1689 if let Some(previous_chunk) = self.previous_mut() {
1691 previous_chunk.next = Some(new_chunk_ptr);
1693
1694 new_chunk.previous = self.previous;
1696 }
1697 else {
1700 new_chunk.lazy_previous = self.lazy_previous.take();
1701 }
1702
1703 self.previous = Some(new_chunk_ptr);
1705 new_chunk.next = Some(self.as_ptr());
1707
1708 if let Some(updates) = updates {
1709 let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1710 let new = new_chunk.identifier();
1711 let next = new_chunk.next().map(Chunk::identifier);
1712
1713 match new_chunk.content() {
1714 ChunkContent::Gap(gap) => {
1715 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1716 }
1717
1718 ChunkContent::Items(..) => {
1719 updates.push(Update::NewItemsChunk { previous, new, next })
1720 }
1721 }
1722 }
1723
1724 new_chunk
1725 }
1726
1727 fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1732 let previous_ptr = self.previous;
1733 let next_ptr = self.next;
1734 let lazy_previous = self.lazy_previous.take();
1738
1739 if let Some(previous) = self.previous_mut() {
1740 previous.next = next_ptr;
1741 }
1742
1743 if let Some(next) = self.next_mut() {
1744 next.previous = previous_ptr;
1745 next.lazy_previous = lazy_previous;
1746 }
1747
1748 if let Some(updates) = updates {
1749 updates.push(Update::RemoveChunk(self.identifier()));
1750 }
1751 }
1752
1753 fn previous(&self) -> Option<&Self> {
1755 self.previous.map(|non_null| unsafe { non_null.as_ref() })
1756 }
1757
1758 fn previous_mut(&mut self) -> Option<&mut Self> {
1760 self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1761 }
1762
1763 fn next(&self) -> Option<&Self> {
1765 self.next.map(|non_null| unsafe { non_null.as_ref() })
1766 }
1767
1768 fn next_mut(&mut self) -> Option<&mut Self> {
1770 self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1771 }
1772}
1773
1774impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<CAP, Item, Gap>
1775where
1776 Item: fmt::Debug,
1777 Gap: fmt::Debug,
1778{
1779 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1780 formatter
1781 .debug_struct("LinkedChunk")
1782 .field("first (deref)", unsafe { self.links.first.as_ref() })
1783 .field("last", &self.links.last)
1784 .finish_non_exhaustive()
1785 }
1786}
1787
1788impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1789where
1790 Item: fmt::Debug,
1791 Gap: fmt::Debug,
1792{
1793 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1794 formatter
1795 .debug_struct("Chunk")
1796 .field("identifier", &self.identifier)
1797 .field("content", &self.content)
1798 .field("previous", &self.previous)
1799 .field("ptr", &std::ptr::from_ref(self))
1800 .field("next", &self.next)
1801 .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1802 .finish()
1803 }
1804}
1805
1806#[derive(Clone, Debug)]
1812pub struct RawChunk<Item, Gap> {
1813 pub content: ChunkContent<Item, Gap>,
1815
1816 pub previous: Option<ChunkIdentifier>,
1818
1819 pub identifier: ChunkIdentifier,
1821
1822 pub next: Option<ChunkIdentifier>,
1824}
1825
1826#[derive(Clone, Debug)]
1829pub struct ChunkMetadata {
1830 pub num_items: usize,
1834
1835 pub previous: Option<ChunkIdentifier>,
1837
1838 pub identifier: ChunkIdentifier,
1840
1841 pub next: Option<ChunkIdentifier>,
1843}
1844
1845#[cfg(test)]
1846mod tests {
1847 use std::{
1848 ops::Not,
1849 sync::{Arc, atomic::Ordering},
1850 };
1851
1852 use assert_matches::assert_matches;
1853
1854 use super::{
1855 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Error, LinkedChunk,
1856 Position,
1857 };
1858
1859 #[test]
1860 fn test_chunk_identifier_generator() {
1861 let generator = ChunkIdentifierGenerator::new_from_scratch();
1862
1863 assert_eq!(generator.next(), ChunkIdentifier(1));
1864 assert_eq!(generator.next(), ChunkIdentifier(2));
1865 assert_eq!(generator.next(), ChunkIdentifier(3));
1866 assert_eq!(generator.next(), ChunkIdentifier(4));
1867
1868 let generator =
1869 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1870
1871 assert_eq!(generator.next(), ChunkIdentifier(43));
1872 assert_eq!(generator.next(), ChunkIdentifier(44));
1873 assert_eq!(generator.next(), ChunkIdentifier(45));
1874 assert_eq!(generator.next(), ChunkIdentifier(46));
1875 }
1876
1877 #[test]
1878 fn test_empty() {
1879 let items = LinkedChunk::<3, char, ()>::new();
1880
1881 assert_eq!(items.num_items(), 0);
1882
1883 }
1886
1887 #[test]
1888 fn test_updates() {
1889 assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1890 assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1891 }
1892
1893 #[test]
1894 fn test_new_with_initial_update() {
1895 use super::Update::*;
1896
1897 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1898
1899 assert_eq!(
1900 linked_chunk.updates().unwrap().take(),
1901 &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1902 );
1903 }
1904
1905 #[test]
1906 fn test_push_items() {
1907 use super::Update::*;
1908
1909 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1910
1911 let _ = linked_chunk.updates().unwrap().take();
1913
1914 linked_chunk.push_items_back(['a']);
1915
1916 assert_items_eq!(linked_chunk, ['a']);
1917 assert_eq!(
1918 linked_chunk.updates().unwrap().take(),
1919 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1920 );
1921
1922 linked_chunk.push_items_back(['b', 'c']);
1923 assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1924 assert_eq!(
1925 linked_chunk.updates().unwrap().take(),
1926 &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1927 );
1928
1929 linked_chunk.push_items_back(['d', 'e']);
1930 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
1931 assert_eq!(
1932 linked_chunk.updates().unwrap().take(),
1933 &[
1934 NewItemsChunk {
1935 previous: Some(ChunkIdentifier(0)),
1936 new: ChunkIdentifier(1),
1937 next: None
1938 },
1939 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1940 ]
1941 );
1942
1943 linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1944 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1945 assert_eq!(
1946 linked_chunk.updates().unwrap().take(),
1947 &[
1948 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1949 NewItemsChunk {
1950 previous: Some(ChunkIdentifier(1)),
1951 new: ChunkIdentifier(2),
1952 next: None,
1953 },
1954 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1955 NewItemsChunk {
1956 previous: Some(ChunkIdentifier(2)),
1957 new: ChunkIdentifier(3),
1958 next: None,
1959 },
1960 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1961 ]
1962 );
1963
1964 assert_eq!(linked_chunk.num_items(), 10);
1965 }
1966
1967 #[test]
1968 fn test_push_gap() {
1969 use super::Update::*;
1970
1971 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1972
1973 let _ = linked_chunk.updates().unwrap().take();
1975
1976 linked_chunk.push_items_back(['a']);
1977 assert_items_eq!(linked_chunk, ['a']);
1978 assert_eq!(
1979 linked_chunk.updates().unwrap().take(),
1980 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1981 );
1982
1983 linked_chunk.push_gap_back(());
1984 assert_items_eq!(linked_chunk, ['a'] [-]);
1985 assert_eq!(
1986 linked_chunk.updates().unwrap().take(),
1987 &[NewGapChunk {
1988 previous: Some(ChunkIdentifier(0)),
1989 new: ChunkIdentifier(1),
1990 next: None,
1991 gap: (),
1992 }]
1993 );
1994
1995 linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1996 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1997 assert_eq!(
1998 linked_chunk.updates().unwrap().take(),
1999 &[
2000 NewItemsChunk {
2001 previous: Some(ChunkIdentifier(1)),
2002 new: ChunkIdentifier(2),
2003 next: None,
2004 },
2005 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
2006 NewItemsChunk {
2007 previous: Some(ChunkIdentifier(2)),
2008 new: ChunkIdentifier(3),
2009 next: None,
2010 },
2011 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
2012 ]
2013 );
2014
2015 linked_chunk.push_gap_back(());
2016 linked_chunk.push_gap_back(()); assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
2018 assert_eq!(
2019 linked_chunk.updates().unwrap().take(),
2020 &[
2021 NewGapChunk {
2022 previous: Some(ChunkIdentifier(3)),
2023 new: ChunkIdentifier(4),
2024 next: None,
2025 gap: (),
2026 },
2027 NewGapChunk {
2028 previous: Some(ChunkIdentifier(4)),
2029 new: ChunkIdentifier(5),
2030 next: None,
2031 gap: (),
2032 }
2033 ]
2034 );
2035
2036 linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
2037 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
2038 assert_eq!(
2039 linked_chunk.updates().unwrap().take(),
2040 &[
2041 NewItemsChunk {
2042 previous: Some(ChunkIdentifier(5)),
2043 new: ChunkIdentifier(6),
2044 next: None,
2045 },
2046 PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
2047 NewItemsChunk {
2048 previous: Some(ChunkIdentifier(6)),
2049 new: ChunkIdentifier(7),
2050 next: None,
2051 },
2052 PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
2053 ]
2054 );
2055
2056 assert_eq!(linked_chunk.num_items(), 9);
2057 }
2058
2059 #[test]
2060 fn test_identifiers_and_positions() {
2061 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2062 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2063 linked_chunk.push_gap_back(());
2064 linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
2065 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
2066
2067 assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
2068 assert_eq!(
2069 linked_chunk.item_position(|item| *item == 'e'),
2070 Some(Position(ChunkIdentifier(1), 1))
2071 );
2072 }
2073
2074 #[test]
2075 fn test_rchunks() {
2076 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2077 linked_chunk.push_items_back(['a', 'b']);
2078 linked_chunk.push_gap_back(());
2079 linked_chunk.push_items_back(['c', 'd', 'e']);
2080
2081 let mut iterator = linked_chunk.rchunks();
2082
2083 assert_matches!(
2084 iterator.next(),
2085 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2086 assert_eq!(items, &['e']);
2087 }
2088 );
2089 assert_matches!(
2090 iterator.next(),
2091 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2092 assert_eq!(items, &['c', 'd']);
2093 }
2094 );
2095 assert_matches!(
2096 iterator.next(),
2097 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2098 );
2099 assert_matches!(
2100 iterator.next(),
2101 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2102 assert_eq!(items, &['a', 'b']);
2103 }
2104 );
2105 assert_matches!(iterator.next(), None);
2106 }
2107
2108 #[test]
2109 fn test_chunks() {
2110 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2111 linked_chunk.push_items_back(['a', 'b']);
2112 linked_chunk.push_gap_back(());
2113 linked_chunk.push_items_back(['c', 'd', 'e']);
2114
2115 let mut iterator = linked_chunk.chunks();
2116
2117 assert_matches!(
2118 iterator.next(),
2119 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2120 assert_eq!(items, &['a', 'b']);
2121 }
2122 );
2123 assert_matches!(
2124 iterator.next(),
2125 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2126 );
2127 assert_matches!(
2128 iterator.next(),
2129 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2130 assert_eq!(items, &['c', 'd']);
2131 }
2132 );
2133 assert_matches!(
2134 iterator.next(),
2135 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2136 assert_eq!(items, &['e']);
2137 }
2138 );
2139 assert_matches!(iterator.next(), None);
2140 }
2141
2142 #[test]
2143 fn test_rchunks_from() -> Result<(), Error> {
2144 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2145 linked_chunk.push_items_back(['a', 'b']);
2146 linked_chunk.push_gap_back(());
2147 linked_chunk.push_items_back(['c', 'd', 'e']);
2148
2149 let mut iterator = linked_chunk.rchunks_from(
2150 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2151 )?;
2152
2153 assert_matches!(
2154 iterator.next(),
2155 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2156 assert_eq!(items, &['c', 'd']);
2157 }
2158 );
2159 assert_matches!(
2160 iterator.next(),
2161 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2162 );
2163 assert_matches!(
2164 iterator.next(),
2165 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2166 assert_eq!(items, &['a', 'b']);
2167 }
2168 );
2169 assert_matches!(iterator.next(), None);
2170
2171 Ok(())
2172 }
2173
2174 #[test]
2175 fn test_chunks_from() -> Result<(), Error> {
2176 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2177 linked_chunk.push_items_back(['a', 'b']);
2178 linked_chunk.push_gap_back(());
2179 linked_chunk.push_items_back(['c', 'd', 'e']);
2180
2181 let mut iterator = linked_chunk.chunks_from(
2182 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2183 )?;
2184
2185 assert_matches!(
2186 iterator.next(),
2187 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2188 assert_eq!(items, &['c', 'd']);
2189 }
2190 );
2191 assert_matches!(
2192 iterator.next(),
2193 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2194 assert_eq!(items, &['e']);
2195 }
2196 );
2197 assert_matches!(iterator.next(), None);
2198
2199 Ok(())
2200 }
2201
2202 #[test]
2203 fn test_ritems() {
2204 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2205 linked_chunk.push_items_back(['a', 'b']);
2206 linked_chunk.push_gap_back(());
2207 linked_chunk.push_items_back(['c', 'd', 'e']);
2208
2209 let mut iterator = linked_chunk.ritems();
2210
2211 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2212 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2213 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2214 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2215 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2216 assert_matches!(iterator.next(), None);
2217 }
2218
2219 #[test]
2220 fn test_ritems_with_final_gap() -> Result<(), Error> {
2221 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2222 linked_chunk.push_items_back(['a', 'b']);
2223 linked_chunk.push_gap_back(());
2224 linked_chunk.push_items_back(['c', 'd', 'e']);
2225 linked_chunk.push_gap_back(());
2226
2227 let mut iterator = linked_chunk.ritems();
2228
2229 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
2230 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2231 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2232 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2233 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2234 assert_matches!(iterator.next(), None);
2235
2236 Ok(())
2237 }
2238
2239 #[test]
2240 fn test_ritems_empty() {
2241 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2242 let mut iterator = linked_chunk.ritems();
2243
2244 assert_matches!(iterator.next(), None);
2245 }
2246
2247 #[test]
2248 fn test_items() {
2249 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2250 linked_chunk.push_items_back(['a', 'b']);
2251 linked_chunk.push_gap_back(());
2252 linked_chunk.push_items_back(['c', 'd', 'e']);
2253
2254 let mut iterator = linked_chunk.items();
2255
2256 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2257 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2258 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2259 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2260 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2261 assert_matches!(iterator.next(), None);
2262 }
2263
2264 #[test]
2265 fn test_items_empty() {
2266 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2267 let mut iterator = linked_chunk.items();
2268
2269 assert_matches!(iterator.next(), None);
2270 }
2271
2272 #[test]
2273 fn test_ritems_from() -> Result<(), Error> {
2274 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2275 linked_chunk.push_items_back(['a', 'b']);
2276 linked_chunk.push_gap_back(());
2277 linked_chunk.push_items_back(['c', 'd', 'e']);
2278
2279 let mut iterator =
2280 linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2281
2282 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2283 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2284 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2285 assert_matches!(iterator.next(), None);
2286
2287 Ok(())
2288 }
2289
2290 #[test]
2291 fn test_items_from() -> Result<(), Error> {
2292 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2293 linked_chunk.push_items_back(['a', 'b']);
2294 linked_chunk.push_gap_back(());
2295 linked_chunk.push_items_back(['c', 'd', 'e']);
2296
2297 let mut iterator =
2298 linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2299
2300 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2301 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2302 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2303 assert_matches!(iterator.next(), None);
2304
2305 Ok(())
2306 }
2307
2308 #[test]
2309 fn test_insert_items_at() -> Result<(), Error> {
2310 use super::Update::*;
2311
2312 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2313
2314 let _ = linked_chunk.updates().unwrap().take();
2316
2317 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2318 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2319 assert_eq!(
2320 linked_chunk.updates().unwrap().take(),
2321 &[
2322 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2323 NewItemsChunk {
2324 previous: Some(ChunkIdentifier(0)),
2325 new: ChunkIdentifier(1),
2326 next: None,
2327 },
2328 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2329 ]
2330 );
2331
2332 {
2334 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2335
2336 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2339
2340 assert_items_eq!(
2341 linked_chunk,
2342 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2343 );
2344 assert_eq!(linked_chunk.num_items(), 10);
2345 assert_eq!(
2346 linked_chunk.updates().unwrap().take(),
2347 &[
2348 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2349 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2350 NewItemsChunk {
2351 previous: Some(ChunkIdentifier(1)),
2352 new: ChunkIdentifier(2),
2353 next: None,
2354 },
2355 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2356 StartReattachItems,
2357 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2358 NewItemsChunk {
2359 previous: Some(ChunkIdentifier(2)),
2360 new: ChunkIdentifier(3),
2361 next: None,
2362 },
2363 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2364 EndReattachItems,
2365 ]
2366 );
2367 }
2368
2369 {
2371 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2372 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2373
2374 assert_items_eq!(
2375 linked_chunk,
2376 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2377 );
2378 assert_eq!(linked_chunk.num_items(), 14);
2379 assert_eq!(
2380 linked_chunk.updates().unwrap().take(),
2381 &[
2382 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2383 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2384 NewItemsChunk {
2385 previous: Some(ChunkIdentifier(0)),
2386 new: ChunkIdentifier(4),
2387 next: Some(ChunkIdentifier(1)),
2388 },
2389 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2390 StartReattachItems,
2391 PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2392 NewItemsChunk {
2393 previous: Some(ChunkIdentifier(4)),
2394 new: ChunkIdentifier(5),
2395 next: Some(ChunkIdentifier(1)),
2396 },
2397 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2398 EndReattachItems,
2399 ]
2400 );
2401 }
2402
2403 {
2405 let pos_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2406 linked_chunk.insert_items_at(pos_c, ['r', 's'])?;
2407
2408 assert_items_eq!(
2409 linked_chunk,
2410 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2411 );
2412 assert_eq!(linked_chunk.num_items(), 16);
2413 assert_eq!(
2414 linked_chunk.updates().unwrap().take(),
2415 &[
2416 DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2417 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2418 StartReattachItems,
2419 PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2420 EndReattachItems,
2421 ]
2422 );
2423 }
2424
2425 {
2427 let pos_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2428 let pos_f = Position(pos_f.chunk_identifier(), pos_f.index() + 1);
2429
2430 linked_chunk.insert_items_at(pos_f, ['p', 'q'])?;
2431 assert_items_eq!(
2432 linked_chunk,
2433 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2434 );
2435 assert_eq!(
2436 linked_chunk.updates().unwrap().take(),
2437 &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2438 );
2439 assert_eq!(linked_chunk.num_items(), 18);
2440 }
2441
2442 {
2444 assert_matches!(
2445 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2446 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2447 );
2448 assert!(linked_chunk.updates().unwrap().take().is_empty());
2449 }
2450
2451 {
2453 assert_matches!(
2454 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2455 Err(Error::InvalidItemIndex { index: 128 })
2456 );
2457 assert!(linked_chunk.updates().unwrap().take().is_empty());
2458 }
2459
2460 {
2462 linked_chunk.push_gap_back(());
2464 assert_items_eq!(
2465 linked_chunk,
2466 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2467 );
2468 assert_eq!(
2469 linked_chunk.updates().unwrap().take(),
2470 &[NewGapChunk {
2471 previous: Some(ChunkIdentifier(3)),
2472 new: ChunkIdentifier(6),
2473 next: None,
2474 gap: ()
2475 }]
2476 );
2477
2478 assert_matches!(
2479 linked_chunk.insert_items_at(Position(ChunkIdentifier(6), 0), ['u', 'v'],),
2480 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2481 );
2482 }
2483
2484 assert_eq!(linked_chunk.num_items(), 18);
2485
2486 Ok(())
2487 }
2488
2489 #[test]
2490 fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2491 use super::Update::*;
2492
2493 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2494
2495 let _ = linked_chunk.updates().unwrap().take();
2497
2498 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2499 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2500 assert_eq!(
2501 linked_chunk.updates().unwrap().take(),
2502 &[
2503 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2504 NewItemsChunk {
2505 previous: Some(ChunkIdentifier(0)),
2506 new: ChunkIdentifier(1),
2507 next: None,
2508 },
2509 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2510 ]
2511 );
2512
2513 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2515
2516 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2519
2520 assert_items_eq!(
2521 linked_chunk,
2522 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2523 );
2524 assert_eq!(linked_chunk.num_items(), 10);
2525 assert_eq!(
2526 linked_chunk.updates().unwrap().take(),
2527 &[
2528 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2529 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2530 NewItemsChunk {
2531 previous: Some(ChunkIdentifier(1)),
2532 new: ChunkIdentifier(2),
2533 next: None,
2534 },
2535 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2536 StartReattachItems,
2537 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2538 NewItemsChunk {
2539 previous: Some(ChunkIdentifier(2)),
2540 new: ChunkIdentifier(3),
2541 next: None,
2542 },
2543 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2544 EndReattachItems,
2545 ]
2546 );
2547
2548 Ok(())
2549 }
2550
2551 #[test]
2552 fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2553 use super::Update::*;
2554
2555 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2556
2557 let _ = linked_chunk.updates().unwrap().take();
2559
2560 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2561 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2562 assert_eq!(
2563 linked_chunk.updates().unwrap().take(),
2564 &[
2565 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2566 NewItemsChunk {
2567 previous: Some(ChunkIdentifier(0)),
2568 new: ChunkIdentifier(1),
2569 next: None,
2570 },
2571 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2572 ]
2573 );
2574
2575 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2577 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2578
2579 assert_items_eq!(
2580 linked_chunk,
2581 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'e', 'f']
2582 );
2583 assert_eq!(linked_chunk.num_items(), 10);
2584 assert_eq!(
2585 linked_chunk.updates().unwrap().take(),
2586 &[
2587 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2588 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2589 NewItemsChunk {
2590 previous: Some(ChunkIdentifier(0)),
2591 new: ChunkIdentifier(2),
2592 next: Some(ChunkIdentifier(1)),
2593 },
2594 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2595 StartReattachItems,
2596 PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2597 NewItemsChunk {
2598 previous: Some(ChunkIdentifier(2)),
2599 new: ChunkIdentifier(3),
2600 next: Some(ChunkIdentifier(1)),
2601 },
2602 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2603 EndReattachItems,
2604 ]
2605 );
2606
2607 Ok(())
2608 }
2609
2610 #[test]
2611 fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2612 use super::Update::*;
2613
2614 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2615
2616 let _ = linked_chunk.updates().unwrap().take();
2618
2619 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2620 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2621 assert_eq!(
2622 linked_chunk.updates().unwrap().take(),
2623 &[
2624 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2625 NewItemsChunk {
2626 previous: Some(ChunkIdentifier(0)),
2627 new: ChunkIdentifier(1),
2628 next: None,
2629 },
2630 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2631 NewItemsChunk {
2632 previous: Some(ChunkIdentifier(1)),
2633 new: ChunkIdentifier(2),
2634 next: None,
2635 },
2636 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2637 ]
2638 );
2639
2640 let pos_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2641 linked_chunk.insert_items_at(pos_d, ['r', 's'])?;
2642
2643 assert_items_eq!(
2644 linked_chunk,
2645 ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2646 );
2647 assert_eq!(linked_chunk.num_items(), 10);
2648 assert_eq!(
2649 linked_chunk.updates().unwrap().take(),
2650 &[
2651 DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2652 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2653 StartReattachItems,
2654 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2655 NewItemsChunk {
2656 previous: Some(ChunkIdentifier(1)),
2657 new: ChunkIdentifier(3),
2658 next: Some(ChunkIdentifier(2)),
2659 },
2660 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2661 EndReattachItems,
2662 ]
2663 );
2664
2665 Ok(())
2666 }
2667
2668 #[test]
2669 fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2670 use super::Update::*;
2671
2672 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2673
2674 let _ = linked_chunk.updates().unwrap().take();
2676
2677 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2678 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2679 assert_eq!(
2680 linked_chunk.updates().unwrap().take(),
2681 &[
2682 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2683 NewItemsChunk {
2684 previous: Some(ChunkIdentifier(0)),
2685 new: ChunkIdentifier(1),
2686 next: None,
2687 },
2688 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2689 ]
2690 );
2691
2692 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2694 let pos_after_e = Position(pos_e.chunk_identifier(), pos_e.index() + 1);
2695
2696 linked_chunk.insert_items_at(pos_after_e, ['p', 'q'])?;
2697 assert_items_eq!(
2698 linked_chunk,
2699 ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2700 );
2701 assert_eq!(
2702 linked_chunk.updates().unwrap().take(),
2703 &[
2704 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2705 NewItemsChunk {
2706 previous: Some(ChunkIdentifier(1)),
2707 new: ChunkIdentifier(2),
2708 next: None
2709 },
2710 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2711 ]
2712 );
2713 assert_eq!(linked_chunk.num_items(), 7);
2714
2715 Ok(())
2716 }
2717
2718 #[test]
2719 fn test_insert_items_at_errs() -> Result<(), Error> {
2720 use super::Update::*;
2721
2722 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2723
2724 let _ = linked_chunk.updates().unwrap().take();
2726
2727 linked_chunk.push_items_back(['a', 'b', 'c']);
2728 linked_chunk.push_gap_back(());
2729 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2730 assert_eq!(
2731 linked_chunk.updates().unwrap().take(),
2732 &[
2733 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2734 NewGapChunk {
2735 previous: Some(ChunkIdentifier(0)),
2736 new: ChunkIdentifier(1),
2737 next: None,
2738 gap: (),
2739 },
2740 ]
2741 );
2742
2743 {
2745 assert_matches!(
2746 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2747 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2748 );
2749 assert!(linked_chunk.updates().unwrap().take().is_empty());
2750 }
2751
2752 {
2754 assert_matches!(
2755 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2756 Err(Error::InvalidItemIndex { index: 128 })
2757 );
2758 assert!(linked_chunk.updates().unwrap().take().is_empty());
2759 }
2760
2761 {
2763 assert_matches!(
2764 linked_chunk.insert_items_at(Position(ChunkIdentifier(1), 0), ['u', 'v'],),
2765 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2766 );
2767 }
2768
2769 Ok(())
2770 }
2771
2772 #[test]
2773 fn test_remove_item_at() -> Result<(), Error> {
2774 use super::Update::*;
2775
2776 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2777
2778 let _ = linked_chunk.updates().unwrap().take();
2780
2781 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2782 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2783 assert_eq!(linked_chunk.num_items(), 11);
2784
2785 let _ = linked_chunk.updates().unwrap().take();
2787
2788 {
2791 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2792 let removed_item = linked_chunk.remove_item_at(position_of_f)?;
2793
2794 assert_eq!(removed_item, 'f');
2795 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2796 assert_eq!(linked_chunk.num_items(), 10);
2797
2798 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2799 let removed_item = linked_chunk.remove_item_at(position_of_e)?;
2800
2801 assert_eq!(removed_item, 'e');
2802 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2803 assert_eq!(linked_chunk.num_items(), 9);
2804
2805 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2806 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2807
2808 assert_eq!(removed_item, 'd');
2809 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2810 assert_eq!(linked_chunk.num_items(), 8);
2811
2812 assert_eq!(
2813 linked_chunk.updates().unwrap().take(),
2814 &[
2815 RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2816 RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2817 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2818 RemoveChunk(ChunkIdentifier(1)),
2819 ]
2820 );
2821 }
2822
2823 {
2826 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2827 let removed_item = linked_chunk.remove_item_at(first_position)?;
2828
2829 assert_eq!(removed_item, 'a');
2830 assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2831 assert_eq!(linked_chunk.num_items(), 7);
2832
2833 let removed_item = linked_chunk.remove_item_at(first_position)?;
2834
2835 assert_eq!(removed_item, 'b');
2836 assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2837 assert_eq!(linked_chunk.num_items(), 6);
2838
2839 let removed_item = linked_chunk.remove_item_at(first_position)?;
2840
2841 assert_eq!(removed_item, 'c');
2842 assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2843 assert_eq!(linked_chunk.num_items(), 5);
2844
2845 assert_eq!(
2846 linked_chunk.updates().unwrap().take(),
2847 &[
2848 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2849 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2850 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2851 ]
2852 );
2853 }
2854
2855 {
2858 let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2859 let removed_item = linked_chunk.remove_item_at(first_position)?;
2860
2861 assert_eq!(removed_item, 'g');
2862 assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2863 assert_eq!(linked_chunk.num_items(), 4);
2864
2865 let removed_item = linked_chunk.remove_item_at(first_position)?;
2866
2867 assert_eq!(removed_item, 'h');
2868 assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2869 assert_eq!(linked_chunk.num_items(), 3);
2870
2871 let removed_item = linked_chunk.remove_item_at(first_position)?;
2872
2873 assert_eq!(removed_item, 'i');
2874 assert_items_eq!(linked_chunk, [] ['j', 'k']);
2875 assert_eq!(linked_chunk.num_items(), 2);
2876
2877 assert_eq!(
2878 linked_chunk.updates().unwrap().take(),
2879 &[
2880 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2881 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2882 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2883 RemoveChunk(ChunkIdentifier(2)),
2884 ]
2885 );
2886 }
2887
2888 {
2891 let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2892 let removed_item = linked_chunk.remove_item_at(position_of_k)?;
2893
2894 assert_eq!(removed_item, 'k');
2895 #[rustfmt::skip]
2896 assert_items_eq!(linked_chunk, [] ['j']);
2897 assert_eq!(linked_chunk.num_items(), 1);
2898
2899 let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2900 let removed_item = linked_chunk.remove_item_at(position_of_j)?;
2901
2902 assert_eq!(removed_item, 'j');
2903 assert_items_eq!(linked_chunk, []);
2904 assert_eq!(linked_chunk.num_items(), 0);
2905
2906 assert_eq!(
2907 linked_chunk.updates().unwrap().take(),
2908 &[
2909 RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2910 RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2911 RemoveChunk(ChunkIdentifier(3)),
2912 ]
2913 );
2914 }
2915
2916 {
2918 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2919
2920 #[rustfmt::skip]
2921 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2922 assert_eq!(linked_chunk.num_items(), 4);
2923
2924 assert_matches!(
2926 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 3)),
2927 Err(Error::InvalidItemIndex { index: 3 })
2928 );
2929
2930 assert_matches!(
2932 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 42)),
2933 Err(Error::InvalidItemIndex { index: 42 })
2934 );
2935
2936 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2937 linked_chunk.insert_gap_at((), position_of_c)?;
2938
2939 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2940 assert_eq!(linked_chunk.num_items(), 4);
2941
2942 let _ = linked_chunk.updates().unwrap().take();
2944
2945 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2946 let removed_item = linked_chunk.remove_item_at(position_of_c)?;
2947
2948 assert_eq!(removed_item, 'c');
2949 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2950 assert_eq!(linked_chunk.num_items(), 3);
2951
2952 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2953 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2954
2955 assert_eq!(removed_item, 'd');
2956 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2957 assert_eq!(linked_chunk.num_items(), 2);
2958
2959 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2960 let removed_item = linked_chunk.remove_item_at(first_position)?;
2961
2962 assert_eq!(removed_item, 'a');
2963 assert_items_eq!(linked_chunk, ['b'] [-]);
2964 assert_eq!(linked_chunk.num_items(), 1);
2965
2966 let removed_item = linked_chunk.remove_item_at(first_position)?;
2967
2968 assert_eq!(removed_item, 'b');
2969 assert_items_eq!(linked_chunk, [] [-]);
2970 assert_eq!(linked_chunk.num_items(), 0);
2971
2972 assert_eq!(
2973 linked_chunk.updates().unwrap().take(),
2974 &[
2975 RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2976 RemoveChunk(ChunkIdentifier(6)),
2977 RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2978 RemoveChunk(ChunkIdentifier(4)),
2979 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2980 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2981 ]
2982 );
2983 }
2984
2985 Ok(())
2986 }
2987
2988 #[test]
2989 fn test_insert_gap_at() -> Result<(), Error> {
2990 use super::Update::*;
2991
2992 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2993
2994 let _ = linked_chunk.updates().unwrap().take();
2996
2997 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2998 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2999 assert_eq!(
3000 linked_chunk.updates().unwrap().take(),
3001 &[
3002 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
3003 NewItemsChunk {
3004 previous: Some(ChunkIdentifier(0)),
3005 new: ChunkIdentifier(1),
3006 next: None
3007 },
3008 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
3009 ]
3010 );
3011
3012 {
3014 let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
3015 linked_chunk.insert_gap_at((), position_of_b)?;
3016
3017 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
3018 assert_eq!(
3019 linked_chunk.updates().unwrap().take(),
3020 &[
3021 DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
3022 NewGapChunk {
3023 previous: Some(ChunkIdentifier(0)),
3024 new: ChunkIdentifier(2),
3025 next: Some(ChunkIdentifier(1)),
3026 gap: (),
3027 },
3028 StartReattachItems,
3029 NewItemsChunk {
3030 previous: Some(ChunkIdentifier(2)),
3031 new: ChunkIdentifier(3),
3032 next: Some(ChunkIdentifier(1)),
3033 },
3034 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
3035 EndReattachItems,
3036 ]
3037 );
3038 }
3039
3040 {
3043 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3044 linked_chunk.insert_gap_at((), position_of_a)?;
3045
3046 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
3049 assert_eq!(
3050 linked_chunk.updates().unwrap().take(),
3051 &[NewGapChunk {
3052 previous: None,
3053 new: ChunkIdentifier(4),
3054 next: Some(ChunkIdentifier(0)),
3055 gap: (),
3056 },]
3057 );
3058 }
3059
3060 {
3063 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
3064 linked_chunk.insert_gap_at((), position_of_d)?;
3065
3066 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
3070 assert_eq!(
3071 linked_chunk.updates().unwrap().take(),
3072 &[NewGapChunk {
3073 previous: Some(ChunkIdentifier(3)),
3074 new: ChunkIdentifier(5),
3075 next: Some(ChunkIdentifier(1)),
3076 gap: (),
3077 }]
3078 );
3079 }
3080
3081 {
3083 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3085 let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
3086
3087 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
3088
3089 assert_eq!(
3090 linked_chunk.updates().unwrap().take(),
3091 &[
3092 NewItemsChunk {
3093 previous: Some(ChunkIdentifier(5)),
3094 new: ChunkIdentifier(6),
3095 next: Some(ChunkIdentifier(1)),
3096 },
3097 RemoveChunk(ChunkIdentifier(5)),
3098 ]
3099 );
3100
3101 linked_chunk.insert_gap_at((), position)?;
3102
3103 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
3104 assert_eq!(
3105 linked_chunk.updates().unwrap().take(),
3106 &[NewGapChunk {
3107 previous: Some(ChunkIdentifier(3)),
3108 new: ChunkIdentifier(7),
3109 next: Some(ChunkIdentifier(6)),
3110 gap: (),
3111 }]
3112 );
3113 }
3114
3115 {
3117 assert_matches!(
3118 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
3119 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
3120 );
3121 assert!(linked_chunk.updates().unwrap().take().is_empty());
3122 }
3123
3124 {
3126 assert_matches!(
3127 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
3128 Err(Error::InvalidItemIndex { index: 128 })
3129 );
3130 assert!(linked_chunk.updates().unwrap().take().is_empty());
3131 }
3132
3133 {
3135 let position_of_a_gap = Position(ChunkIdentifier(2), 0);
3138 assert_matches!(
3139 linked_chunk.insert_gap_at((), position_of_a_gap),
3140 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
3141 );
3142 assert!(linked_chunk.updates().unwrap().take().is_empty());
3143 }
3144
3145 assert_eq!(linked_chunk.num_items(), 6);
3146
3147 Ok(())
3148 }
3149
3150 #[test]
3151 fn test_replace_gap_at_middle() -> Result<(), Error> {
3152 use super::Update::*;
3153
3154 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3155
3156 let _ = linked_chunk.updates().unwrap().take();
3158
3159 linked_chunk.push_items_back(['a', 'b']);
3160 linked_chunk.push_gap_back(());
3161 linked_chunk.push_items_back(['l', 'm']);
3162 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
3163 assert_eq!(
3164 linked_chunk.updates().unwrap().take(),
3165 &[
3166 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3167 NewGapChunk {
3168 previous: Some(ChunkIdentifier(0)),
3169 new: ChunkIdentifier(1),
3170 next: None,
3171 gap: (),
3172 },
3173 NewItemsChunk {
3174 previous: Some(ChunkIdentifier(1)),
3175 new: ChunkIdentifier(2),
3176 next: None,
3177 },
3178 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
3179 ]
3180 );
3181
3182 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3184 assert_eq!(gap_identifier, ChunkIdentifier(1));
3185
3186 let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
3187 assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
3188 assert_items_eq!(
3189 linked_chunk,
3190 ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
3191 );
3192 assert_eq!(
3193 linked_chunk.updates().unwrap().take(),
3194 &[
3195 NewItemsChunk {
3196 previous: Some(ChunkIdentifier(1)),
3197 new: ChunkIdentifier(3),
3198 next: Some(ChunkIdentifier(2)),
3199 },
3200 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
3201 NewItemsChunk {
3202 previous: Some(ChunkIdentifier(3)),
3203 new: ChunkIdentifier(4),
3204 next: Some(ChunkIdentifier(2)),
3205 },
3206 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
3207 RemoveChunk(ChunkIdentifier(1)),
3208 ]
3209 );
3210
3211 assert_eq!(linked_chunk.num_items(), 9);
3212
3213 Ok(())
3214 }
3215
3216 #[test]
3217 fn test_replace_gap_at_end() -> Result<(), Error> {
3218 use super::Update::*;
3219
3220 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3221
3222 let _ = linked_chunk.updates().unwrap().take();
3224
3225 linked_chunk.push_items_back(['a', 'b']);
3226 linked_chunk.push_gap_back(());
3227 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3228 assert_eq!(
3229 linked_chunk.updates().unwrap().take(),
3230 &[
3231 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3232 NewGapChunk {
3233 previous: Some(ChunkIdentifier(0)),
3234 new: ChunkIdentifier(1),
3235 next: None,
3236 gap: (),
3237 },
3238 ]
3239 );
3240
3241 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3243 assert_eq!(gap_identifier, ChunkIdentifier(1));
3244
3245 let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3246 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3247 assert_items_eq!(
3248 linked_chunk,
3249 ['a', 'b'] ['w', 'x', 'y'] ['z']
3250 );
3251 assert_eq!(
3252 linked_chunk.updates().unwrap().take(),
3253 &[
3254 NewItemsChunk {
3255 previous: Some(ChunkIdentifier(1)),
3256 new: ChunkIdentifier(2),
3257 next: None,
3258 },
3259 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3260 NewItemsChunk {
3261 previous: Some(ChunkIdentifier(2)),
3262 new: ChunkIdentifier(3),
3263 next: None,
3264 },
3265 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3266 RemoveChunk(ChunkIdentifier(1)),
3267 ]
3268 );
3269
3270 assert_eq!(linked_chunk.num_items(), 6);
3271
3272 Ok(())
3273 }
3274
3275 #[test]
3276 fn test_replace_gap_at_beginning() -> Result<(), Error> {
3277 use super::Update::*;
3278
3279 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3280
3281 let _ = linked_chunk.updates().unwrap().take();
3283
3284 linked_chunk.push_items_back(['a', 'b']);
3285 assert_items_eq!(linked_chunk, ['a', 'b']);
3286 assert_eq!(
3287 linked_chunk.updates().unwrap().take(),
3288 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },]
3289 );
3290
3291 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3293 linked_chunk.insert_gap_at((), position_of_a).unwrap();
3294 assert_items_eq!(
3295 linked_chunk,
3296 [-] ['a', 'b']
3297 );
3298 assert_eq!(
3299 linked_chunk.updates().unwrap().take(),
3300 &[NewGapChunk {
3301 previous: None,
3302 new: ChunkIdentifier(1),
3303 next: Some(ChunkIdentifier(0)),
3304 gap: (),
3305 }]
3306 );
3307
3308 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3309 assert_eq!(gap_identifier, ChunkIdentifier(1));
3310
3311 let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3312 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3313 assert_items_eq!(
3314 linked_chunk,
3315 ['x'] ['a', 'b']
3316 );
3317 assert_eq!(
3318 linked_chunk.updates().unwrap().take(),
3319 &[
3320 NewItemsChunk {
3321 previous: Some(ChunkIdentifier(1)),
3322 new: ChunkIdentifier(2),
3323 next: Some(ChunkIdentifier(0)),
3324 },
3325 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3326 RemoveChunk(ChunkIdentifier(1)),
3327 ]
3328 );
3329
3330 assert_eq!(linked_chunk.num_items(), 3);
3331
3332 Ok(())
3333 }
3334
3335 #[test]
3336 fn test_remove_empty_chunk_at() -> Result<(), Error> {
3337 use super::Update::*;
3338
3339 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3340
3341 let _ = linked_chunk.updates().unwrap().take();
3343
3344 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3345 linked_chunk.push_items_back(['a', 'b']);
3346 linked_chunk.push_gap_back(());
3347 linked_chunk.push_items_back(['l', 'm']);
3348 linked_chunk.push_gap_back(());
3349 assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3350 assert_eq!(
3351 linked_chunk.updates().unwrap().take(),
3352 &[
3353 NewGapChunk {
3354 previous: None,
3355 new: ChunkIdentifier(1),
3356 next: Some(ChunkIdentifier(0)),
3357 gap: (),
3358 },
3359 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3360 NewGapChunk {
3361 previous: Some(ChunkIdentifier(0)),
3362 new: ChunkIdentifier(2),
3363 next: None,
3364 gap: (),
3365 },
3366 NewItemsChunk {
3367 previous: Some(ChunkIdentifier(2)),
3368 new: ChunkIdentifier(3),
3369 next: None,
3370 },
3371 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3372 NewGapChunk {
3373 previous: Some(ChunkIdentifier(3)),
3374 new: ChunkIdentifier(4),
3375 next: None,
3376 gap: (),
3377 },
3378 ]
3379 );
3380
3381 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3383 assert_matches!(err, Error::RemovingNonEmptyItemsChunk { .. });
3384
3385 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(42)).unwrap_err();
3387 assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3388
3389 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(2)).unwrap();
3391 let next = maybe_next.unwrap();
3392 assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3394 assert_eq!(next.index(), 0);
3395 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3396 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3397
3398 let next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(4)).unwrap();
3400 assert!(next.is_none());
3402 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3403 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3404
3405 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(1)).unwrap();
3407 let next = maybe_next.unwrap();
3408 assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3409 assert_eq!(next.index(), 0);
3410 assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3411 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3412
3413 Ok(())
3414 }
3415
3416 #[test]
3417 fn test_remove_empty_last_chunk() {
3418 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3419
3420 let _ = linked_chunk.updates().unwrap().take();
3422
3423 assert_items_eq!(linked_chunk, []);
3424 assert!(linked_chunk.updates().unwrap().take().is_empty());
3425
3426 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3428 assert_matches!(err, Error::RemovingLastChunk);
3429 }
3430
3431 #[test]
3432 fn test_chunk_item_positions() {
3433 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3434 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3435 linked_chunk.push_gap_back(());
3436 linked_chunk.push_items_back(['f']);
3437
3438 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3439
3440 let mut iterator = linked_chunk.chunks();
3441
3442 {
3444 let chunk = iterator.next().unwrap();
3445 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3446 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3447 }
3448
3449 {
3451 let chunk = iterator.next().unwrap();
3452 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3453 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3454 }
3455
3456 {
3458 let chunk = iterator.next().unwrap();
3459 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3460 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3461 }
3462
3463 {
3465 let chunk = iterator.next().unwrap();
3466 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3467 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3468 }
3469 }
3470
3471 #[test]
3472 fn test_is_first_and_last_chunk() {
3473 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3474
3475 let mut chunks = linked_chunk.chunks().peekable();
3476 assert!(chunks.peek().unwrap().is_first_chunk());
3477 assert!(chunks.next().unwrap().is_last_chunk());
3478 assert!(chunks.next().is_none());
3479
3480 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3481
3482 let mut chunks = linked_chunk.chunks().peekable();
3483 assert!(chunks.next().unwrap().is_first_chunk());
3484 assert!(chunks.peek().unwrap().is_first_chunk().not());
3485 assert!(chunks.next().unwrap().is_last_chunk().not());
3486 assert!(chunks.next().unwrap().is_last_chunk());
3487 assert!(chunks.next().is_none());
3488 }
3489
3490 #[test]
3495 fn test_clear() {
3496 let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3497
3498 let item = Arc::new('a');
3499 let gap = Arc::new(());
3500
3501 linked_chunk.push_items_back([
3502 item.clone(),
3503 item.clone(),
3504 item.clone(),
3505 item.clone(),
3506 item.clone(),
3507 ]);
3508 linked_chunk.push_gap_back(gap.clone());
3509 linked_chunk.push_items_back([item.clone()]);
3510
3511 assert_eq!(Arc::strong_count(&item), 7);
3512 assert_eq!(Arc::strong_count(&gap), 2);
3513 assert_eq!(linked_chunk.num_items(), 6);
3514 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3515
3516 linked_chunk.clear();
3518
3519 assert_eq!(Arc::strong_count(&item), 1);
3520 assert_eq!(Arc::strong_count(&gap), 1);
3521 assert_eq!(linked_chunk.num_items(), 0);
3522 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3523 }
3524
3525 #[test]
3526 fn test_clear_emit_an_update_clear() {
3527 use super::Update::*;
3528
3529 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3530
3531 assert_eq!(
3532 linked_chunk.updates().unwrap().take(),
3533 &[NewItemsChunk {
3534 previous: None,
3535 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3536 next: None
3537 }]
3538 );
3539
3540 linked_chunk.clear();
3541
3542 assert_eq!(
3543 linked_chunk.updates().unwrap().take(),
3544 &[
3545 Clear,
3546 NewItemsChunk {
3547 previous: None,
3548 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3549 next: None
3550 }
3551 ]
3552 );
3553 }
3554
3555 #[test]
3556 fn test_replace_item() {
3557 use super::Update::*;
3558
3559 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3560
3561 linked_chunk.push_items_back(['a', 'b', 'c']);
3562 linked_chunk.push_gap_back(());
3563 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3565
3566 let _ = linked_chunk.updates().unwrap().take();
3568
3569 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3571 assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3572
3573 assert_eq!(
3574 linked_chunk.updates().unwrap().take(),
3575 &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3576 );
3577
3578 assert_matches!(
3580 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3581 Err(Error::InvalidItemIndex { index: 3 })
3582 );
3583
3584 assert_matches!(
3586 linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3587 Err(Error::ChunkIsAGap { .. })
3588 );
3589 }
3590
3591 #[test]
3592 fn test_lazy_previous() {
3593 use std::marker::PhantomData;
3594
3595 use super::{Ends, ObservableUpdates, Update::*};
3596
3597 let first_chunk_identifier = ChunkIdentifier(0);
3599 let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3600 unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3601
3602 let mut linked_chunk = LinkedChunk::<3, char, ()> {
3603 links: Ends { first: first_loaded_chunk, last: None },
3604 chunk_identifier_generator:
3605 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3606 updates: Some(ObservableUpdates::new()),
3607 marker: PhantomData,
3608 };
3609
3610 {
3613 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3614
3615 assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3616
3617 {
3619 let mut chunks = linked_chunk.chunks();
3620
3621 assert_matches!(chunks.next(), Some(chunk) => {
3622 assert_eq!(chunk.identifier(), 1);
3623 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3624 });
3625 assert_matches!(chunks.next(), Some(chunk) => {
3626 assert_eq!(chunk.identifier(), 2);
3627 assert!(chunk.lazy_previous.is_none());
3628 });
3629 assert!(chunks.next().is_none());
3630 }
3631
3632 assert_eq!(
3634 linked_chunk.updates().unwrap().take(),
3635 &[
3636 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3637 NewItemsChunk {
3638 previous: Some(ChunkIdentifier(1)),
3639 new: ChunkIdentifier(2),
3640 next: None,
3641 },
3642 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3643 ]
3644 );
3645 }
3646
3647 {
3649 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3650
3651 assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3652
3653 {
3655 let mut chunks = linked_chunk.chunks();
3656
3657 assert_matches!(chunks.next(), Some(chunk) => {
3658 assert_eq!(chunk.identifier(), 3);
3659 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3661 });
3662 assert_matches!(chunks.next(), Some(chunk) => {
3663 assert_eq!(chunk.identifier(), 1);
3664 assert!(chunk.lazy_previous.is_none());
3666 });
3667 assert_matches!(chunks.next(), Some(chunk) => {
3668 assert_eq!(chunk.identifier(), 2);
3669 assert!(chunk.lazy_previous.is_none());
3670 });
3671 assert!(chunks.next().is_none());
3672 }
3673
3674 assert_eq!(
3676 linked_chunk.updates().unwrap().take(),
3677 &[NewGapChunk {
3678 previous: Some(ChunkIdentifier(0)),
3680 new: ChunkIdentifier(3),
3681 next: Some(ChunkIdentifier(1)),
3682 gap: ()
3683 }]
3684 );
3685 }
3686
3687 {
3689 linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3690
3691 assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3692
3693 {
3695 let mut chunks = linked_chunk.chunks();
3696
3697 assert_matches!(chunks.next(), Some(chunk) => {
3698 assert_eq!(chunk.identifier(), 4);
3699 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3701 });
3702 assert_matches!(chunks.next(), Some(chunk) => {
3703 assert_eq!(chunk.identifier(), 5);
3704 assert!(chunk.lazy_previous.is_none());
3705 });
3706 assert_matches!(chunks.next(), Some(chunk) => {
3707 assert_eq!(chunk.identifier(), 1);
3708 assert!(chunk.lazy_previous.is_none());
3709 });
3710 assert_matches!(chunks.next(), Some(chunk) => {
3711 assert_eq!(chunk.identifier(), 2);
3712 assert!(chunk.lazy_previous.is_none());
3713 });
3714 assert!(chunks.next().is_none());
3715 }
3716
3717 assert_eq!(
3719 linked_chunk.updates().unwrap().take(),
3720 &[
3721 NewItemsChunk {
3723 previous: Some(ChunkIdentifier(3)),
3724 new: ChunkIdentifier(4),
3725 next: Some(ChunkIdentifier(1)),
3726 },
3727 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3729 NewItemsChunk {
3731 previous: Some(ChunkIdentifier(4)),
3732 new: ChunkIdentifier(5),
3733 next: Some(ChunkIdentifier(1)),
3734 },
3735 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3737 RemoveChunk(ChunkIdentifier(3)),
3739 ]
3740 );
3741 }
3742
3743 {
3747 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3748
3749 assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3750
3751 {
3753 let mut chunks = linked_chunk.chunks();
3754
3755 assert_matches!(chunks.next(), Some(chunk) => {
3756 assert_eq!(chunk.identifier(), 6);
3757 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3759 });
3760 assert_matches!(chunks.next(), Some(chunk) => {
3761 assert_eq!(chunk.identifier(), 4);
3762 assert!(chunk.lazy_previous.is_none());
3764 });
3765 assert_matches!(chunks.next(), Some(chunk) => {
3766 assert_eq!(chunk.identifier(), 5);
3767 assert!(chunk.lazy_previous.is_none());
3768 });
3769 assert_matches!(chunks.next(), Some(chunk) => {
3770 assert_eq!(chunk.identifier(), 1);
3771 assert!(chunk.lazy_previous.is_none());
3772 });
3773 assert_matches!(chunks.next(), Some(chunk) => {
3774 assert_eq!(chunk.identifier(), 2);
3775 assert!(chunk.lazy_previous.is_none());
3776 });
3777 assert!(chunks.next().is_none());
3778 }
3779
3780 assert_eq!(
3782 linked_chunk.updates().unwrap().take(),
3783 &[NewGapChunk {
3784 previous: Some(ChunkIdentifier(0)),
3786 new: ChunkIdentifier(6),
3787 next: Some(ChunkIdentifier(4)),
3788 gap: ()
3789 }]
3790 );
3791 }
3792 }
3793}