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}
123
124impl Display for LinkedChunkId<'_> {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 match self {
127 Self::Room(room_id) => write!(f, "{room_id}"),
128 Self::Thread(room_id, thread_root) => {
129 write!(f, "{room_id}:thread:{thread_root}")
130 }
131 Self::PinnedEvents(room_id) => {
132 write!(f, "{room_id}:pinned")
133 }
134 }
135 }
136}
137
138impl LinkedChunkId<'_> {
139 pub fn storage_key(&self) -> impl '_ + AsRef<[u8]> {
140 match self {
141 LinkedChunkId::Room(room_id) => room_id.to_string(),
142 LinkedChunkId::Thread(room_id, event_id) => format!("t:{room_id}:{event_id}"),
143 LinkedChunkId::PinnedEvents(room_id) => format!("pinned:{room_id}"),
144 }
145 }
146
147 pub fn to_owned(&self) -> OwnedLinkedChunkId {
148 match self {
149 LinkedChunkId::Room(room_id) => OwnedLinkedChunkId::Room((*room_id).to_owned()),
150 LinkedChunkId::Thread(room_id, event_id) => {
151 OwnedLinkedChunkId::Thread((*room_id).to_owned(), (*event_id).to_owned())
152 }
153 LinkedChunkId::PinnedEvents(room_id) => {
154 OwnedLinkedChunkId::PinnedEvents((*room_id).to_owned())
155 }
156 }
157 }
158}
159
160impl<'a> From<&'a OwnedLinkedChunkId> for LinkedChunkId<'a> {
161 fn from(value: &'a OwnedLinkedChunkId) -> Self {
162 value.as_ref()
163 }
164}
165
166impl PartialEq<&OwnedLinkedChunkId> for LinkedChunkId<'_> {
167 fn eq(&self, other: &&OwnedLinkedChunkId) -> bool {
168 match (self, other) {
169 (LinkedChunkId::Room(a), OwnedLinkedChunkId::Room(b)) => *a == b,
170 (LinkedChunkId::PinnedEvents(a), OwnedLinkedChunkId::PinnedEvents(b)) => *a == b,
171 (LinkedChunkId::Thread(r, ev), OwnedLinkedChunkId::Thread(r2, ev2)) => {
172 r == r2 && ev == ev2
173 }
174 _ => false,
175 }
176 }
177}
178
179impl PartialEq<LinkedChunkId<'_>> for OwnedLinkedChunkId {
180 fn eq(&self, other: &LinkedChunkId<'_>) -> bool {
181 other.eq(&self)
182 }
183}
184
185#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
187pub enum OwnedLinkedChunkId {
188 Room(OwnedRoomId),
189 Thread(OwnedRoomId, OwnedEventId),
190 PinnedEvents(OwnedRoomId),
191}
192
193impl Display for OwnedLinkedChunkId {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 self.as_ref().fmt(f)
196 }
197}
198
199impl OwnedLinkedChunkId {
200 pub fn as_ref(&self) -> LinkedChunkId<'_> {
201 match self {
202 OwnedLinkedChunkId::Room(room_id) => LinkedChunkId::Room(room_id.as_ref()),
203 OwnedLinkedChunkId::Thread(room_id, event_id) => {
204 LinkedChunkId::Thread(room_id.as_ref(), event_id.as_ref())
205 }
206 OwnedLinkedChunkId::PinnedEvents(room_id) => {
207 LinkedChunkId::PinnedEvents(room_id.as_ref())
208 }
209 }
210 }
211
212 pub fn room_id(&self) -> &RoomId {
213 match self {
214 OwnedLinkedChunkId::Room(room_id)
215 | OwnedLinkedChunkId::Thread(room_id, ..)
216 | OwnedLinkedChunkId::PinnedEvents(room_id, ..) => room_id,
217 }
218 }
219}
220
221impl From<LinkedChunkId<'_>> for OwnedLinkedChunkId {
222 fn from(value: LinkedChunkId<'_>) -> Self {
223 value.to_owned()
224 }
225}
226
227#[derive(thiserror::Error, Debug)]
229pub enum Error {
230 #[error("The chunk identifier is invalid: `{identifier:?}`")]
232 InvalidChunkIdentifier {
233 identifier: ChunkIdentifier,
235 },
236
237 #[error("The chunk is a gap: `{identifier:?}`")]
239 ChunkIsAGap {
240 identifier: ChunkIdentifier,
242 },
243
244 #[error("The chunk is an item: `{identifier:?}`")]
246 ChunkIsItems {
247 identifier: ChunkIdentifier,
249 },
250
251 #[error("The chunk is a non-empty item chunk: `{identifier:?}`")]
253 RemovingNonEmptyItemsChunk {
254 identifier: ChunkIdentifier,
256 },
257
258 #[error("Trying to remove the only chunk, but a linked chunk can't be empty")]
261 RemovingLastChunk,
262
263 #[error("The item index is invalid: `{index}`")]
265 InvalidItemIndex {
266 index: usize,
268 },
269}
270
271struct Ends<const CHUNK_CAPACITY: usize, Item, Gap> {
276 first: NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>,
278 last: Option<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
280}
281
282impl<const CAP: usize, Item, Gap> Ends<CAP, Item, Gap> {
283 fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
285 unsafe { self.first.as_ref() }
286 }
287
288 fn first_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
290 unsafe { self.first.as_mut() }
291 }
292
293 fn latest_chunk(&self) -> &Chunk<CAP, Item, Gap> {
295 unsafe { self.last.unwrap_or(self.first).as_ref() }
296 }
297
298 fn latest_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
300 unsafe { self.last.as_mut().unwrap_or(&mut self.first).as_mut() }
301 }
302
303 fn chunk(&self, identifier: ChunkIdentifier) -> Option<&Chunk<CAP, Item, Gap>> {
305 let mut chunk = self.latest_chunk();
306
307 loop {
308 if chunk.identifier() == identifier {
309 return Some(chunk);
310 }
311
312 chunk = chunk.previous()?;
313 }
314 }
315
316 fn chunk_mut(&mut self, identifier: ChunkIdentifier) -> Option<&mut Chunk<CAP, Item, Gap>> {
318 let mut chunk = self.latest_chunk_mut();
319
320 loop {
321 if chunk.identifier() == identifier {
322 return Some(chunk);
323 }
324
325 chunk = chunk.previous_mut()?;
326 }
327 }
328
329 unsafe fn clear(&mut self) {
336 let mut current_chunk_ptr = self.last.or(Some(self.first));
339
340 while let Some(chunk_ptr) = current_chunk_ptr {
342 let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
344
345 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
347
348 current_chunk_ptr = previous_ptr;
350 }
351
352 self.first = NonNull::dangling();
354 self.last = None;
355 }
356
357 fn replace_with(&mut self, first_chunk: NonNull<Chunk<CAP, Item, Gap>>) {
360 unsafe {
362 self.clear();
363 }
364
365 self.first = first_chunk;
367 }
368
369 fn reset(&mut self) {
374 self.replace_with(Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER));
375 }
376}
377
378pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
385 links: Ends<CHUNK_CAPACITY, Item, Gap>,
387
388 chunk_identifier_generator: ChunkIdentifierGenerator,
390
391 updates: Option<ObservableUpdates<Item, Gap>>,
395
396 marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
398}
399
400impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
401 fn default() -> Self {
402 Self::new()
403 }
404}
405
406impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
407 pub fn new() -> Self {
409 Self {
410 links: Ends {
411 first: Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER),
412 last: None,
413 },
414 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
415 updates: None,
416 marker: PhantomData,
417 }
418 }
419
420 pub fn new_with_update_history() -> Self {
426 let first_chunk_identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
427
428 let mut updates = ObservableUpdates::new();
429 updates.push(Update::NewItemsChunk {
430 previous: None,
431 new: first_chunk_identifier,
432 next: None,
433 });
434
435 Self {
436 links: Ends { first: Chunk::new_items_leaked(first_chunk_identifier), last: None },
437 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
438 updates: Some(updates),
439 marker: PhantomData,
440 }
441 }
442
443 pub fn clear(&mut self) {
445 self.links.reset();
447
448 self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
450
451 if let Some(updates) = self.updates.as_mut() {
453 updates.clear_pending();
456 updates.push(Update::Clear);
457 updates.push(Update::NewItemsChunk {
458 previous: None,
459 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
460 next: None,
461 })
462 }
463 }
464
465 pub fn push_items_back<I>(&mut self, items: I)
471 where
472 Item: Clone,
473 Gap: Clone,
474 I: IntoIterator<Item = Item>,
475 I::IntoIter: ExactSizeIterator,
476 {
477 let items = items.into_iter();
478
479 let last_chunk = self.links.latest_chunk_mut();
480
481 let last_chunk =
483 last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
484
485 debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
486
487 if !last_chunk.is_first_chunk() {
491 self.links.last = Some(last_chunk.as_ptr());
494 }
495 }
496
497 pub fn push_gap_back(&mut self, content: Gap)
500 where
501 Item: Clone,
502 Gap: Clone,
503 {
504 let last_chunk = self.links.latest_chunk_mut();
505 last_chunk.insert_next(
506 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
507 &mut self.updates,
508 );
509
510 self.links.last = last_chunk.next;
511 }
512
513 pub fn insert_items_at<I>(&mut self, position: Position, items: I) -> Result<(), Error>
518 where
519 Item: Clone,
520 Gap: Clone,
521 I: IntoIterator<Item = Item>,
522 I::IntoIter: ExactSizeIterator,
523 {
524 let chunk_identifier = position.chunk_identifier();
525 let item_index = position.index();
526
527 let chunk = self
528 .links
529 .chunk_mut(chunk_identifier)
530 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
531
532 let chunk = match &mut chunk.content {
533 ChunkContent::Gap(..) => {
534 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
535 }
536
537 ChunkContent::Items(current_items) => {
538 let current_items_length = current_items.len();
539
540 if item_index > current_items_length {
541 return Err(Error::InvalidItemIndex { index: item_index });
542 }
543
544 let items = items.into_iter();
546
547 if item_index == current_items_length {
549 chunk
550 .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
552 }
553 else {
555 if let Some(updates) = self.updates.as_mut() {
556 updates.push(Update::DetachLastItems {
557 at: Position(chunk_identifier, item_index),
558 });
559 }
560
561 let detached_items = current_items.split_off(item_index);
563
564 let chunk = chunk
565 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
567
568 if let Some(updates) = self.updates.as_mut() {
569 updates.push(Update::StartReattachItems);
570 }
571
572 let chunk = chunk
573 .push_items(
575 detached_items.into_iter(),
576 &self.chunk_identifier_generator,
577 &mut self.updates,
578 );
579
580 if let Some(updates) = self.updates.as_mut() {
581 updates.push(Update::EndReattachItems);
582 }
583
584 chunk
585 }
586 }
587 };
588
589 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
592 self.links.last = Some(chunk.as_ptr());
595 }
596
597 Ok(())
598 }
599
600 pub fn remove_item_at(&mut self, position: Position) -> Result<Item, Error> {
608 let chunk_identifier = position.chunk_identifier();
609 let item_index = position.index();
610
611 let mut chunk_ptr = None;
612 let removed_item;
613
614 {
615 let chunk = self
616 .links
617 .chunk_mut(chunk_identifier)
618 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
619
620 let current_items = match &mut chunk.content {
621 ChunkContent::Gap(..) => {
622 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
623 }
624 ChunkContent::Items(current_items) => current_items,
625 };
626
627 if item_index >= current_items.len() {
628 return Err(Error::InvalidItemIndex { index: item_index });
629 }
630
631 removed_item = current_items.remove(item_index);
632
633 if let Some(updates) = self.updates.as_mut() {
634 updates.push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
635 }
636
637 if current_items.is_empty() && !chunk.is_first_chunk() {
639 chunk.unlink(self.updates.as_mut());
641
642 chunk_ptr = Some(chunk.as_ptr());
643
644 if chunk.is_last_chunk() {
647 self.links.last = chunk.previous;
648 }
649 }
650
651 }
653
654 if let Some(chunk_ptr) = chunk_ptr {
655 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
662 }
663
664 Ok(removed_item)
665 }
666
667 pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
672 where
673 Item: Clone,
674 {
675 let chunk_identifier = position.chunk_identifier();
676 let item_index = position.index();
677
678 let chunk = self
679 .links
680 .chunk_mut(chunk_identifier)
681 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
682
683 match &mut chunk.content {
684 ChunkContent::Gap(..) => {
685 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
686 }
687
688 ChunkContent::Items(current_items) => {
689 if item_index >= current_items.len() {
690 return Err(Error::InvalidItemIndex { index: item_index });
691 }
692
693 if let Some(updates) = self.updates.as_mut() {
695 updates.push(Update::ReplaceItem {
696 at: Position(chunk_identifier, item_index),
697 item: item.clone(),
698 });
699 }
700
701 current_items[item_index] = item;
702 }
703 }
704
705 Ok(())
706 }
707
708 pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
713 where
714 Item: Clone,
715 Gap: Clone,
716 {
717 let chunk_identifier = position.chunk_identifier();
718 let item_index = position.index();
719
720 let chunk = self
721 .links
722 .chunk_mut(chunk_identifier)
723 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
724
725 let chunk = match &mut chunk.content {
726 ChunkContent::Gap(..) => {
727 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
728 }
729
730 ChunkContent::Items(current_items) => {
731 if item_index == 0 {
735 let chunk_was_first = chunk.is_first_chunk();
736 let chunk_was_last = chunk.is_last_chunk();
737
738 let new_chunk = chunk.insert_before(
739 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
740 self.updates.as_mut(),
741 );
742
743 let new_chunk_ptr = new_chunk.as_ptr();
744 let chunk_ptr = chunk.as_ptr();
745
746 if chunk_was_first {
751 self.links.first = new_chunk_ptr;
752
753 if chunk_was_last {
755 self.links.last = Some(chunk_ptr);
756 }
757 }
758
759 return Ok(());
760 }
761
762 let current_items_length = current_items.len();
763
764 if item_index >= current_items_length {
765 return Err(Error::InvalidItemIndex { index: item_index });
766 }
767
768 if let Some(updates) = self.updates.as_mut() {
769 updates.push(Update::DetachLastItems {
770 at: Position(chunk_identifier, item_index),
771 });
772 }
773
774 let detached_items = current_items.split_off(item_index);
776
777 let chunk = chunk
778 .insert_next(
780 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
781 &mut self.updates,
782 );
783
784 if let Some(updates) = self.updates.as_mut() {
785 updates.push(Update::StartReattachItems);
786 }
787
788 let chunk = chunk
789 .insert_next(
791 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
792 &mut self.updates,
793 )
794 .push_items(
796 detached_items.into_iter(),
797 &self.chunk_identifier_generator,
798 &mut self.updates,
799 );
800
801 if let Some(updates) = self.updates.as_mut() {
802 updates.push(Update::EndReattachItems);
803 }
804
805 chunk
806 }
807 };
808
809 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
812 self.links.last = Some(chunk.as_ptr());
815 }
816
817 Ok(())
818 }
819
820 pub fn remove_empty_chunk_at(
829 &mut self,
830 chunk_identifier: ChunkIdentifier,
831 ) -> Result<Option<Position>, Error> {
832 if self.links.first_chunk().is_last_chunk() {
834 return Err(Error::RemovingLastChunk);
835 }
836
837 let chunk = self
838 .links
839 .chunk_mut(chunk_identifier)
840 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
841
842 if chunk.num_items() > 0 {
843 return Err(Error::RemovingNonEmptyItemsChunk { identifier: chunk_identifier });
844 }
845
846 let chunk_was_first = chunk.is_first_chunk();
847 let chunk_was_last = chunk.is_last_chunk();
848 let next_ptr = chunk.next;
849 let previous_ptr = chunk.previous;
850 let position_of_next = chunk.next().map(|next| next.first_position());
851
852 chunk.unlink(self.updates.as_mut());
853
854 let chunk_ptr = chunk.as_ptr();
855
856 if chunk_was_first {
858 if let Some(next_ptr) = next_ptr {
860 self.links.first = next_ptr;
861 }
862 }
863
864 if chunk_was_last {
865 self.links.last = previous_ptr;
866 }
867
868 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
871
872 Ok(position_of_next)
874 }
875
876 pub fn replace_gap_at<I>(
884 &mut self,
885 items: I,
886 chunk_identifier: ChunkIdentifier,
887 ) -> Result<&Chunk<CAP, Item, Gap>, Error>
888 where
889 Item: Clone,
890 Gap: Clone,
891 I: IntoIterator<Item = Item>,
892 I::IntoIter: ExactSizeIterator,
893 {
894 let chunk_ptr;
895 let new_chunk_ptr;
896
897 {
898 let chunk = self
899 .links
900 .chunk_mut(chunk_identifier)
901 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
902
903 if chunk.is_items() {
904 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
905 }
906
907 let chunk_was_first = chunk.is_first_chunk();
908
909 let maybe_last_chunk_ptr = {
910 let items = items.into_iter();
911
912 let last_inserted_chunk = chunk
913 .insert_next(
915 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
916 &mut self.updates,
917 )
918 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
920
921 last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
922 };
923
924 new_chunk_ptr = chunk
925 .next
926 .unwrap();
928
929 chunk.unlink(self.updates.as_mut());
931
932 chunk_ptr = chunk.as_ptr();
934
935 if chunk_was_first {
937 self.links.first = new_chunk_ptr;
938 }
939
940 if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
943 self.links.last = Some(last_chunk_ptr);
944 }
945
946 }
948
949 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
954
955 Ok(
956 unsafe { new_chunk_ptr.as_ref() },
960 )
961 }
962
963 pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
965 where
966 P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
967 {
968 self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
969 }
970
971 pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
973 where
974 P: FnMut(&'a Item) -> bool,
975 {
976 self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
977 }
978
979 pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
983 IterBackward::new(self.links.latest_chunk())
984 }
985
986 pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
990 Iter::new(self.links.first_chunk())
991 }
992
993 pub fn rchunks_from(
998 &self,
999 identifier: ChunkIdentifier,
1000 ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
1001 Ok(IterBackward::new(
1002 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
1003 ))
1004 }
1005
1006 pub fn chunks_from(
1011 &self,
1012 identifier: ChunkIdentifier,
1013 ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
1014 Ok(Iter::new(
1015 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
1016 ))
1017 }
1018
1019 pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
1023 self.ritems_from(self.links.latest_chunk().last_position())
1024 .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
1025 }
1026
1027 pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
1031 let first_chunk = self.links.first_chunk();
1032
1033 self.items_from(first_chunk.first_position())
1034 .expect("`items` cannot fail because at least one empty chunk must exist")
1035 }
1036
1037 pub fn ritems_from(
1041 &self,
1042 position: Position,
1043 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1044 Ok(self
1045 .rchunks_from(position.chunk_identifier())?
1046 .filter_map(|chunk| match &chunk.content {
1047 ChunkContent::Gap(..) => None,
1048 ChunkContent::Items(items) => {
1049 let identifier = chunk.identifier();
1050
1051 Some(
1052 items.iter().enumerate().rev().map(move |(item_index, item)| {
1053 (Position(identifier, item_index), item)
1054 }),
1055 )
1056 }
1057 })
1058 .flatten()
1059 .skip_while({
1060 let expected_index = position.index();
1061
1062 move |(Position(chunk_identifier, item_index), _item)| {
1063 *chunk_identifier == position.chunk_identifier()
1064 && *item_index != expected_index
1065 }
1066 }))
1067 }
1068
1069 pub fn items_from(
1073 &self,
1074 position: Position,
1075 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1076 Ok(self
1077 .chunks_from(position.chunk_identifier())?
1078 .filter_map(|chunk| match &chunk.content {
1079 ChunkContent::Gap(..) => None,
1080 ChunkContent::Items(items) => {
1081 let identifier = chunk.identifier();
1082
1083 Some(
1084 items.iter().enumerate().map(move |(item_index, item)| {
1085 (Position(identifier, item_index), item)
1086 }),
1087 )
1088 }
1089 })
1090 .flatten()
1091 .skip(position.index()))
1092 }
1093
1094 #[must_use]
1106 pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
1107 self.updates.as_mut()
1108 }
1109
1110 pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
1117 let (updates, token) = self
1118 .updates
1119 .as_mut()
1120 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1121 let chunk_iterator = self.chunks();
1122
1123 Some(AsVector::new(updates, token, chunk_iterator))
1124 }
1125
1126 pub fn order_tracker(
1134 &mut self,
1135 all_chunks: Option<Vec<ChunkMetadata>>,
1136 ) -> Option<OrderTracker<Item, Gap>>
1137 where
1138 Item: Clone,
1139 {
1140 let (updates, token) = self
1141 .updates
1142 .as_mut()
1143 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1144
1145 Some(OrderTracker::new(
1146 updates,
1147 token,
1148 all_chunks.unwrap_or_else(|| {
1149 self.chunks()
1151 .map(|chunk| ChunkMetadata {
1152 identifier: chunk.identifier(),
1153 num_items: chunk.num_items(),
1154 previous: chunk.previous().map(|prev| prev.identifier()),
1155 next: chunk.next().map(|next| next.identifier()),
1156 })
1157 .collect()
1158 }),
1159 ))
1160 }
1161
1162 pub fn num_items(&self) -> usize {
1164 self.items().count()
1165 }
1166}
1167
1168impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
1169 fn drop(&mut self) {
1170 unsafe {
1180 self.links.clear();
1181 }
1182 }
1183}
1184
1185unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1189
1190unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1194
1195#[derive(Debug)]
1205pub struct ChunkIdentifierGenerator {
1206 next: AtomicU64,
1207}
1208
1209impl ChunkIdentifierGenerator {
1210 const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1212
1213 pub fn new_from_scratch() -> Self {
1216 Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1217 }
1218
1219 pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1222 Self { next: AtomicU64::new(last_chunk_identifier.0) }
1223 }
1224
1225 fn next(&self) -> ChunkIdentifier {
1230 let previous = self.next.fetch_add(1, atomic::Ordering::Relaxed);
1231
1232 if previous == u64::MAX {
1235 panic!(
1236 "No more chunk identifiers available. Congrats, you did it. \
1237 2^64 identifiers have been consumed."
1238 )
1239 }
1240
1241 ChunkIdentifier(previous + 1)
1242 }
1243
1244 #[doc(hidden)]
1248 pub fn current(&self) -> ChunkIdentifier {
1249 ChunkIdentifier(self.next.load(atomic::Ordering::Relaxed))
1250 }
1251}
1252
1253#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1259#[repr(transparent)]
1260pub struct ChunkIdentifier(u64);
1261
1262impl ChunkIdentifier {
1263 pub fn new(identifier: u64) -> Self {
1265 Self(identifier)
1266 }
1267
1268 pub fn index(&self) -> u64 {
1270 self.0
1271 }
1272}
1273
1274impl PartialEq<u64> for ChunkIdentifier {
1275 fn eq(&self, other: &u64) -> bool {
1276 self.0 == *other
1277 }
1278}
1279
1280#[derive(Copy, Clone, Debug, PartialEq)]
1284pub struct Position(ChunkIdentifier, usize);
1285
1286impl Position {
1287 pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1289 Self(chunk_identifier, index)
1290 }
1291
1292 pub fn chunk_identifier(&self) -> ChunkIdentifier {
1294 self.0
1295 }
1296
1297 pub fn index(&self) -> usize {
1299 self.1
1300 }
1301
1302 pub fn decrement_index(&mut self) {
1308 self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1309 }
1310
1311 pub fn increment_index(&mut self) {
1318 self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1319 }
1320}
1321
1322#[derive(Debug)]
1325pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1326 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1327}
1328
1329impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1330 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1332 Self { chunk: Some(from_chunk) }
1333 }
1334}
1335
1336impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1337 type Item = &'a Chunk<CAP, Item, Gap>;
1338
1339 fn next(&mut self) -> Option<Self::Item> {
1340 self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1341 }
1342}
1343
1344#[derive(Debug)]
1347pub struct Iter<'a, const CAP: usize, Item, Gap> {
1348 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1349}
1350
1351impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1352 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1354 Self { chunk: Some(from_chunk) }
1355 }
1356}
1357
1358impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1359 type Item = &'a Chunk<CAP, Item, Gap>;
1360
1361 fn next(&mut self) -> Option<Self::Item> {
1362 self.chunk.inspect(|chunk| self.chunk = chunk.next())
1363 }
1364}
1365
1366#[derive(Clone, Debug)]
1368pub enum ChunkContent<Item, Gap> {
1369 Gap(Gap),
1372
1373 Items(Vec<Item>),
1375}
1376
1377pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1379 previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1381
1382 lazy_previous: Option<ChunkIdentifier>,
1387
1388 next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1390
1391 identifier: ChunkIdentifier,
1393
1394 content: ChunkContent<Item, Gap>,
1396}
1397
1398impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1399 fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1401 Self::new(identifier, ChunkContent::Gap(content))
1402 }
1403
1404 fn new_items(identifier: ChunkIdentifier) -> Self {
1406 Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1407 }
1408
1409 fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1410 Self { previous: None, lazy_previous: None, next: None, identifier, content }
1411 }
1412
1413 fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1415 let chunk = Self::new(identifier, content);
1416 let chunk_box = Box::new(chunk);
1417
1418 NonNull::from(Box::leak(chunk_box))
1419 }
1420
1421 fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1423 let chunk = Self::new_gap(identifier, content);
1424 let chunk_box = Box::new(chunk);
1425
1426 NonNull::from(Box::leak(chunk_box))
1427 }
1428
1429 fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1431 let chunk = Self::new_items(identifier);
1432 let chunk_box = Box::new(chunk);
1433
1434 NonNull::from(Box::leak(chunk_box))
1435 }
1436
1437 pub fn as_ptr(&self) -> NonNull<Self> {
1439 NonNull::from(self)
1440 }
1441
1442 pub fn is_gap(&self) -> bool {
1444 matches!(self.content, ChunkContent::Gap(..))
1445 }
1446
1447 pub fn is_items(&self) -> bool {
1449 !self.is_gap()
1450 }
1451
1452 pub fn is_definitive_head(&self) -> bool {
1455 self.previous.is_none() && self.lazy_previous.is_none()
1456 }
1457
1458 fn is_first_chunk(&self) -> bool {
1460 self.previous.is_none()
1461 }
1462
1463 fn is_last_chunk(&self) -> bool {
1465 self.next.is_none()
1466 }
1467
1468 #[doc(hidden)]
1472 pub fn lazy_previous(&self) -> Option<ChunkIdentifier> {
1473 self.lazy_previous
1474 }
1475
1476 pub fn identifier(&self) -> ChunkIdentifier {
1478 self.identifier
1479 }
1480
1481 pub fn content(&self) -> &ChunkContent<Item, Gap> {
1483 &self.content
1484 }
1485
1486 pub fn first_position(&self) -> Position {
1490 Position(self.identifier(), 0)
1491 }
1492
1493 pub fn last_position(&self) -> Position {
1497 let identifier = self.identifier();
1498
1499 match &self.content {
1500 ChunkContent::Gap(..) => Position(identifier, 0),
1501 ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1502 }
1503 }
1504
1505 pub fn num_items(&self) -> usize {
1509 match &self.content {
1510 ChunkContent::Gap(..) => 0,
1511 ChunkContent::Items(items) => items.len(),
1512 }
1513 }
1514
1515 fn push_items<I>(
1527 &mut self,
1528 mut new_items: I,
1529 chunk_identifier_generator: &ChunkIdentifierGenerator,
1530 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1531 ) -> &mut Self
1532 where
1533 I: Iterator<Item = Item> + ExactSizeIterator,
1534 Item: Clone,
1535 Gap: Clone,
1536 {
1537 if new_items.len() == 0 {
1539 return self;
1540 }
1541
1542 let identifier = self.identifier();
1543 let prev_num_items = self.num_items();
1544
1545 match &mut self.content {
1546 ChunkContent::Gap(..) => {
1549 self
1550 .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1552 .push_items(new_items, chunk_identifier_generator, updates)
1555 }
1556
1557 ChunkContent::Items(items) => {
1558 let free_space = CAPACITY.saturating_sub(prev_num_items);
1560
1561 if new_items.len() <= free_space {
1563 let start = items.len();
1564 items.extend(new_items);
1565
1566 if let Some(updates) = updates.as_mut() {
1567 updates.push(Update::PushItems {
1568 at: Position(identifier, start),
1569 items: items[start..].to_vec(),
1570 });
1571 }
1572
1573 self
1575 } else {
1576 if free_space > 0 {
1577 let start = items.len();
1579 items.extend(new_items.by_ref().take(free_space));
1580
1581 if let Some(updates) = updates.as_mut() {
1582 updates.push(Update::PushItems {
1583 at: Position(identifier, start),
1584 items: items[start..].to_vec(),
1585 });
1586 }
1587 }
1588
1589 self
1590 .insert_next(
1592 Self::new_items_leaked(chunk_identifier_generator.next()),
1593 updates,
1594 )
1595 .push_items(new_items, chunk_identifier_generator, updates)
1598 }
1599 }
1600 }
1601 }
1602
1603 fn insert_next(
1608 &mut self,
1609 mut new_chunk_ptr: NonNull<Self>,
1610 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1611 ) -> &mut Self
1612 where
1613 Gap: Clone,
1614 {
1615 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1616
1617 if let Some(next_chunk) = self.next_mut() {
1619 next_chunk.previous = Some(new_chunk_ptr);
1621
1622 new_chunk.next = self.next;
1624 }
1625
1626 self.next = Some(new_chunk_ptr);
1628 new_chunk.previous = Some(self.as_ptr());
1630
1631 if let Some(updates) = updates.as_mut() {
1632 let previous = new_chunk.previous().map(Chunk::identifier);
1633 let new = new_chunk.identifier();
1634 let next = new_chunk.next().map(Chunk::identifier);
1635
1636 match new_chunk.content() {
1637 ChunkContent::Gap(gap) => {
1638 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1639 }
1640
1641 ChunkContent::Items(..) => {
1642 updates.push(Update::NewItemsChunk { previous, new, next })
1643 }
1644 }
1645 }
1646
1647 new_chunk
1648 }
1649
1650 fn insert_before(
1655 &mut self,
1656 mut new_chunk_ptr: NonNull<Self>,
1657 updates: Option<&mut ObservableUpdates<Item, Gap>>,
1658 ) -> &mut Self
1659 where
1660 Gap: Clone,
1661 {
1662 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1663
1664 if let Some(previous_chunk) = self.previous_mut() {
1666 previous_chunk.next = Some(new_chunk_ptr);
1668
1669 new_chunk.previous = self.previous;
1671 }
1672 else {
1675 new_chunk.lazy_previous = self.lazy_previous.take();
1676 }
1677
1678 self.previous = Some(new_chunk_ptr);
1680 new_chunk.next = Some(self.as_ptr());
1682
1683 if let Some(updates) = updates {
1684 let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1685 let new = new_chunk.identifier();
1686 let next = new_chunk.next().map(Chunk::identifier);
1687
1688 match new_chunk.content() {
1689 ChunkContent::Gap(gap) => {
1690 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1691 }
1692
1693 ChunkContent::Items(..) => {
1694 updates.push(Update::NewItemsChunk { previous, new, next })
1695 }
1696 }
1697 }
1698
1699 new_chunk
1700 }
1701
1702 fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1707 let previous_ptr = self.previous;
1708 let next_ptr = self.next;
1709 let lazy_previous = self.lazy_previous.take();
1713
1714 if let Some(previous) = self.previous_mut() {
1715 previous.next = next_ptr;
1716 }
1717
1718 if let Some(next) = self.next_mut() {
1719 next.previous = previous_ptr;
1720 next.lazy_previous = lazy_previous;
1721 }
1722
1723 if let Some(updates) = updates {
1724 updates.push(Update::RemoveChunk(self.identifier()));
1725 }
1726 }
1727
1728 fn previous(&self) -> Option<&Self> {
1730 self.previous.map(|non_null| unsafe { non_null.as_ref() })
1731 }
1732
1733 fn previous_mut(&mut self) -> Option<&mut Self> {
1735 self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1736 }
1737
1738 fn next(&self) -> Option<&Self> {
1740 self.next.map(|non_null| unsafe { non_null.as_ref() })
1741 }
1742
1743 fn next_mut(&mut self) -> Option<&mut Self> {
1745 self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1746 }
1747}
1748
1749impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<CAP, Item, Gap>
1750where
1751 Item: fmt::Debug,
1752 Gap: fmt::Debug,
1753{
1754 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1755 formatter
1756 .debug_struct("LinkedChunk")
1757 .field("first (deref)", unsafe { self.links.first.as_ref() })
1758 .field("last", &self.links.last)
1759 .finish_non_exhaustive()
1760 }
1761}
1762
1763impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1764where
1765 Item: fmt::Debug,
1766 Gap: fmt::Debug,
1767{
1768 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1769 formatter
1770 .debug_struct("Chunk")
1771 .field("identifier", &self.identifier)
1772 .field("content", &self.content)
1773 .field("previous", &self.previous)
1774 .field("ptr", &std::ptr::from_ref(self))
1775 .field("next", &self.next)
1776 .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1777 .finish()
1778 }
1779}
1780
1781#[derive(Clone, Debug)]
1787pub struct RawChunk<Item, Gap> {
1788 pub content: ChunkContent<Item, Gap>,
1790
1791 pub previous: Option<ChunkIdentifier>,
1793
1794 pub identifier: ChunkIdentifier,
1796
1797 pub next: Option<ChunkIdentifier>,
1799}
1800
1801#[derive(Clone, Debug)]
1804pub struct ChunkMetadata {
1805 pub num_items: usize,
1809
1810 pub previous: Option<ChunkIdentifier>,
1812
1813 pub identifier: ChunkIdentifier,
1815
1816 pub next: Option<ChunkIdentifier>,
1818}
1819
1820#[cfg(test)]
1821mod tests {
1822 use std::{
1823 ops::Not,
1824 sync::{Arc, atomic::Ordering},
1825 };
1826
1827 use assert_matches::assert_matches;
1828
1829 use super::{
1830 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Error, LinkedChunk,
1831 Position,
1832 };
1833
1834 #[test]
1835 fn test_chunk_identifier_generator() {
1836 let generator = ChunkIdentifierGenerator::new_from_scratch();
1837
1838 assert_eq!(generator.next(), ChunkIdentifier(1));
1839 assert_eq!(generator.next(), ChunkIdentifier(2));
1840 assert_eq!(generator.next(), ChunkIdentifier(3));
1841 assert_eq!(generator.next(), ChunkIdentifier(4));
1842
1843 let generator =
1844 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1845
1846 assert_eq!(generator.next(), ChunkIdentifier(43));
1847 assert_eq!(generator.next(), ChunkIdentifier(44));
1848 assert_eq!(generator.next(), ChunkIdentifier(45));
1849 assert_eq!(generator.next(), ChunkIdentifier(46));
1850 }
1851
1852 #[test]
1853 fn test_empty() {
1854 let items = LinkedChunk::<3, char, ()>::new();
1855
1856 assert_eq!(items.num_items(), 0);
1857
1858 }
1861
1862 #[test]
1863 fn test_updates() {
1864 assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1865 assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1866 }
1867
1868 #[test]
1869 fn test_new_with_initial_update() {
1870 use super::Update::*;
1871
1872 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1873
1874 assert_eq!(
1875 linked_chunk.updates().unwrap().take(),
1876 &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1877 );
1878 }
1879
1880 #[test]
1881 fn test_push_items() {
1882 use super::Update::*;
1883
1884 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1885
1886 let _ = linked_chunk.updates().unwrap().take();
1888
1889 linked_chunk.push_items_back(['a']);
1890
1891 assert_items_eq!(linked_chunk, ['a']);
1892 assert_eq!(
1893 linked_chunk.updates().unwrap().take(),
1894 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1895 );
1896
1897 linked_chunk.push_items_back(['b', 'c']);
1898 assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1899 assert_eq!(
1900 linked_chunk.updates().unwrap().take(),
1901 &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1902 );
1903
1904 linked_chunk.push_items_back(['d', 'e']);
1905 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
1906 assert_eq!(
1907 linked_chunk.updates().unwrap().take(),
1908 &[
1909 NewItemsChunk {
1910 previous: Some(ChunkIdentifier(0)),
1911 new: ChunkIdentifier(1),
1912 next: None
1913 },
1914 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1915 ]
1916 );
1917
1918 linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1919 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1920 assert_eq!(
1921 linked_chunk.updates().unwrap().take(),
1922 &[
1923 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1924 NewItemsChunk {
1925 previous: Some(ChunkIdentifier(1)),
1926 new: ChunkIdentifier(2),
1927 next: None,
1928 },
1929 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1930 NewItemsChunk {
1931 previous: Some(ChunkIdentifier(2)),
1932 new: ChunkIdentifier(3),
1933 next: None,
1934 },
1935 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1936 ]
1937 );
1938
1939 assert_eq!(linked_chunk.num_items(), 10);
1940 }
1941
1942 #[test]
1943 fn test_push_gap() {
1944 use super::Update::*;
1945
1946 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1947
1948 let _ = linked_chunk.updates().unwrap().take();
1950
1951 linked_chunk.push_items_back(['a']);
1952 assert_items_eq!(linked_chunk, ['a']);
1953 assert_eq!(
1954 linked_chunk.updates().unwrap().take(),
1955 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1956 );
1957
1958 linked_chunk.push_gap_back(());
1959 assert_items_eq!(linked_chunk, ['a'] [-]);
1960 assert_eq!(
1961 linked_chunk.updates().unwrap().take(),
1962 &[NewGapChunk {
1963 previous: Some(ChunkIdentifier(0)),
1964 new: ChunkIdentifier(1),
1965 next: None,
1966 gap: (),
1967 }]
1968 );
1969
1970 linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1971 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1972 assert_eq!(
1973 linked_chunk.updates().unwrap().take(),
1974 &[
1975 NewItemsChunk {
1976 previous: Some(ChunkIdentifier(1)),
1977 new: ChunkIdentifier(2),
1978 next: None,
1979 },
1980 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
1981 NewItemsChunk {
1982 previous: Some(ChunkIdentifier(2)),
1983 new: ChunkIdentifier(3),
1984 next: None,
1985 },
1986 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
1987 ]
1988 );
1989
1990 linked_chunk.push_gap_back(());
1991 linked_chunk.push_gap_back(()); assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
1993 assert_eq!(
1994 linked_chunk.updates().unwrap().take(),
1995 &[
1996 NewGapChunk {
1997 previous: Some(ChunkIdentifier(3)),
1998 new: ChunkIdentifier(4),
1999 next: None,
2000 gap: (),
2001 },
2002 NewGapChunk {
2003 previous: Some(ChunkIdentifier(4)),
2004 new: ChunkIdentifier(5),
2005 next: None,
2006 gap: (),
2007 }
2008 ]
2009 );
2010
2011 linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
2012 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
2013 assert_eq!(
2014 linked_chunk.updates().unwrap().take(),
2015 &[
2016 NewItemsChunk {
2017 previous: Some(ChunkIdentifier(5)),
2018 new: ChunkIdentifier(6),
2019 next: None,
2020 },
2021 PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
2022 NewItemsChunk {
2023 previous: Some(ChunkIdentifier(6)),
2024 new: ChunkIdentifier(7),
2025 next: None,
2026 },
2027 PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
2028 ]
2029 );
2030
2031 assert_eq!(linked_chunk.num_items(), 9);
2032 }
2033
2034 #[test]
2035 fn test_identifiers_and_positions() {
2036 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2037 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2038 linked_chunk.push_gap_back(());
2039 linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
2040 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
2041
2042 assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
2043 assert_eq!(
2044 linked_chunk.item_position(|item| *item == 'e'),
2045 Some(Position(ChunkIdentifier(1), 1))
2046 );
2047 }
2048
2049 #[test]
2050 fn test_rchunks() {
2051 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2052 linked_chunk.push_items_back(['a', 'b']);
2053 linked_chunk.push_gap_back(());
2054 linked_chunk.push_items_back(['c', 'd', 'e']);
2055
2056 let mut iterator = linked_chunk.rchunks();
2057
2058 assert_matches!(
2059 iterator.next(),
2060 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2061 assert_eq!(items, &['e']);
2062 }
2063 );
2064 assert_matches!(
2065 iterator.next(),
2066 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2067 assert_eq!(items, &['c', 'd']);
2068 }
2069 );
2070 assert_matches!(
2071 iterator.next(),
2072 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2073 );
2074 assert_matches!(
2075 iterator.next(),
2076 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2077 assert_eq!(items, &['a', 'b']);
2078 }
2079 );
2080 assert_matches!(iterator.next(), None);
2081 }
2082
2083 #[test]
2084 fn test_chunks() {
2085 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2086 linked_chunk.push_items_back(['a', 'b']);
2087 linked_chunk.push_gap_back(());
2088 linked_chunk.push_items_back(['c', 'd', 'e']);
2089
2090 let mut iterator = linked_chunk.chunks();
2091
2092 assert_matches!(
2093 iterator.next(),
2094 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2095 assert_eq!(items, &['a', 'b']);
2096 }
2097 );
2098 assert_matches!(
2099 iterator.next(),
2100 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2101 );
2102 assert_matches!(
2103 iterator.next(),
2104 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2105 assert_eq!(items, &['c', 'd']);
2106 }
2107 );
2108 assert_matches!(
2109 iterator.next(),
2110 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2111 assert_eq!(items, &['e']);
2112 }
2113 );
2114 assert_matches!(iterator.next(), None);
2115 }
2116
2117 #[test]
2118 fn test_rchunks_from() -> Result<(), Error> {
2119 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2120 linked_chunk.push_items_back(['a', 'b']);
2121 linked_chunk.push_gap_back(());
2122 linked_chunk.push_items_back(['c', 'd', 'e']);
2123
2124 let mut iterator = linked_chunk.rchunks_from(
2125 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2126 )?;
2127
2128 assert_matches!(
2129 iterator.next(),
2130 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2131 assert_eq!(items, &['c', 'd']);
2132 }
2133 );
2134 assert_matches!(
2135 iterator.next(),
2136 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2137 );
2138 assert_matches!(
2139 iterator.next(),
2140 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2141 assert_eq!(items, &['a', 'b']);
2142 }
2143 );
2144 assert_matches!(iterator.next(), None);
2145
2146 Ok(())
2147 }
2148
2149 #[test]
2150 fn test_chunks_from() -> Result<(), Error> {
2151 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2152 linked_chunk.push_items_back(['a', 'b']);
2153 linked_chunk.push_gap_back(());
2154 linked_chunk.push_items_back(['c', 'd', 'e']);
2155
2156 let mut iterator = linked_chunk.chunks_from(
2157 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2158 )?;
2159
2160 assert_matches!(
2161 iterator.next(),
2162 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2163 assert_eq!(items, &['c', 'd']);
2164 }
2165 );
2166 assert_matches!(
2167 iterator.next(),
2168 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2169 assert_eq!(items, &['e']);
2170 }
2171 );
2172 assert_matches!(iterator.next(), None);
2173
2174 Ok(())
2175 }
2176
2177 #[test]
2178 fn test_ritems() {
2179 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2180 linked_chunk.push_items_back(['a', 'b']);
2181 linked_chunk.push_gap_back(());
2182 linked_chunk.push_items_back(['c', 'd', 'e']);
2183
2184 let mut iterator = linked_chunk.ritems();
2185
2186 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2187 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2188 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2189 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2190 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2191 assert_matches!(iterator.next(), None);
2192 }
2193
2194 #[test]
2195 fn test_ritems_with_final_gap() -> Result<(), Error> {
2196 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2197 linked_chunk.push_items_back(['a', 'b']);
2198 linked_chunk.push_gap_back(());
2199 linked_chunk.push_items_back(['c', 'd', 'e']);
2200 linked_chunk.push_gap_back(());
2201
2202 let mut iterator = linked_chunk.ritems();
2203
2204 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
2205 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2206 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2207 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2208 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2209 assert_matches!(iterator.next(), None);
2210
2211 Ok(())
2212 }
2213
2214 #[test]
2215 fn test_ritems_empty() {
2216 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2217 let mut iterator = linked_chunk.ritems();
2218
2219 assert_matches!(iterator.next(), None);
2220 }
2221
2222 #[test]
2223 fn test_items() {
2224 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2225 linked_chunk.push_items_back(['a', 'b']);
2226 linked_chunk.push_gap_back(());
2227 linked_chunk.push_items_back(['c', 'd', 'e']);
2228
2229 let mut iterator = linked_chunk.items();
2230
2231 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2232 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2233 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2234 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2235 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2236 assert_matches!(iterator.next(), None);
2237 }
2238
2239 #[test]
2240 fn test_items_empty() {
2241 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2242 let mut iterator = linked_chunk.items();
2243
2244 assert_matches!(iterator.next(), None);
2245 }
2246
2247 #[test]
2248 fn test_ritems_from() -> Result<(), Error> {
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 =
2255 linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2256
2257 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2258 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2259 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2260 assert_matches!(iterator.next(), None);
2261
2262 Ok(())
2263 }
2264
2265 #[test]
2266 fn test_items_from() -> Result<(), Error> {
2267 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2268 linked_chunk.push_items_back(['a', 'b']);
2269 linked_chunk.push_gap_back(());
2270 linked_chunk.push_items_back(['c', 'd', 'e']);
2271
2272 let mut iterator =
2273 linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2274
2275 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2276 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2277 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2278 assert_matches!(iterator.next(), None);
2279
2280 Ok(())
2281 }
2282
2283 #[test]
2284 fn test_insert_items_at() -> Result<(), Error> {
2285 use super::Update::*;
2286
2287 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2288
2289 let _ = linked_chunk.updates().unwrap().take();
2291
2292 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2293 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2294 assert_eq!(
2295 linked_chunk.updates().unwrap().take(),
2296 &[
2297 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2298 NewItemsChunk {
2299 previous: Some(ChunkIdentifier(0)),
2300 new: ChunkIdentifier(1),
2301 next: None,
2302 },
2303 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2304 ]
2305 );
2306
2307 {
2309 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2310
2311 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2314
2315 assert_items_eq!(
2316 linked_chunk,
2317 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2318 );
2319 assert_eq!(linked_chunk.num_items(), 10);
2320 assert_eq!(
2321 linked_chunk.updates().unwrap().take(),
2322 &[
2323 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2324 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2325 NewItemsChunk {
2326 previous: Some(ChunkIdentifier(1)),
2327 new: ChunkIdentifier(2),
2328 next: None,
2329 },
2330 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2331 StartReattachItems,
2332 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2333 NewItemsChunk {
2334 previous: Some(ChunkIdentifier(2)),
2335 new: ChunkIdentifier(3),
2336 next: None,
2337 },
2338 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2339 EndReattachItems,
2340 ]
2341 );
2342 }
2343
2344 {
2346 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2347 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2348
2349 assert_items_eq!(
2350 linked_chunk,
2351 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2352 );
2353 assert_eq!(linked_chunk.num_items(), 14);
2354 assert_eq!(
2355 linked_chunk.updates().unwrap().take(),
2356 &[
2357 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2358 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2359 NewItemsChunk {
2360 previous: Some(ChunkIdentifier(0)),
2361 new: ChunkIdentifier(4),
2362 next: Some(ChunkIdentifier(1)),
2363 },
2364 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2365 StartReattachItems,
2366 PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2367 NewItemsChunk {
2368 previous: Some(ChunkIdentifier(4)),
2369 new: ChunkIdentifier(5),
2370 next: Some(ChunkIdentifier(1)),
2371 },
2372 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2373 EndReattachItems,
2374 ]
2375 );
2376 }
2377
2378 {
2380 let pos_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2381 linked_chunk.insert_items_at(pos_c, ['r', 's'])?;
2382
2383 assert_items_eq!(
2384 linked_chunk,
2385 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2386 );
2387 assert_eq!(linked_chunk.num_items(), 16);
2388 assert_eq!(
2389 linked_chunk.updates().unwrap().take(),
2390 &[
2391 DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2392 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2393 StartReattachItems,
2394 PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2395 EndReattachItems,
2396 ]
2397 );
2398 }
2399
2400 {
2402 let pos_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2403 let pos_f = Position(pos_f.chunk_identifier(), pos_f.index() + 1);
2404
2405 linked_chunk.insert_items_at(pos_f, ['p', 'q'])?;
2406 assert_items_eq!(
2407 linked_chunk,
2408 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2409 );
2410 assert_eq!(
2411 linked_chunk.updates().unwrap().take(),
2412 &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2413 );
2414 assert_eq!(linked_chunk.num_items(), 18);
2415 }
2416
2417 {
2419 assert_matches!(
2420 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2421 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2422 );
2423 assert!(linked_chunk.updates().unwrap().take().is_empty());
2424 }
2425
2426 {
2428 assert_matches!(
2429 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2430 Err(Error::InvalidItemIndex { index: 128 })
2431 );
2432 assert!(linked_chunk.updates().unwrap().take().is_empty());
2433 }
2434
2435 {
2437 linked_chunk.push_gap_back(());
2439 assert_items_eq!(
2440 linked_chunk,
2441 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2442 );
2443 assert_eq!(
2444 linked_chunk.updates().unwrap().take(),
2445 &[NewGapChunk {
2446 previous: Some(ChunkIdentifier(3)),
2447 new: ChunkIdentifier(6),
2448 next: None,
2449 gap: ()
2450 }]
2451 );
2452
2453 assert_matches!(
2454 linked_chunk.insert_items_at(Position(ChunkIdentifier(6), 0), ['u', 'v'],),
2455 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2456 );
2457 }
2458
2459 assert_eq!(linked_chunk.num_items(), 18);
2460
2461 Ok(())
2462 }
2463
2464 #[test]
2465 fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2466 use super::Update::*;
2467
2468 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2469
2470 let _ = linked_chunk.updates().unwrap().take();
2472
2473 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2474 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2475 assert_eq!(
2476 linked_chunk.updates().unwrap().take(),
2477 &[
2478 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2479 NewItemsChunk {
2480 previous: Some(ChunkIdentifier(0)),
2481 new: ChunkIdentifier(1),
2482 next: None,
2483 },
2484 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2485 ]
2486 );
2487
2488 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2490
2491 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2494
2495 assert_items_eq!(
2496 linked_chunk,
2497 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2498 );
2499 assert_eq!(linked_chunk.num_items(), 10);
2500 assert_eq!(
2501 linked_chunk.updates().unwrap().take(),
2502 &[
2503 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2504 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2505 NewItemsChunk {
2506 previous: Some(ChunkIdentifier(1)),
2507 new: ChunkIdentifier(2),
2508 next: None,
2509 },
2510 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2511 StartReattachItems,
2512 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2513 NewItemsChunk {
2514 previous: Some(ChunkIdentifier(2)),
2515 new: ChunkIdentifier(3),
2516 next: None,
2517 },
2518 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2519 EndReattachItems,
2520 ]
2521 );
2522
2523 Ok(())
2524 }
2525
2526 #[test]
2527 fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2528 use super::Update::*;
2529
2530 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2531
2532 let _ = linked_chunk.updates().unwrap().take();
2534
2535 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2536 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2537 assert_eq!(
2538 linked_chunk.updates().unwrap().take(),
2539 &[
2540 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2541 NewItemsChunk {
2542 previous: Some(ChunkIdentifier(0)),
2543 new: ChunkIdentifier(1),
2544 next: None,
2545 },
2546 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2547 ]
2548 );
2549
2550 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2552 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2553
2554 assert_items_eq!(
2555 linked_chunk,
2556 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'e', 'f']
2557 );
2558 assert_eq!(linked_chunk.num_items(), 10);
2559 assert_eq!(
2560 linked_chunk.updates().unwrap().take(),
2561 &[
2562 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2563 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2564 NewItemsChunk {
2565 previous: Some(ChunkIdentifier(0)),
2566 new: ChunkIdentifier(2),
2567 next: Some(ChunkIdentifier(1)),
2568 },
2569 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2570 StartReattachItems,
2571 PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2572 NewItemsChunk {
2573 previous: Some(ChunkIdentifier(2)),
2574 new: ChunkIdentifier(3),
2575 next: Some(ChunkIdentifier(1)),
2576 },
2577 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2578 EndReattachItems,
2579 ]
2580 );
2581
2582 Ok(())
2583 }
2584
2585 #[test]
2586 fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2587 use super::Update::*;
2588
2589 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2590
2591 let _ = linked_chunk.updates().unwrap().take();
2593
2594 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2595 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2596 assert_eq!(
2597 linked_chunk.updates().unwrap().take(),
2598 &[
2599 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2600 NewItemsChunk {
2601 previous: Some(ChunkIdentifier(0)),
2602 new: ChunkIdentifier(1),
2603 next: None,
2604 },
2605 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2606 NewItemsChunk {
2607 previous: Some(ChunkIdentifier(1)),
2608 new: ChunkIdentifier(2),
2609 next: None,
2610 },
2611 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2612 ]
2613 );
2614
2615 let pos_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2616 linked_chunk.insert_items_at(pos_d, ['r', 's'])?;
2617
2618 assert_items_eq!(
2619 linked_chunk,
2620 ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2621 );
2622 assert_eq!(linked_chunk.num_items(), 10);
2623 assert_eq!(
2624 linked_chunk.updates().unwrap().take(),
2625 &[
2626 DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2627 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2628 StartReattachItems,
2629 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2630 NewItemsChunk {
2631 previous: Some(ChunkIdentifier(1)),
2632 new: ChunkIdentifier(3),
2633 next: Some(ChunkIdentifier(2)),
2634 },
2635 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2636 EndReattachItems,
2637 ]
2638 );
2639
2640 Ok(())
2641 }
2642
2643 #[test]
2644 fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2645 use super::Update::*;
2646
2647 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2648
2649 let _ = linked_chunk.updates().unwrap().take();
2651
2652 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2653 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2654 assert_eq!(
2655 linked_chunk.updates().unwrap().take(),
2656 &[
2657 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2658 NewItemsChunk {
2659 previous: Some(ChunkIdentifier(0)),
2660 new: ChunkIdentifier(1),
2661 next: None,
2662 },
2663 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2664 ]
2665 );
2666
2667 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2669 let pos_after_e = Position(pos_e.chunk_identifier(), pos_e.index() + 1);
2670
2671 linked_chunk.insert_items_at(pos_after_e, ['p', 'q'])?;
2672 assert_items_eq!(
2673 linked_chunk,
2674 ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2675 );
2676 assert_eq!(
2677 linked_chunk.updates().unwrap().take(),
2678 &[
2679 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2680 NewItemsChunk {
2681 previous: Some(ChunkIdentifier(1)),
2682 new: ChunkIdentifier(2),
2683 next: None
2684 },
2685 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2686 ]
2687 );
2688 assert_eq!(linked_chunk.num_items(), 7);
2689
2690 Ok(())
2691 }
2692
2693 #[test]
2694 fn test_insert_items_at_errs() -> Result<(), Error> {
2695 use super::Update::*;
2696
2697 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2698
2699 let _ = linked_chunk.updates().unwrap().take();
2701
2702 linked_chunk.push_items_back(['a', 'b', 'c']);
2703 linked_chunk.push_gap_back(());
2704 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2705 assert_eq!(
2706 linked_chunk.updates().unwrap().take(),
2707 &[
2708 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2709 NewGapChunk {
2710 previous: Some(ChunkIdentifier(0)),
2711 new: ChunkIdentifier(1),
2712 next: None,
2713 gap: (),
2714 },
2715 ]
2716 );
2717
2718 {
2720 assert_matches!(
2721 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2722 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2723 );
2724 assert!(linked_chunk.updates().unwrap().take().is_empty());
2725 }
2726
2727 {
2729 assert_matches!(
2730 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2731 Err(Error::InvalidItemIndex { index: 128 })
2732 );
2733 assert!(linked_chunk.updates().unwrap().take().is_empty());
2734 }
2735
2736 {
2738 assert_matches!(
2739 linked_chunk.insert_items_at(Position(ChunkIdentifier(1), 0), ['u', 'v'],),
2740 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2741 );
2742 }
2743
2744 Ok(())
2745 }
2746
2747 #[test]
2748 fn test_remove_item_at() -> Result<(), Error> {
2749 use super::Update::*;
2750
2751 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2752
2753 let _ = linked_chunk.updates().unwrap().take();
2755
2756 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2757 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2758 assert_eq!(linked_chunk.num_items(), 11);
2759
2760 let _ = linked_chunk.updates().unwrap().take();
2762
2763 {
2766 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2767 let removed_item = linked_chunk.remove_item_at(position_of_f)?;
2768
2769 assert_eq!(removed_item, 'f');
2770 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2771 assert_eq!(linked_chunk.num_items(), 10);
2772
2773 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2774 let removed_item = linked_chunk.remove_item_at(position_of_e)?;
2775
2776 assert_eq!(removed_item, 'e');
2777 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2778 assert_eq!(linked_chunk.num_items(), 9);
2779
2780 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2781 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2782
2783 assert_eq!(removed_item, 'd');
2784 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2785 assert_eq!(linked_chunk.num_items(), 8);
2786
2787 assert_eq!(
2788 linked_chunk.updates().unwrap().take(),
2789 &[
2790 RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2791 RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2792 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2793 RemoveChunk(ChunkIdentifier(1)),
2794 ]
2795 );
2796 }
2797
2798 {
2801 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2802 let removed_item = linked_chunk.remove_item_at(first_position)?;
2803
2804 assert_eq!(removed_item, 'a');
2805 assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2806 assert_eq!(linked_chunk.num_items(), 7);
2807
2808 let removed_item = linked_chunk.remove_item_at(first_position)?;
2809
2810 assert_eq!(removed_item, 'b');
2811 assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2812 assert_eq!(linked_chunk.num_items(), 6);
2813
2814 let removed_item = linked_chunk.remove_item_at(first_position)?;
2815
2816 assert_eq!(removed_item, 'c');
2817 assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2818 assert_eq!(linked_chunk.num_items(), 5);
2819
2820 assert_eq!(
2821 linked_chunk.updates().unwrap().take(),
2822 &[
2823 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2824 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2825 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2826 ]
2827 );
2828 }
2829
2830 {
2833 let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2834 let removed_item = linked_chunk.remove_item_at(first_position)?;
2835
2836 assert_eq!(removed_item, 'g');
2837 assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2838 assert_eq!(linked_chunk.num_items(), 4);
2839
2840 let removed_item = linked_chunk.remove_item_at(first_position)?;
2841
2842 assert_eq!(removed_item, 'h');
2843 assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2844 assert_eq!(linked_chunk.num_items(), 3);
2845
2846 let removed_item = linked_chunk.remove_item_at(first_position)?;
2847
2848 assert_eq!(removed_item, 'i');
2849 assert_items_eq!(linked_chunk, [] ['j', 'k']);
2850 assert_eq!(linked_chunk.num_items(), 2);
2851
2852 assert_eq!(
2853 linked_chunk.updates().unwrap().take(),
2854 &[
2855 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2856 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2857 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2858 RemoveChunk(ChunkIdentifier(2)),
2859 ]
2860 );
2861 }
2862
2863 {
2866 let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2867 let removed_item = linked_chunk.remove_item_at(position_of_k)?;
2868
2869 assert_eq!(removed_item, 'k');
2870 #[rustfmt::skip]
2871 assert_items_eq!(linked_chunk, [] ['j']);
2872 assert_eq!(linked_chunk.num_items(), 1);
2873
2874 let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2875 let removed_item = linked_chunk.remove_item_at(position_of_j)?;
2876
2877 assert_eq!(removed_item, 'j');
2878 assert_items_eq!(linked_chunk, []);
2879 assert_eq!(linked_chunk.num_items(), 0);
2880
2881 assert_eq!(
2882 linked_chunk.updates().unwrap().take(),
2883 &[
2884 RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2885 RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2886 RemoveChunk(ChunkIdentifier(3)),
2887 ]
2888 );
2889 }
2890
2891 {
2893 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2894
2895 #[rustfmt::skip]
2896 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2897 assert_eq!(linked_chunk.num_items(), 4);
2898
2899 assert_matches!(
2901 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 3)),
2902 Err(Error::InvalidItemIndex { index: 3 })
2903 );
2904
2905 assert_matches!(
2907 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 42)),
2908 Err(Error::InvalidItemIndex { index: 42 })
2909 );
2910
2911 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2912 linked_chunk.insert_gap_at((), position_of_c)?;
2913
2914 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2915 assert_eq!(linked_chunk.num_items(), 4);
2916
2917 let _ = linked_chunk.updates().unwrap().take();
2919
2920 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2921 let removed_item = linked_chunk.remove_item_at(position_of_c)?;
2922
2923 assert_eq!(removed_item, 'c');
2924 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2925 assert_eq!(linked_chunk.num_items(), 3);
2926
2927 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2928 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2929
2930 assert_eq!(removed_item, 'd');
2931 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2932 assert_eq!(linked_chunk.num_items(), 2);
2933
2934 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2935 let removed_item = linked_chunk.remove_item_at(first_position)?;
2936
2937 assert_eq!(removed_item, 'a');
2938 assert_items_eq!(linked_chunk, ['b'] [-]);
2939 assert_eq!(linked_chunk.num_items(), 1);
2940
2941 let removed_item = linked_chunk.remove_item_at(first_position)?;
2942
2943 assert_eq!(removed_item, 'b');
2944 assert_items_eq!(linked_chunk, [] [-]);
2945 assert_eq!(linked_chunk.num_items(), 0);
2946
2947 assert_eq!(
2948 linked_chunk.updates().unwrap().take(),
2949 &[
2950 RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2951 RemoveChunk(ChunkIdentifier(6)),
2952 RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2953 RemoveChunk(ChunkIdentifier(4)),
2954 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2955 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2956 ]
2957 );
2958 }
2959
2960 Ok(())
2961 }
2962
2963 #[test]
2964 fn test_insert_gap_at() -> Result<(), Error> {
2965 use super::Update::*;
2966
2967 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2968
2969 let _ = linked_chunk.updates().unwrap().take();
2971
2972 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2973 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2974 assert_eq!(
2975 linked_chunk.updates().unwrap().take(),
2976 &[
2977 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2978 NewItemsChunk {
2979 previous: Some(ChunkIdentifier(0)),
2980 new: ChunkIdentifier(1),
2981 next: None
2982 },
2983 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2984 ]
2985 );
2986
2987 {
2989 let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
2990 linked_chunk.insert_gap_at((), position_of_b)?;
2991
2992 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2993 assert_eq!(
2994 linked_chunk.updates().unwrap().take(),
2995 &[
2996 DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
2997 NewGapChunk {
2998 previous: Some(ChunkIdentifier(0)),
2999 new: ChunkIdentifier(2),
3000 next: Some(ChunkIdentifier(1)),
3001 gap: (),
3002 },
3003 StartReattachItems,
3004 NewItemsChunk {
3005 previous: Some(ChunkIdentifier(2)),
3006 new: ChunkIdentifier(3),
3007 next: Some(ChunkIdentifier(1)),
3008 },
3009 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
3010 EndReattachItems,
3011 ]
3012 );
3013 }
3014
3015 {
3018 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3019 linked_chunk.insert_gap_at((), position_of_a)?;
3020
3021 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
3024 assert_eq!(
3025 linked_chunk.updates().unwrap().take(),
3026 &[NewGapChunk {
3027 previous: None,
3028 new: ChunkIdentifier(4),
3029 next: Some(ChunkIdentifier(0)),
3030 gap: (),
3031 },]
3032 );
3033 }
3034
3035 {
3038 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
3039 linked_chunk.insert_gap_at((), position_of_d)?;
3040
3041 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
3045 assert_eq!(
3046 linked_chunk.updates().unwrap().take(),
3047 &[NewGapChunk {
3048 previous: Some(ChunkIdentifier(3)),
3049 new: ChunkIdentifier(5),
3050 next: Some(ChunkIdentifier(1)),
3051 gap: (),
3052 }]
3053 );
3054 }
3055
3056 {
3058 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3060 let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
3061
3062 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
3063
3064 assert_eq!(
3065 linked_chunk.updates().unwrap().take(),
3066 &[
3067 NewItemsChunk {
3068 previous: Some(ChunkIdentifier(5)),
3069 new: ChunkIdentifier(6),
3070 next: Some(ChunkIdentifier(1)),
3071 },
3072 RemoveChunk(ChunkIdentifier(5)),
3073 ]
3074 );
3075
3076 linked_chunk.insert_gap_at((), position)?;
3077
3078 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
3079 assert_eq!(
3080 linked_chunk.updates().unwrap().take(),
3081 &[NewGapChunk {
3082 previous: Some(ChunkIdentifier(3)),
3083 new: ChunkIdentifier(7),
3084 next: Some(ChunkIdentifier(6)),
3085 gap: (),
3086 }]
3087 );
3088 }
3089
3090 {
3092 assert_matches!(
3093 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
3094 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
3095 );
3096 assert!(linked_chunk.updates().unwrap().take().is_empty());
3097 }
3098
3099 {
3101 assert_matches!(
3102 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
3103 Err(Error::InvalidItemIndex { index: 128 })
3104 );
3105 assert!(linked_chunk.updates().unwrap().take().is_empty());
3106 }
3107
3108 {
3110 let position_of_a_gap = Position(ChunkIdentifier(2), 0);
3113 assert_matches!(
3114 linked_chunk.insert_gap_at((), position_of_a_gap),
3115 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
3116 );
3117 assert!(linked_chunk.updates().unwrap().take().is_empty());
3118 }
3119
3120 assert_eq!(linked_chunk.num_items(), 6);
3121
3122 Ok(())
3123 }
3124
3125 #[test]
3126 fn test_replace_gap_at_middle() -> Result<(), Error> {
3127 use super::Update::*;
3128
3129 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3130
3131 let _ = linked_chunk.updates().unwrap().take();
3133
3134 linked_chunk.push_items_back(['a', 'b']);
3135 linked_chunk.push_gap_back(());
3136 linked_chunk.push_items_back(['l', 'm']);
3137 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
3138 assert_eq!(
3139 linked_chunk.updates().unwrap().take(),
3140 &[
3141 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3142 NewGapChunk {
3143 previous: Some(ChunkIdentifier(0)),
3144 new: ChunkIdentifier(1),
3145 next: None,
3146 gap: (),
3147 },
3148 NewItemsChunk {
3149 previous: Some(ChunkIdentifier(1)),
3150 new: ChunkIdentifier(2),
3151 next: None,
3152 },
3153 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
3154 ]
3155 );
3156
3157 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3159 assert_eq!(gap_identifier, ChunkIdentifier(1));
3160
3161 let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
3162 assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
3163 assert_items_eq!(
3164 linked_chunk,
3165 ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
3166 );
3167 assert_eq!(
3168 linked_chunk.updates().unwrap().take(),
3169 &[
3170 NewItemsChunk {
3171 previous: Some(ChunkIdentifier(1)),
3172 new: ChunkIdentifier(3),
3173 next: Some(ChunkIdentifier(2)),
3174 },
3175 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
3176 NewItemsChunk {
3177 previous: Some(ChunkIdentifier(3)),
3178 new: ChunkIdentifier(4),
3179 next: Some(ChunkIdentifier(2)),
3180 },
3181 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
3182 RemoveChunk(ChunkIdentifier(1)),
3183 ]
3184 );
3185
3186 assert_eq!(linked_chunk.num_items(), 9);
3187
3188 Ok(())
3189 }
3190
3191 #[test]
3192 fn test_replace_gap_at_end() -> Result<(), Error> {
3193 use super::Update::*;
3194
3195 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3196
3197 let _ = linked_chunk.updates().unwrap().take();
3199
3200 linked_chunk.push_items_back(['a', 'b']);
3201 linked_chunk.push_gap_back(());
3202 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3203 assert_eq!(
3204 linked_chunk.updates().unwrap().take(),
3205 &[
3206 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3207 NewGapChunk {
3208 previous: Some(ChunkIdentifier(0)),
3209 new: ChunkIdentifier(1),
3210 next: None,
3211 gap: (),
3212 },
3213 ]
3214 );
3215
3216 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3218 assert_eq!(gap_identifier, ChunkIdentifier(1));
3219
3220 let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3221 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3222 assert_items_eq!(
3223 linked_chunk,
3224 ['a', 'b'] ['w', 'x', 'y'] ['z']
3225 );
3226 assert_eq!(
3227 linked_chunk.updates().unwrap().take(),
3228 &[
3229 NewItemsChunk {
3230 previous: Some(ChunkIdentifier(1)),
3231 new: ChunkIdentifier(2),
3232 next: None,
3233 },
3234 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3235 NewItemsChunk {
3236 previous: Some(ChunkIdentifier(2)),
3237 new: ChunkIdentifier(3),
3238 next: None,
3239 },
3240 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3241 RemoveChunk(ChunkIdentifier(1)),
3242 ]
3243 );
3244
3245 assert_eq!(linked_chunk.num_items(), 6);
3246
3247 Ok(())
3248 }
3249
3250 #[test]
3251 fn test_replace_gap_at_beginning() -> Result<(), Error> {
3252 use super::Update::*;
3253
3254 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3255
3256 let _ = linked_chunk.updates().unwrap().take();
3258
3259 linked_chunk.push_items_back(['a', 'b']);
3260 assert_items_eq!(linked_chunk, ['a', 'b']);
3261 assert_eq!(
3262 linked_chunk.updates().unwrap().take(),
3263 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },]
3264 );
3265
3266 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3268 linked_chunk.insert_gap_at((), position_of_a).unwrap();
3269 assert_items_eq!(
3270 linked_chunk,
3271 [-] ['a', 'b']
3272 );
3273 assert_eq!(
3274 linked_chunk.updates().unwrap().take(),
3275 &[NewGapChunk {
3276 previous: None,
3277 new: ChunkIdentifier(1),
3278 next: Some(ChunkIdentifier(0)),
3279 gap: (),
3280 }]
3281 );
3282
3283 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3284 assert_eq!(gap_identifier, ChunkIdentifier(1));
3285
3286 let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3287 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3288 assert_items_eq!(
3289 linked_chunk,
3290 ['x'] ['a', 'b']
3291 );
3292 assert_eq!(
3293 linked_chunk.updates().unwrap().take(),
3294 &[
3295 NewItemsChunk {
3296 previous: Some(ChunkIdentifier(1)),
3297 new: ChunkIdentifier(2),
3298 next: Some(ChunkIdentifier(0)),
3299 },
3300 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3301 RemoveChunk(ChunkIdentifier(1)),
3302 ]
3303 );
3304
3305 assert_eq!(linked_chunk.num_items(), 3);
3306
3307 Ok(())
3308 }
3309
3310 #[test]
3311 fn test_remove_empty_chunk_at() -> Result<(), Error> {
3312 use super::Update::*;
3313
3314 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3315
3316 let _ = linked_chunk.updates().unwrap().take();
3318
3319 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3320 linked_chunk.push_items_back(['a', 'b']);
3321 linked_chunk.push_gap_back(());
3322 linked_chunk.push_items_back(['l', 'm']);
3323 linked_chunk.push_gap_back(());
3324 assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3325 assert_eq!(
3326 linked_chunk.updates().unwrap().take(),
3327 &[
3328 NewGapChunk {
3329 previous: None,
3330 new: ChunkIdentifier(1),
3331 next: Some(ChunkIdentifier(0)),
3332 gap: (),
3333 },
3334 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3335 NewGapChunk {
3336 previous: Some(ChunkIdentifier(0)),
3337 new: ChunkIdentifier(2),
3338 next: None,
3339 gap: (),
3340 },
3341 NewItemsChunk {
3342 previous: Some(ChunkIdentifier(2)),
3343 new: ChunkIdentifier(3),
3344 next: None,
3345 },
3346 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3347 NewGapChunk {
3348 previous: Some(ChunkIdentifier(3)),
3349 new: ChunkIdentifier(4),
3350 next: None,
3351 gap: (),
3352 },
3353 ]
3354 );
3355
3356 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3358 assert_matches!(err, Error::RemovingNonEmptyItemsChunk { .. });
3359
3360 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(42)).unwrap_err();
3362 assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3363
3364 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(2)).unwrap();
3366 let next = maybe_next.unwrap();
3367 assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3369 assert_eq!(next.index(), 0);
3370 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3371 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3372
3373 let next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(4)).unwrap();
3375 assert!(next.is_none());
3377 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3378 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3379
3380 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(1)).unwrap();
3382 let next = maybe_next.unwrap();
3383 assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3384 assert_eq!(next.index(), 0);
3385 assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3386 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3387
3388 Ok(())
3389 }
3390
3391 #[test]
3392 fn test_remove_empty_last_chunk() {
3393 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3394
3395 let _ = linked_chunk.updates().unwrap().take();
3397
3398 assert_items_eq!(linked_chunk, []);
3399 assert!(linked_chunk.updates().unwrap().take().is_empty());
3400
3401 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3403 assert_matches!(err, Error::RemovingLastChunk);
3404 }
3405
3406 #[test]
3407 fn test_chunk_item_positions() {
3408 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3409 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3410 linked_chunk.push_gap_back(());
3411 linked_chunk.push_items_back(['f']);
3412
3413 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3414
3415 let mut iterator = linked_chunk.chunks();
3416
3417 {
3419 let chunk = iterator.next().unwrap();
3420 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3421 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3422 }
3423
3424 {
3426 let chunk = iterator.next().unwrap();
3427 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3428 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3429 }
3430
3431 {
3433 let chunk = iterator.next().unwrap();
3434 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3435 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3436 }
3437
3438 {
3440 let chunk = iterator.next().unwrap();
3441 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3442 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3443 }
3444 }
3445
3446 #[test]
3447 fn test_is_first_and_last_chunk() {
3448 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3449
3450 let mut chunks = linked_chunk.chunks().peekable();
3451 assert!(chunks.peek().unwrap().is_first_chunk());
3452 assert!(chunks.next().unwrap().is_last_chunk());
3453 assert!(chunks.next().is_none());
3454
3455 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3456
3457 let mut chunks = linked_chunk.chunks().peekable();
3458 assert!(chunks.next().unwrap().is_first_chunk());
3459 assert!(chunks.peek().unwrap().is_first_chunk().not());
3460 assert!(chunks.next().unwrap().is_last_chunk().not());
3461 assert!(chunks.next().unwrap().is_last_chunk());
3462 assert!(chunks.next().is_none());
3463 }
3464
3465 #[test]
3470 fn test_clear() {
3471 let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3472
3473 let item = Arc::new('a');
3474 let gap = Arc::new(());
3475
3476 linked_chunk.push_items_back([
3477 item.clone(),
3478 item.clone(),
3479 item.clone(),
3480 item.clone(),
3481 item.clone(),
3482 ]);
3483 linked_chunk.push_gap_back(gap.clone());
3484 linked_chunk.push_items_back([item.clone()]);
3485
3486 assert_eq!(Arc::strong_count(&item), 7);
3487 assert_eq!(Arc::strong_count(&gap), 2);
3488 assert_eq!(linked_chunk.num_items(), 6);
3489 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3490
3491 linked_chunk.clear();
3493
3494 assert_eq!(Arc::strong_count(&item), 1);
3495 assert_eq!(Arc::strong_count(&gap), 1);
3496 assert_eq!(linked_chunk.num_items(), 0);
3497 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3498 }
3499
3500 #[test]
3501 fn test_clear_emit_an_update_clear() {
3502 use super::Update::*;
3503
3504 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3505
3506 assert_eq!(
3507 linked_chunk.updates().unwrap().take(),
3508 &[NewItemsChunk {
3509 previous: None,
3510 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3511 next: None
3512 }]
3513 );
3514
3515 linked_chunk.clear();
3516
3517 assert_eq!(
3518 linked_chunk.updates().unwrap().take(),
3519 &[
3520 Clear,
3521 NewItemsChunk {
3522 previous: None,
3523 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3524 next: None
3525 }
3526 ]
3527 );
3528 }
3529
3530 #[test]
3531 fn test_replace_item() {
3532 use super::Update::*;
3533
3534 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3535
3536 linked_chunk.push_items_back(['a', 'b', 'c']);
3537 linked_chunk.push_gap_back(());
3538 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3540
3541 let _ = linked_chunk.updates().unwrap().take();
3543
3544 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3546 assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3547
3548 assert_eq!(
3549 linked_chunk.updates().unwrap().take(),
3550 &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3551 );
3552
3553 assert_matches!(
3555 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3556 Err(Error::InvalidItemIndex { index: 3 })
3557 );
3558
3559 assert_matches!(
3561 linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3562 Err(Error::ChunkIsAGap { .. })
3563 );
3564 }
3565
3566 #[test]
3567 fn test_lazy_previous() {
3568 use std::marker::PhantomData;
3569
3570 use super::{Ends, ObservableUpdates, Update::*};
3571
3572 let first_chunk_identifier = ChunkIdentifier(0);
3574 let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3575 unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3576
3577 let mut linked_chunk = LinkedChunk::<3, char, ()> {
3578 links: Ends { first: first_loaded_chunk, last: None },
3579 chunk_identifier_generator:
3580 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3581 updates: Some(ObservableUpdates::new()),
3582 marker: PhantomData,
3583 };
3584
3585 {
3588 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3589
3590 assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3591
3592 {
3594 let mut chunks = linked_chunk.chunks();
3595
3596 assert_matches!(chunks.next(), Some(chunk) => {
3597 assert_eq!(chunk.identifier(), 1);
3598 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3599 });
3600 assert_matches!(chunks.next(), Some(chunk) => {
3601 assert_eq!(chunk.identifier(), 2);
3602 assert!(chunk.lazy_previous.is_none());
3603 });
3604 assert!(chunks.next().is_none());
3605 }
3606
3607 assert_eq!(
3609 linked_chunk.updates().unwrap().take(),
3610 &[
3611 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3612 NewItemsChunk {
3613 previous: Some(ChunkIdentifier(1)),
3614 new: ChunkIdentifier(2),
3615 next: None,
3616 },
3617 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3618 ]
3619 );
3620 }
3621
3622 {
3624 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3625
3626 assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3627
3628 {
3630 let mut chunks = linked_chunk.chunks();
3631
3632 assert_matches!(chunks.next(), Some(chunk) => {
3633 assert_eq!(chunk.identifier(), 3);
3634 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3636 });
3637 assert_matches!(chunks.next(), Some(chunk) => {
3638 assert_eq!(chunk.identifier(), 1);
3639 assert!(chunk.lazy_previous.is_none());
3641 });
3642 assert_matches!(chunks.next(), Some(chunk) => {
3643 assert_eq!(chunk.identifier(), 2);
3644 assert!(chunk.lazy_previous.is_none());
3645 });
3646 assert!(chunks.next().is_none());
3647 }
3648
3649 assert_eq!(
3651 linked_chunk.updates().unwrap().take(),
3652 &[NewGapChunk {
3653 previous: Some(ChunkIdentifier(0)),
3655 new: ChunkIdentifier(3),
3656 next: Some(ChunkIdentifier(1)),
3657 gap: ()
3658 }]
3659 );
3660 }
3661
3662 {
3664 linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3665
3666 assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3667
3668 {
3670 let mut chunks = linked_chunk.chunks();
3671
3672 assert_matches!(chunks.next(), Some(chunk) => {
3673 assert_eq!(chunk.identifier(), 4);
3674 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3676 });
3677 assert_matches!(chunks.next(), Some(chunk) => {
3678 assert_eq!(chunk.identifier(), 5);
3679 assert!(chunk.lazy_previous.is_none());
3680 });
3681 assert_matches!(chunks.next(), Some(chunk) => {
3682 assert_eq!(chunk.identifier(), 1);
3683 assert!(chunk.lazy_previous.is_none());
3684 });
3685 assert_matches!(chunks.next(), Some(chunk) => {
3686 assert_eq!(chunk.identifier(), 2);
3687 assert!(chunk.lazy_previous.is_none());
3688 });
3689 assert!(chunks.next().is_none());
3690 }
3691
3692 assert_eq!(
3694 linked_chunk.updates().unwrap().take(),
3695 &[
3696 NewItemsChunk {
3698 previous: Some(ChunkIdentifier(3)),
3699 new: ChunkIdentifier(4),
3700 next: Some(ChunkIdentifier(1)),
3701 },
3702 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3704 NewItemsChunk {
3706 previous: Some(ChunkIdentifier(4)),
3707 new: ChunkIdentifier(5),
3708 next: Some(ChunkIdentifier(1)),
3709 },
3710 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3712 RemoveChunk(ChunkIdentifier(3)),
3714 ]
3715 );
3716 }
3717
3718 {
3722 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3723
3724 assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3725
3726 {
3728 let mut chunks = linked_chunk.chunks();
3729
3730 assert_matches!(chunks.next(), Some(chunk) => {
3731 assert_eq!(chunk.identifier(), 6);
3732 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3734 });
3735 assert_matches!(chunks.next(), Some(chunk) => {
3736 assert_eq!(chunk.identifier(), 4);
3737 assert!(chunk.lazy_previous.is_none());
3739 });
3740 assert_matches!(chunks.next(), Some(chunk) => {
3741 assert_eq!(chunk.identifier(), 5);
3742 assert!(chunk.lazy_previous.is_none());
3743 });
3744 assert_matches!(chunks.next(), Some(chunk) => {
3745 assert_eq!(chunk.identifier(), 1);
3746 assert!(chunk.lazy_previous.is_none());
3747 });
3748 assert_matches!(chunks.next(), Some(chunk) => {
3749 assert_eq!(chunk.identifier(), 2);
3750 assert!(chunk.lazy_previous.is_none());
3751 });
3752 assert!(chunks.next().is_none());
3753 }
3754
3755 assert_eq!(
3757 linked_chunk.updates().unwrap().take(),
3758 &[NewGapChunk {
3759 previous: Some(ChunkIdentifier(0)),
3761 new: ChunkIdentifier(6),
3762 next: Some(ChunkIdentifier(4)),
3763 gap: ()
3764 }]
3765 );
3766 }
3767 }
3768}