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;
95mod identifiers;
96pub mod lazy_loader;
97mod order_tracker;
98pub mod relational;
99mod updates;
100
101use std::{
102 fmt::{self},
103 marker::PhantomData,
104 ptr::NonNull,
105 sync::atomic::{self, AtomicU64},
106};
107
108pub use self::{as_vector::*, identifiers::*, order_tracker::OrderTracker, updates::*};
109
110#[derive(thiserror::Error, Debug)]
112pub enum Error {
113 #[error("The chunk identifier is invalid: `{identifier:?}`")]
115 InvalidChunkIdentifier {
116 identifier: ChunkIdentifier,
118 },
119
120 #[error("The chunk is a gap: `{identifier:?}`")]
122 ChunkIsAGap {
123 identifier: ChunkIdentifier,
125 },
126
127 #[error("The chunk is an item: `{identifier:?}`")]
129 ChunkIsItems {
130 identifier: ChunkIdentifier,
132 },
133
134 #[error("The chunk is a non-empty item chunk: `{identifier:?}`")]
136 RemovingNonEmptyItemsChunk {
137 identifier: ChunkIdentifier,
139 },
140
141 #[error("Trying to remove the only chunk, but a linked chunk can't be empty")]
144 RemovingLastChunk,
145
146 #[error("The item index is invalid: `{index}`")]
148 InvalidItemIndex {
149 index: usize,
151 },
152}
153
154struct Ends<const CHUNK_CAPACITY: usize, Item, Gap> {
159 first: NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>,
161 last: Option<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
163}
164
165impl<const CAP: usize, Item, Gap> Ends<CAP, Item, Gap> {
166 fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
168 unsafe { self.first.as_ref() }
169 }
170
171 fn first_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
173 unsafe { self.first.as_mut() }
174 }
175
176 fn latest_chunk(&self) -> &Chunk<CAP, Item, Gap> {
178 unsafe { self.last.unwrap_or(self.first).as_ref() }
179 }
180
181 fn latest_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
183 unsafe { self.last.as_mut().unwrap_or(&mut self.first).as_mut() }
184 }
185
186 fn chunk(&self, identifier: ChunkIdentifier) -> Option<&Chunk<CAP, Item, Gap>> {
188 let mut chunk = self.latest_chunk();
189
190 loop {
191 if chunk.identifier() == identifier {
192 return Some(chunk);
193 }
194
195 chunk = chunk.previous()?;
196 }
197 }
198
199 fn chunk_mut(&mut self, identifier: ChunkIdentifier) -> Option<&mut Chunk<CAP, Item, Gap>> {
201 let mut chunk = self.latest_chunk_mut();
202
203 loop {
204 if chunk.identifier() == identifier {
205 return Some(chunk);
206 }
207
208 chunk = chunk.previous_mut()?;
209 }
210 }
211
212 unsafe fn clear(&mut self) {
220 let mut current_chunk_ptr = self.last.or(Some(self.first));
223
224 while let Some(chunk_ptr) = current_chunk_ptr {
226 let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
228
229 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
231
232 current_chunk_ptr = previous_ptr;
234 }
235
236 self.first = NonNull::dangling();
238 self.last = None;
239 }
240
241 fn replace_with(&mut self, first_chunk: NonNull<Chunk<CAP, Item, Gap>>) {
244 unsafe {
246 self.clear();
247 }
248
249 self.first = first_chunk;
251 }
252
253 fn reset(&mut self) {
258 self.replace_with(Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER));
259 }
260}
261
262pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
269 links: Ends<CHUNK_CAPACITY, Item, Gap>,
271
272 chunk_identifier_generator: ChunkIdentifierGenerator,
274
275 updates: Option<ObservableUpdates<Item, Gap>>,
279
280 marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
282}
283
284impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
285 fn default() -> Self {
286 Self::new()
287 }
288}
289
290impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
291 pub fn new() -> Self {
293 Self {
294 links: Ends {
295 first: Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER),
296 last: None,
297 },
298 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
299 updates: None,
300 marker: PhantomData,
301 }
302 }
303
304 pub fn new_with_update_history() -> Self {
310 let first_chunk_identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
311
312 let mut updates = ObservableUpdates::new();
313 updates.push(Update::NewItemsChunk {
314 previous: None,
315 new: first_chunk_identifier,
316 next: None,
317 });
318
319 Self {
320 links: Ends { first: Chunk::new_items_leaked(first_chunk_identifier), last: None },
321 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
322 updates: Some(updates),
323 marker: PhantomData,
324 }
325 }
326
327 pub fn clear(&mut self) {
329 self.links.reset();
331
332 self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
334
335 if let Some(updates) = self.updates.as_mut() {
337 updates.clear_pending();
340 updates.push(Update::Clear);
341 updates.push(Update::NewItemsChunk {
342 previous: None,
343 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
344 next: None,
345 })
346 }
347 }
348
349 pub fn push_items_back<I>(&mut self, items: I)
355 where
356 Item: Clone,
357 Gap: Clone,
358 I: IntoIterator<Item = Item>,
359 I::IntoIter: ExactSizeIterator,
360 {
361 let items = items.into_iter();
362
363 let last_chunk = self.links.latest_chunk_mut();
364
365 let last_chunk =
367 last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
368
369 debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
370
371 if !last_chunk.is_first_chunk() {
375 self.links.last = Some(last_chunk.as_ptr());
378 }
379 }
380
381 pub fn push_gap_back(&mut self, content: Gap)
384 where
385 Item: Clone,
386 Gap: Clone,
387 {
388 let last_chunk = self.links.latest_chunk_mut();
389 last_chunk.insert_next(
390 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
391 &mut self.updates,
392 );
393
394 self.links.last = last_chunk.next;
395 }
396
397 pub fn insert_items_at<I>(&mut self, position: Position, items: I) -> Result<(), Error>
402 where
403 Item: Clone,
404 Gap: Clone,
405 I: IntoIterator<Item = Item>,
406 I::IntoIter: ExactSizeIterator,
407 {
408 let chunk_identifier = position.chunk_identifier();
409 let item_index = position.index();
410
411 let chunk = self
412 .links
413 .chunk_mut(chunk_identifier)
414 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
415
416 let chunk = match &mut chunk.content {
417 ChunkContent::Gap(..) => {
418 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
419 }
420
421 ChunkContent::Items(current_items) => {
422 let current_items_length = current_items.len();
423
424 if item_index > current_items_length {
425 return Err(Error::InvalidItemIndex { index: item_index });
426 }
427
428 let items = items.into_iter();
430
431 if item_index == current_items_length {
433 chunk
434 .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
436 }
437 else {
439 if let Some(updates) = self.updates.as_mut() {
440 updates.push(Update::DetachLastItems {
441 at: Position(chunk_identifier, item_index),
442 });
443 }
444
445 let detached_items = current_items.split_off(item_index);
447
448 let chunk = chunk
449 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
451
452 if let Some(updates) = self.updates.as_mut() {
453 updates.push(Update::StartReattachItems);
454 }
455
456 let chunk = chunk
457 .push_items(
459 detached_items.into_iter(),
460 &self.chunk_identifier_generator,
461 &mut self.updates,
462 );
463
464 if let Some(updates) = self.updates.as_mut() {
465 updates.push(Update::EndReattachItems);
466 }
467
468 chunk
469 }
470 }
471 };
472
473 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
476 self.links.last = Some(chunk.as_ptr());
479 }
480
481 Ok(())
482 }
483
484 pub fn remove_item_at(&mut self, position: Position) -> Result<Item, Error> {
492 let chunk_identifier = position.chunk_identifier();
493 let item_index = position.index();
494
495 let mut chunk_ptr = None;
496 let removed_item;
497
498 {
499 let chunk = self
500 .links
501 .chunk_mut(chunk_identifier)
502 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
503
504 let current_items = match &mut chunk.content {
505 ChunkContent::Gap(..) => {
506 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
507 }
508 ChunkContent::Items(current_items) => current_items,
509 };
510
511 if item_index >= current_items.len() {
512 return Err(Error::InvalidItemIndex { index: item_index });
513 }
514
515 removed_item = current_items.remove(item_index);
516
517 if let Some(updates) = self.updates.as_mut() {
518 updates.push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
519 }
520
521 if current_items.is_empty() && !chunk.is_first_chunk() {
523 chunk.unlink(self.updates.as_mut());
525
526 chunk_ptr = Some(chunk.as_ptr());
527
528 if chunk.is_last_chunk() {
531 self.links.last = chunk.previous;
532 }
533 }
534
535 }
537
538 if let Some(chunk_ptr) = chunk_ptr {
539 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
546 }
547
548 Ok(removed_item)
549 }
550
551 pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
556 where
557 Item: Clone,
558 {
559 let chunk_identifier = position.chunk_identifier();
560 let item_index = position.index();
561
562 let chunk = self
563 .links
564 .chunk_mut(chunk_identifier)
565 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
566
567 match &mut chunk.content {
568 ChunkContent::Gap(..) => {
569 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
570 }
571
572 ChunkContent::Items(current_items) => {
573 if item_index >= current_items.len() {
574 return Err(Error::InvalidItemIndex { index: item_index });
575 }
576
577 if let Some(updates) = self.updates.as_mut() {
579 updates.push(Update::ReplaceItem {
580 at: Position(chunk_identifier, item_index),
581 item: item.clone(),
582 });
583 }
584
585 current_items[item_index] = item;
586 }
587 }
588
589 Ok(())
590 }
591
592 pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
597 where
598 Item: Clone,
599 Gap: Clone,
600 {
601 let chunk_identifier = position.chunk_identifier();
602 let item_index = position.index();
603
604 let chunk = self
605 .links
606 .chunk_mut(chunk_identifier)
607 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
608
609 let chunk = match &mut chunk.content {
610 ChunkContent::Gap(..) => {
611 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
612 }
613
614 ChunkContent::Items(current_items) => {
615 if item_index == 0 {
619 let chunk_was_first = chunk.is_first_chunk();
620 let chunk_was_last = chunk.is_last_chunk();
621
622 let new_chunk = chunk.insert_before(
623 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
624 self.updates.as_mut(),
625 );
626
627 let new_chunk_ptr = new_chunk.as_ptr();
628 let chunk_ptr = chunk.as_ptr();
629
630 if chunk_was_first {
635 self.links.first = new_chunk_ptr;
636
637 if chunk_was_last {
639 self.links.last = Some(chunk_ptr);
640 }
641 }
642
643 return Ok(());
644 }
645
646 let current_items_length = current_items.len();
647
648 if item_index >= current_items_length {
649 return Err(Error::InvalidItemIndex { index: item_index });
650 }
651
652 if let Some(updates) = self.updates.as_mut() {
653 updates.push(Update::DetachLastItems {
654 at: Position(chunk_identifier, item_index),
655 });
656 }
657
658 let detached_items = current_items.split_off(item_index);
660
661 let chunk = chunk
662 .insert_next(
664 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
665 &mut self.updates,
666 );
667
668 if let Some(updates) = self.updates.as_mut() {
669 updates.push(Update::StartReattachItems);
670 }
671
672 let chunk = chunk
673 .insert_next(
675 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
676 &mut self.updates,
677 )
678 .push_items(
680 detached_items.into_iter(),
681 &self.chunk_identifier_generator,
682 &mut self.updates,
683 );
684
685 if let Some(updates) = self.updates.as_mut() {
686 updates.push(Update::EndReattachItems);
687 }
688
689 chunk
690 }
691 };
692
693 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
696 self.links.last = Some(chunk.as_ptr());
699 }
700
701 Ok(())
702 }
703
704 pub fn remove_empty_chunk_at(
713 &mut self,
714 chunk_identifier: ChunkIdentifier,
715 ) -> Result<Option<Position>, Error> {
716 if self.links.first_chunk().is_last_chunk() {
718 return Err(Error::RemovingLastChunk);
719 }
720
721 let chunk = self
722 .links
723 .chunk_mut(chunk_identifier)
724 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
725
726 if chunk.num_items() > 0 {
727 return Err(Error::RemovingNonEmptyItemsChunk { identifier: chunk_identifier });
728 }
729
730 let chunk_was_first = chunk.is_first_chunk();
731 let chunk_was_last = chunk.is_last_chunk();
732 let next_ptr = chunk.next;
733 let previous_ptr = chunk.previous;
734 let position_of_next = chunk.next().map(|next| next.first_position());
735
736 chunk.unlink(self.updates.as_mut());
737
738 let chunk_ptr = chunk.as_ptr();
739
740 if chunk_was_first {
742 if let Some(next_ptr) = next_ptr {
744 self.links.first = next_ptr;
745 }
746 }
747
748 if chunk_was_last {
749 self.links.last = previous_ptr;
750 }
751
752 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
755
756 Ok(position_of_next)
758 }
759
760 pub fn replace_gap_at<I>(
768 &mut self,
769 items: I,
770 chunk_identifier: ChunkIdentifier,
771 ) -> Result<&Chunk<CAP, Item, Gap>, Error>
772 where
773 Item: Clone,
774 Gap: Clone,
775 I: IntoIterator<Item = Item>,
776 I::IntoIter: ExactSizeIterator,
777 {
778 let chunk_ptr;
779 let new_chunk_ptr;
780
781 {
782 let chunk = self
783 .links
784 .chunk_mut(chunk_identifier)
785 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
786
787 if chunk.is_items() {
788 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
789 }
790
791 let chunk_was_first = chunk.is_first_chunk();
792
793 let maybe_last_chunk_ptr = {
794 let items = items.into_iter();
795
796 let last_inserted_chunk = chunk
797 .insert_next(
799 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
800 &mut self.updates,
801 )
802 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
804
805 last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
806 };
807
808 new_chunk_ptr = chunk
809 .next
810 .unwrap();
812
813 chunk.unlink(self.updates.as_mut());
815
816 chunk_ptr = chunk.as_ptr();
818
819 if chunk_was_first {
821 self.links.first = new_chunk_ptr;
822 }
823
824 if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
827 self.links.last = Some(last_chunk_ptr);
828 }
829
830 }
832
833 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
838
839 Ok(
840 unsafe { new_chunk_ptr.as_ref() },
844 )
845 }
846
847 pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
849 where
850 P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
851 {
852 self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
853 }
854
855 pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
857 where
858 P: FnMut(&'a Item) -> bool,
859 {
860 self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
861 }
862
863 pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
867 IterBackward::new(self.links.latest_chunk())
868 }
869
870 pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
874 Iter::new(self.links.first_chunk())
875 }
876
877 pub fn rchunks_from(
882 &self,
883 identifier: ChunkIdentifier,
884 ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
885 Ok(IterBackward::new(
886 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
887 ))
888 }
889
890 pub fn chunks_from(
895 &self,
896 identifier: ChunkIdentifier,
897 ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
898 Ok(Iter::new(
899 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
900 ))
901 }
902
903 pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
907 self.ritems_from(self.links.latest_chunk().last_position())
908 .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
909 }
910
911 pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
915 let first_chunk = self.links.first_chunk();
916
917 self.items_from(first_chunk.first_position())
918 .expect("`items` cannot fail because at least one empty chunk must exist")
919 }
920
921 pub fn ritems_from(
925 &self,
926 position: Position,
927 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
928 Ok(self
929 .rchunks_from(position.chunk_identifier())?
930 .filter_map(|chunk| match &chunk.content {
931 ChunkContent::Gap(..) => None,
932 ChunkContent::Items(items) => {
933 let identifier = chunk.identifier();
934
935 Some(
936 items.iter().enumerate().rev().map(move |(item_index, item)| {
937 (Position(identifier, item_index), item)
938 }),
939 )
940 }
941 })
942 .flatten()
943 .skip_while({
944 let expected_index = position.index();
945
946 move |(Position(chunk_identifier, item_index), _item)| {
947 *chunk_identifier == position.chunk_identifier()
948 && *item_index != expected_index
949 }
950 }))
951 }
952
953 pub fn items_from(
957 &self,
958 position: Position,
959 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
960 Ok(self
961 .chunks_from(position.chunk_identifier())?
962 .filter_map(|chunk| match &chunk.content {
963 ChunkContent::Gap(..) => None,
964 ChunkContent::Items(items) => {
965 let identifier = chunk.identifier();
966
967 Some(
968 items.iter().enumerate().map(move |(item_index, item)| {
969 (Position(identifier, item_index), item)
970 }),
971 )
972 }
973 })
974 .flatten()
975 .skip(position.index()))
976 }
977
978 pub fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
980 self.links.first_chunk()
981 }
982
983 #[must_use]
995 pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
996 self.updates.as_mut()
997 }
998
999 pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
1006 let (updates, token) = self
1007 .updates
1008 .as_mut()
1009 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1010 let chunk_iterator = self.chunks();
1011
1012 Some(AsVector::new(updates, token, chunk_iterator))
1013 }
1014
1015 pub fn order_tracker(
1023 &mut self,
1024 all_chunks: Option<Vec<ChunkMetadata>>,
1025 ) -> Option<OrderTracker<Item, Gap>>
1026 where
1027 Item: Clone,
1028 {
1029 let (updates, token) = self
1030 .updates
1031 .as_mut()
1032 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1033
1034 Some(OrderTracker::new(
1035 updates,
1036 token,
1037 all_chunks.unwrap_or_else(|| {
1038 self.chunks()
1040 .map(|chunk| ChunkMetadata {
1041 identifier: chunk.identifier(),
1042 num_items: chunk.num_items(),
1043 previous: chunk.previous().map(|prev| prev.identifier()),
1044 next: chunk.next().map(|next| next.identifier()),
1045 })
1046 .collect()
1047 }),
1048 ))
1049 }
1050
1051 pub fn num_items(&self) -> usize {
1053 self.items().count()
1054 }
1055}
1056
1057impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
1058 fn drop(&mut self) {
1059 unsafe {
1069 self.links.clear();
1070 }
1071 }
1072}
1073
1074unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1078
1079unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1083
1084#[derive(Debug)]
1094pub struct ChunkIdentifierGenerator {
1095 next: AtomicU64,
1096}
1097
1098impl ChunkIdentifierGenerator {
1099 const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1101
1102 pub fn new_from_scratch() -> Self {
1105 Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1106 }
1107
1108 pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1111 Self { next: AtomicU64::new(last_chunk_identifier.0) }
1112 }
1113
1114 fn next(&self) -> ChunkIdentifier {
1119 let previous = self.next.fetch_add(1, atomic::Ordering::Relaxed);
1120
1121 if previous == u64::MAX {
1124 panic!(
1125 "No more chunk identifiers available. Congrats, you did it. \
1126 2^64 identifiers have been consumed."
1127 )
1128 }
1129
1130 ChunkIdentifier(previous + 1)
1131 }
1132
1133 #[doc(hidden)]
1137 pub fn current(&self) -> ChunkIdentifier {
1138 ChunkIdentifier(self.next.load(atomic::Ordering::Relaxed))
1139 }
1140}
1141
1142#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1148#[repr(transparent)]
1149pub struct ChunkIdentifier(u64);
1150
1151impl ChunkIdentifier {
1152 pub fn new(identifier: u64) -> Self {
1154 Self(identifier)
1155 }
1156
1157 pub fn index(&self) -> u64 {
1159 self.0
1160 }
1161}
1162
1163impl PartialEq<u64> for ChunkIdentifier {
1164 fn eq(&self, other: &u64) -> bool {
1165 self.0 == *other
1166 }
1167}
1168
1169#[derive(Copy, Clone, Debug, PartialEq)]
1173pub struct Position(ChunkIdentifier, usize);
1174
1175impl Position {
1176 pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1178 Self(chunk_identifier, index)
1179 }
1180
1181 pub fn chunk_identifier(&self) -> ChunkIdentifier {
1183 self.0
1184 }
1185
1186 pub fn index(&self) -> usize {
1188 self.1
1189 }
1190
1191 pub fn decrement_index(&mut self) {
1197 self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1198 }
1199
1200 pub fn increment_index(&mut self) {
1207 self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1208 }
1209}
1210
1211#[derive(Debug)]
1214pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1215 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1216}
1217
1218impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1219 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1221 Self { chunk: Some(from_chunk) }
1222 }
1223}
1224
1225impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1226 type Item = &'a Chunk<CAP, Item, Gap>;
1227
1228 fn next(&mut self) -> Option<Self::Item> {
1229 self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1230 }
1231}
1232
1233#[derive(Debug)]
1236pub struct Iter<'a, const CAP: usize, Item, Gap> {
1237 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1238}
1239
1240impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1241 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1243 Self { chunk: Some(from_chunk) }
1244 }
1245}
1246
1247impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1248 type Item = &'a Chunk<CAP, Item, Gap>;
1249
1250 fn next(&mut self) -> Option<Self::Item> {
1251 self.chunk.inspect(|chunk| self.chunk = chunk.next())
1252 }
1253}
1254
1255#[derive(Clone, Debug)]
1257pub enum ChunkContent<Item, Gap> {
1258 Gap(Gap),
1261
1262 Items(Vec<Item>),
1264}
1265
1266pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1268 previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1270
1271 lazy_previous: Option<ChunkIdentifier>,
1276
1277 next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1279
1280 identifier: ChunkIdentifier,
1282
1283 content: ChunkContent<Item, Gap>,
1285}
1286
1287impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1288 fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1290 Self::new(identifier, ChunkContent::Gap(content))
1291 }
1292
1293 fn new_items(identifier: ChunkIdentifier) -> Self {
1295 Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1296 }
1297
1298 fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1299 Self { previous: None, lazy_previous: None, next: None, identifier, content }
1300 }
1301
1302 fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1304 let chunk = Self::new(identifier, content);
1305 let chunk_box = Box::new(chunk);
1306
1307 NonNull::from(Box::leak(chunk_box))
1308 }
1309
1310 fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1312 let chunk = Self::new_gap(identifier, content);
1313 let chunk_box = Box::new(chunk);
1314
1315 NonNull::from(Box::leak(chunk_box))
1316 }
1317
1318 fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1320 let chunk = Self::new_items(identifier);
1321 let chunk_box = Box::new(chunk);
1322
1323 NonNull::from(Box::leak(chunk_box))
1324 }
1325
1326 pub fn as_ptr(&self) -> NonNull<Self> {
1328 NonNull::from(self)
1329 }
1330
1331 pub fn is_gap(&self) -> bool {
1333 matches!(self.content, ChunkContent::Gap(..))
1334 }
1335
1336 pub fn is_items(&self) -> bool {
1338 !self.is_gap()
1339 }
1340
1341 pub fn is_definitive_head(&self) -> bool {
1344 self.previous.is_none() && self.lazy_previous.is_none()
1345 }
1346
1347 fn is_first_chunk(&self) -> bool {
1349 self.previous.is_none()
1350 }
1351
1352 fn is_last_chunk(&self) -> bool {
1354 self.next.is_none()
1355 }
1356
1357 #[doc(hidden)]
1361 pub fn lazy_previous(&self) -> Option<ChunkIdentifier> {
1362 self.lazy_previous
1363 }
1364
1365 pub fn identifier(&self) -> ChunkIdentifier {
1367 self.identifier
1368 }
1369
1370 pub fn content(&self) -> &ChunkContent<Item, Gap> {
1372 &self.content
1373 }
1374
1375 pub fn first_position(&self) -> Position {
1379 Position(self.identifier(), 0)
1380 }
1381
1382 pub fn last_position(&self) -> Position {
1386 let identifier = self.identifier();
1387
1388 match &self.content {
1389 ChunkContent::Gap(..) => Position(identifier, 0),
1390 ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1391 }
1392 }
1393
1394 pub fn num_items(&self) -> usize {
1398 match &self.content {
1399 ChunkContent::Gap(..) => 0,
1400 ChunkContent::Items(items) => items.len(),
1401 }
1402 }
1403
1404 fn push_items<I>(
1416 &mut self,
1417 mut new_items: I,
1418 chunk_identifier_generator: &ChunkIdentifierGenerator,
1419 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1420 ) -> &mut Self
1421 where
1422 I: Iterator<Item = Item> + ExactSizeIterator,
1423 Item: Clone,
1424 Gap: Clone,
1425 {
1426 if new_items.len() == 0 {
1428 return self;
1429 }
1430
1431 let identifier = self.identifier();
1432 let prev_num_items = self.num_items();
1433
1434 match &mut self.content {
1435 ChunkContent::Gap(..) => {
1438 self
1439 .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1441 .push_items(new_items, chunk_identifier_generator, updates)
1444 }
1445
1446 ChunkContent::Items(items) => {
1447 let free_space = CAPACITY.saturating_sub(prev_num_items);
1449
1450 if new_items.len() <= free_space {
1452 let start = items.len();
1453 items.extend(new_items);
1454
1455 if let Some(updates) = updates.as_mut() {
1456 updates.push(Update::PushItems {
1457 at: Position(identifier, start),
1458 items: items[start..].to_vec(),
1459 });
1460 }
1461
1462 self
1464 } else {
1465 if free_space > 0 {
1466 let start = items.len();
1468 items.extend(new_items.by_ref().take(free_space));
1469
1470 if let Some(updates) = updates.as_mut() {
1471 updates.push(Update::PushItems {
1472 at: Position(identifier, start),
1473 items: items[start..].to_vec(),
1474 });
1475 }
1476 }
1477
1478 self
1479 .insert_next(
1481 Self::new_items_leaked(chunk_identifier_generator.next()),
1482 updates,
1483 )
1484 .push_items(new_items, chunk_identifier_generator, updates)
1487 }
1488 }
1489 }
1490 }
1491
1492 fn insert_next(
1497 &mut self,
1498 mut new_chunk_ptr: NonNull<Self>,
1499 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1500 ) -> &mut Self
1501 where
1502 Gap: Clone,
1503 {
1504 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1505
1506 if let Some(next_chunk) = self.next_mut() {
1508 next_chunk.previous = Some(new_chunk_ptr);
1510
1511 new_chunk.next = self.next;
1513 }
1514
1515 self.next = Some(new_chunk_ptr);
1517 new_chunk.previous = Some(self.as_ptr());
1519
1520 if let Some(updates) = updates.as_mut() {
1521 let previous = new_chunk.previous().map(Chunk::identifier);
1522 let new = new_chunk.identifier();
1523 let next = new_chunk.next().map(Chunk::identifier);
1524
1525 match new_chunk.content() {
1526 ChunkContent::Gap(gap) => {
1527 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1528 }
1529
1530 ChunkContent::Items(..) => {
1531 updates.push(Update::NewItemsChunk { previous, new, next })
1532 }
1533 }
1534 }
1535
1536 new_chunk
1537 }
1538
1539 fn insert_before(
1544 &mut self,
1545 mut new_chunk_ptr: NonNull<Self>,
1546 updates: Option<&mut ObservableUpdates<Item, Gap>>,
1547 ) -> &mut Self
1548 where
1549 Gap: Clone,
1550 {
1551 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1552
1553 if let Some(previous_chunk) = self.previous_mut() {
1555 previous_chunk.next = Some(new_chunk_ptr);
1557
1558 new_chunk.previous = self.previous;
1560 }
1561 else {
1564 new_chunk.lazy_previous = self.lazy_previous.take();
1565 }
1566
1567 self.previous = Some(new_chunk_ptr);
1569 new_chunk.next = Some(self.as_ptr());
1571
1572 if let Some(updates) = updates {
1573 let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1574 let new = new_chunk.identifier();
1575 let next = new_chunk.next().map(Chunk::identifier);
1576
1577 match new_chunk.content() {
1578 ChunkContent::Gap(gap) => {
1579 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1580 }
1581
1582 ChunkContent::Items(..) => {
1583 updates.push(Update::NewItemsChunk { previous, new, next })
1584 }
1585 }
1586 }
1587
1588 new_chunk
1589 }
1590
1591 fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1596 let previous_ptr = self.previous;
1597 let next_ptr = self.next;
1598 let lazy_previous = self.lazy_previous.take();
1602
1603 if let Some(previous) = self.previous_mut() {
1604 previous.next = next_ptr;
1605 }
1606
1607 if let Some(next) = self.next_mut() {
1608 next.previous = previous_ptr;
1609 next.lazy_previous = lazy_previous;
1610 }
1611
1612 if let Some(updates) = updates {
1613 updates.push(Update::RemoveChunk(self.identifier()));
1614 }
1615 }
1616
1617 fn previous(&self) -> Option<&Self> {
1619 self.previous.map(|non_null| unsafe { non_null.as_ref() })
1620 }
1621
1622 fn previous_mut(&mut self) -> Option<&mut Self> {
1624 self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1625 }
1626
1627 fn next(&self) -> Option<&Self> {
1629 self.next.map(|non_null| unsafe { non_null.as_ref() })
1630 }
1631
1632 fn next_mut(&mut self) -> Option<&mut Self> {
1634 self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1635 }
1636}
1637
1638impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<CAP, Item, Gap>
1639where
1640 Item: fmt::Debug,
1641 Gap: fmt::Debug,
1642{
1643 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1644 formatter
1645 .debug_struct("LinkedChunk")
1646 .field("first (deref)", unsafe { self.links.first.as_ref() })
1647 .field("last", &self.links.last)
1648 .finish_non_exhaustive()
1649 }
1650}
1651
1652impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1653where
1654 Item: fmt::Debug,
1655 Gap: fmt::Debug,
1656{
1657 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1658 formatter
1659 .debug_struct("Chunk")
1660 .field("identifier", &self.identifier)
1661 .field("content", &self.content)
1662 .field("previous", &self.previous)
1663 .field("ptr", &std::ptr::from_ref(self))
1664 .field("next", &self.next)
1665 .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1666 .finish()
1667 }
1668}
1669
1670#[derive(Clone, Debug)]
1676pub struct RawChunk<Item, Gap> {
1677 pub content: ChunkContent<Item, Gap>,
1679
1680 pub previous: Option<ChunkIdentifier>,
1682
1683 pub identifier: ChunkIdentifier,
1685
1686 pub next: Option<ChunkIdentifier>,
1688}
1689
1690#[derive(Clone, Debug)]
1693pub struct ChunkMetadata {
1694 pub num_items: usize,
1698
1699 pub previous: Option<ChunkIdentifier>,
1701
1702 pub identifier: ChunkIdentifier,
1704
1705 pub next: Option<ChunkIdentifier>,
1707}
1708
1709#[cfg(test)]
1710mod tests {
1711 use std::{
1712 ops::Not,
1713 sync::{Arc, atomic::Ordering},
1714 };
1715
1716 use assert_matches::assert_matches;
1717
1718 use super::{
1719 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Error, LinkedChunk,
1720 Position,
1721 };
1722
1723 #[test]
1724 fn test_chunk_identifier_generator() {
1725 let generator = ChunkIdentifierGenerator::new_from_scratch();
1726
1727 assert_eq!(generator.next(), ChunkIdentifier(1));
1728 assert_eq!(generator.next(), ChunkIdentifier(2));
1729 assert_eq!(generator.next(), ChunkIdentifier(3));
1730 assert_eq!(generator.next(), ChunkIdentifier(4));
1731
1732 let generator =
1733 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1734
1735 assert_eq!(generator.next(), ChunkIdentifier(43));
1736 assert_eq!(generator.next(), ChunkIdentifier(44));
1737 assert_eq!(generator.next(), ChunkIdentifier(45));
1738 assert_eq!(generator.next(), ChunkIdentifier(46));
1739 }
1740
1741 #[test]
1742 fn test_empty() {
1743 let items = LinkedChunk::<3, char, ()>::new();
1744
1745 assert_eq!(items.num_items(), 0);
1746
1747 }
1750
1751 #[test]
1752 fn test_updates() {
1753 assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1754 assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1755 }
1756
1757 #[test]
1758 fn test_new_with_initial_update() {
1759 use super::Update::*;
1760
1761 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1762
1763 assert_eq!(
1764 linked_chunk.updates().unwrap().take(),
1765 &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1766 );
1767 }
1768
1769 #[test]
1770 fn test_push_items() {
1771 use super::Update::*;
1772
1773 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1774
1775 let _ = linked_chunk.updates().unwrap().take();
1777
1778 linked_chunk.push_items_back(['a']);
1779
1780 assert_items_eq!(linked_chunk, ['a']);
1781 assert_eq!(
1782 linked_chunk.updates().unwrap().take(),
1783 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1784 );
1785
1786 linked_chunk.push_items_back(['b', 'c']);
1787 assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1788 assert_eq!(
1789 linked_chunk.updates().unwrap().take(),
1790 &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1791 );
1792
1793 linked_chunk.push_items_back(['d', 'e']);
1794 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
1795 assert_eq!(
1796 linked_chunk.updates().unwrap().take(),
1797 &[
1798 NewItemsChunk {
1799 previous: Some(ChunkIdentifier(0)),
1800 new: ChunkIdentifier(1),
1801 next: None
1802 },
1803 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1804 ]
1805 );
1806
1807 linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1808 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1809 assert_eq!(
1810 linked_chunk.updates().unwrap().take(),
1811 &[
1812 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1813 NewItemsChunk {
1814 previous: Some(ChunkIdentifier(1)),
1815 new: ChunkIdentifier(2),
1816 next: None,
1817 },
1818 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1819 NewItemsChunk {
1820 previous: Some(ChunkIdentifier(2)),
1821 new: ChunkIdentifier(3),
1822 next: None,
1823 },
1824 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1825 ]
1826 );
1827
1828 assert_eq!(linked_chunk.num_items(), 10);
1829 }
1830
1831 #[test]
1832 fn test_push_gap() {
1833 use super::Update::*;
1834
1835 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1836
1837 let _ = linked_chunk.updates().unwrap().take();
1839
1840 linked_chunk.push_items_back(['a']);
1841 assert_items_eq!(linked_chunk, ['a']);
1842 assert_eq!(
1843 linked_chunk.updates().unwrap().take(),
1844 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1845 );
1846
1847 linked_chunk.push_gap_back(());
1848 assert_items_eq!(linked_chunk, ['a'] [-]);
1849 assert_eq!(
1850 linked_chunk.updates().unwrap().take(),
1851 &[NewGapChunk {
1852 previous: Some(ChunkIdentifier(0)),
1853 new: ChunkIdentifier(1),
1854 next: None,
1855 gap: (),
1856 }]
1857 );
1858
1859 linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1860 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1861 assert_eq!(
1862 linked_chunk.updates().unwrap().take(),
1863 &[
1864 NewItemsChunk {
1865 previous: Some(ChunkIdentifier(1)),
1866 new: ChunkIdentifier(2),
1867 next: None,
1868 },
1869 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
1870 NewItemsChunk {
1871 previous: Some(ChunkIdentifier(2)),
1872 new: ChunkIdentifier(3),
1873 next: None,
1874 },
1875 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
1876 ]
1877 );
1878
1879 linked_chunk.push_gap_back(());
1880 linked_chunk.push_gap_back(()); assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
1882 assert_eq!(
1883 linked_chunk.updates().unwrap().take(),
1884 &[
1885 NewGapChunk {
1886 previous: Some(ChunkIdentifier(3)),
1887 new: ChunkIdentifier(4),
1888 next: None,
1889 gap: (),
1890 },
1891 NewGapChunk {
1892 previous: Some(ChunkIdentifier(4)),
1893 new: ChunkIdentifier(5),
1894 next: None,
1895 gap: (),
1896 }
1897 ]
1898 );
1899
1900 linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
1901 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
1902 assert_eq!(
1903 linked_chunk.updates().unwrap().take(),
1904 &[
1905 NewItemsChunk {
1906 previous: Some(ChunkIdentifier(5)),
1907 new: ChunkIdentifier(6),
1908 next: None,
1909 },
1910 PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
1911 NewItemsChunk {
1912 previous: Some(ChunkIdentifier(6)),
1913 new: ChunkIdentifier(7),
1914 next: None,
1915 },
1916 PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
1917 ]
1918 );
1919
1920 assert_eq!(linked_chunk.num_items(), 9);
1921 }
1922
1923 #[test]
1924 fn test_identifiers_and_positions() {
1925 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
1926 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
1927 linked_chunk.push_gap_back(());
1928 linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
1929 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
1930
1931 assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
1932 assert_eq!(
1933 linked_chunk.item_position(|item| *item == 'e'),
1934 Some(Position(ChunkIdentifier(1), 1))
1935 );
1936 }
1937
1938 #[test]
1939 fn test_rchunks() {
1940 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1941 linked_chunk.push_items_back(['a', 'b']);
1942 linked_chunk.push_gap_back(());
1943 linked_chunk.push_items_back(['c', 'd', 'e']);
1944
1945 let mut iterator = linked_chunk.rchunks();
1946
1947 assert_matches!(
1948 iterator.next(),
1949 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
1950 assert_eq!(items, &['e']);
1951 }
1952 );
1953 assert_matches!(
1954 iterator.next(),
1955 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1956 assert_eq!(items, &['c', 'd']);
1957 }
1958 );
1959 assert_matches!(
1960 iterator.next(),
1961 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
1962 );
1963 assert_matches!(
1964 iterator.next(),
1965 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
1966 assert_eq!(items, &['a', 'b']);
1967 }
1968 );
1969 assert_matches!(iterator.next(), None);
1970 }
1971
1972 #[test]
1973 fn test_chunks() {
1974 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1975 linked_chunk.push_items_back(['a', 'b']);
1976 linked_chunk.push_gap_back(());
1977 linked_chunk.push_items_back(['c', 'd', 'e']);
1978
1979 let mut iterator = linked_chunk.chunks();
1980
1981 assert_matches!(
1982 iterator.next(),
1983 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
1984 assert_eq!(items, &['a', 'b']);
1985 }
1986 );
1987 assert_matches!(
1988 iterator.next(),
1989 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
1990 );
1991 assert_matches!(
1992 iterator.next(),
1993 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1994 assert_eq!(items, &['c', 'd']);
1995 }
1996 );
1997 assert_matches!(
1998 iterator.next(),
1999 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2000 assert_eq!(items, &['e']);
2001 }
2002 );
2003 assert_matches!(iterator.next(), None);
2004 }
2005
2006 #[test]
2007 fn test_rchunks_from() -> Result<(), Error> {
2008 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2009 linked_chunk.push_items_back(['a', 'b']);
2010 linked_chunk.push_gap_back(());
2011 linked_chunk.push_items_back(['c', 'd', 'e']);
2012
2013 let mut iterator = linked_chunk.rchunks_from(
2014 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2015 )?;
2016
2017 assert_matches!(
2018 iterator.next(),
2019 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2020 assert_eq!(items, &['c', 'd']);
2021 }
2022 );
2023 assert_matches!(
2024 iterator.next(),
2025 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2026 );
2027 assert_matches!(
2028 iterator.next(),
2029 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2030 assert_eq!(items, &['a', 'b']);
2031 }
2032 );
2033 assert_matches!(iterator.next(), None);
2034
2035 Ok(())
2036 }
2037
2038 #[test]
2039 fn test_chunks_from() -> Result<(), Error> {
2040 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2041 linked_chunk.push_items_back(['a', 'b']);
2042 linked_chunk.push_gap_back(());
2043 linked_chunk.push_items_back(['c', 'd', 'e']);
2044
2045 let mut iterator = linked_chunk.chunks_from(
2046 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2047 )?;
2048
2049 assert_matches!(
2050 iterator.next(),
2051 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2052 assert_eq!(items, &['c', 'd']);
2053 }
2054 );
2055 assert_matches!(
2056 iterator.next(),
2057 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2058 assert_eq!(items, &['e']);
2059 }
2060 );
2061 assert_matches!(iterator.next(), None);
2062
2063 Ok(())
2064 }
2065
2066 #[test]
2067 fn test_ritems() {
2068 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2069 linked_chunk.push_items_back(['a', 'b']);
2070 linked_chunk.push_gap_back(());
2071 linked_chunk.push_items_back(['c', 'd', 'e']);
2072
2073 let mut iterator = linked_chunk.ritems();
2074
2075 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2076 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2077 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2078 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2079 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2080 assert_matches!(iterator.next(), None);
2081 }
2082
2083 #[test]
2084 fn test_ritems_with_final_gap() -> Result<(), Error> {
2085 let mut linked_chunk = LinkedChunk::<3, 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 linked_chunk.push_gap_back(());
2090
2091 let mut iterator = linked_chunk.ritems();
2092
2093 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
2094 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2095 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2096 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2097 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2098 assert_matches!(iterator.next(), None);
2099
2100 Ok(())
2101 }
2102
2103 #[test]
2104 fn test_ritems_empty() {
2105 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2106 let mut iterator = linked_chunk.ritems();
2107
2108 assert_matches!(iterator.next(), None);
2109 }
2110
2111 #[test]
2112 fn test_items() {
2113 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2114 linked_chunk.push_items_back(['a', 'b']);
2115 linked_chunk.push_gap_back(());
2116 linked_chunk.push_items_back(['c', 'd', 'e']);
2117
2118 let mut iterator = linked_chunk.items();
2119
2120 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2121 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2122 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2123 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2124 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2125 assert_matches!(iterator.next(), None);
2126 }
2127
2128 #[test]
2129 fn test_items_empty() {
2130 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2131 let mut iterator = linked_chunk.items();
2132
2133 assert_matches!(iterator.next(), None);
2134 }
2135
2136 #[test]
2137 fn test_ritems_from() -> Result<(), Error> {
2138 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2139 linked_chunk.push_items_back(['a', 'b']);
2140 linked_chunk.push_gap_back(());
2141 linked_chunk.push_items_back(['c', 'd', 'e']);
2142
2143 let mut iterator =
2144 linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2145
2146 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2147 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2148 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2149 assert_matches!(iterator.next(), None);
2150
2151 Ok(())
2152 }
2153
2154 #[test]
2155 fn test_items_from() -> Result<(), Error> {
2156 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2157 linked_chunk.push_items_back(['a', 'b']);
2158 linked_chunk.push_gap_back(());
2159 linked_chunk.push_items_back(['c', 'd', 'e']);
2160
2161 let mut iterator =
2162 linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2163
2164 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2165 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2166 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2167 assert_matches!(iterator.next(), None);
2168
2169 Ok(())
2170 }
2171
2172 #[test]
2173 fn test_insert_items_at() -> Result<(), Error> {
2174 use super::Update::*;
2175
2176 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2177
2178 let _ = linked_chunk.updates().unwrap().take();
2180
2181 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2182 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2183 assert_eq!(
2184 linked_chunk.updates().unwrap().take(),
2185 &[
2186 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2187 NewItemsChunk {
2188 previous: Some(ChunkIdentifier(0)),
2189 new: ChunkIdentifier(1),
2190 next: None,
2191 },
2192 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2193 ]
2194 );
2195
2196 {
2198 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2199
2200 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2203
2204 assert_items_eq!(
2205 linked_chunk,
2206 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2207 );
2208 assert_eq!(linked_chunk.num_items(), 10);
2209 assert_eq!(
2210 linked_chunk.updates().unwrap().take(),
2211 &[
2212 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2213 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2214 NewItemsChunk {
2215 previous: Some(ChunkIdentifier(1)),
2216 new: ChunkIdentifier(2),
2217 next: None,
2218 },
2219 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2220 StartReattachItems,
2221 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2222 NewItemsChunk {
2223 previous: Some(ChunkIdentifier(2)),
2224 new: ChunkIdentifier(3),
2225 next: None,
2226 },
2227 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2228 EndReattachItems,
2229 ]
2230 );
2231 }
2232
2233 {
2235 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2236 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2237
2238 assert_items_eq!(
2239 linked_chunk,
2240 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2241 );
2242 assert_eq!(linked_chunk.num_items(), 14);
2243 assert_eq!(
2244 linked_chunk.updates().unwrap().take(),
2245 &[
2246 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2247 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2248 NewItemsChunk {
2249 previous: Some(ChunkIdentifier(0)),
2250 new: ChunkIdentifier(4),
2251 next: Some(ChunkIdentifier(1)),
2252 },
2253 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2254 StartReattachItems,
2255 PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2256 NewItemsChunk {
2257 previous: Some(ChunkIdentifier(4)),
2258 new: ChunkIdentifier(5),
2259 next: Some(ChunkIdentifier(1)),
2260 },
2261 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2262 EndReattachItems,
2263 ]
2264 );
2265 }
2266
2267 {
2269 let pos_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2270 linked_chunk.insert_items_at(pos_c, ['r', 's'])?;
2271
2272 assert_items_eq!(
2273 linked_chunk,
2274 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2275 );
2276 assert_eq!(linked_chunk.num_items(), 16);
2277 assert_eq!(
2278 linked_chunk.updates().unwrap().take(),
2279 &[
2280 DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2281 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2282 StartReattachItems,
2283 PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2284 EndReattachItems,
2285 ]
2286 );
2287 }
2288
2289 {
2291 let pos_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2292 let pos_f = Position(pos_f.chunk_identifier(), pos_f.index() + 1);
2293
2294 linked_chunk.insert_items_at(pos_f, ['p', 'q'])?;
2295 assert_items_eq!(
2296 linked_chunk,
2297 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2298 );
2299 assert_eq!(
2300 linked_chunk.updates().unwrap().take(),
2301 &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2302 );
2303 assert_eq!(linked_chunk.num_items(), 18);
2304 }
2305
2306 {
2308 assert_matches!(
2309 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2310 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2311 );
2312 assert!(linked_chunk.updates().unwrap().take().is_empty());
2313 }
2314
2315 {
2317 assert_matches!(
2318 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2319 Err(Error::InvalidItemIndex { index: 128 })
2320 );
2321 assert!(linked_chunk.updates().unwrap().take().is_empty());
2322 }
2323
2324 {
2326 linked_chunk.push_gap_back(());
2328 assert_items_eq!(
2329 linked_chunk,
2330 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2331 );
2332 assert_eq!(
2333 linked_chunk.updates().unwrap().take(),
2334 &[NewGapChunk {
2335 previous: Some(ChunkIdentifier(3)),
2336 new: ChunkIdentifier(6),
2337 next: None,
2338 gap: ()
2339 }]
2340 );
2341
2342 assert_matches!(
2343 linked_chunk.insert_items_at(Position(ChunkIdentifier(6), 0), ['u', 'v'],),
2344 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2345 );
2346 }
2347
2348 assert_eq!(linked_chunk.num_items(), 18);
2349
2350 Ok(())
2351 }
2352
2353 #[test]
2354 fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2355 use super::Update::*;
2356
2357 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2358
2359 let _ = linked_chunk.updates().unwrap().take();
2361
2362 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2363 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2364 assert_eq!(
2365 linked_chunk.updates().unwrap().take(),
2366 &[
2367 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2368 NewItemsChunk {
2369 previous: Some(ChunkIdentifier(0)),
2370 new: ChunkIdentifier(1),
2371 next: None,
2372 },
2373 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2374 ]
2375 );
2376
2377 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2379
2380 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2383
2384 assert_items_eq!(
2385 linked_chunk,
2386 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2387 );
2388 assert_eq!(linked_chunk.num_items(), 10);
2389 assert_eq!(
2390 linked_chunk.updates().unwrap().take(),
2391 &[
2392 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2393 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2394 NewItemsChunk {
2395 previous: Some(ChunkIdentifier(1)),
2396 new: ChunkIdentifier(2),
2397 next: None,
2398 },
2399 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2400 StartReattachItems,
2401 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2402 NewItemsChunk {
2403 previous: Some(ChunkIdentifier(2)),
2404 new: ChunkIdentifier(3),
2405 next: None,
2406 },
2407 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2408 EndReattachItems,
2409 ]
2410 );
2411
2412 Ok(())
2413 }
2414
2415 #[test]
2416 fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2417 use super::Update::*;
2418
2419 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2420
2421 let _ = linked_chunk.updates().unwrap().take();
2423
2424 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2425 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2426 assert_eq!(
2427 linked_chunk.updates().unwrap().take(),
2428 &[
2429 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2430 NewItemsChunk {
2431 previous: Some(ChunkIdentifier(0)),
2432 new: ChunkIdentifier(1),
2433 next: None,
2434 },
2435 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2436 ]
2437 );
2438
2439 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2441 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2442
2443 assert_items_eq!(
2444 linked_chunk,
2445 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'e', 'f']
2446 );
2447 assert_eq!(linked_chunk.num_items(), 10);
2448 assert_eq!(
2449 linked_chunk.updates().unwrap().take(),
2450 &[
2451 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2452 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2453 NewItemsChunk {
2454 previous: Some(ChunkIdentifier(0)),
2455 new: ChunkIdentifier(2),
2456 next: Some(ChunkIdentifier(1)),
2457 },
2458 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2459 StartReattachItems,
2460 PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2461 NewItemsChunk {
2462 previous: Some(ChunkIdentifier(2)),
2463 new: ChunkIdentifier(3),
2464 next: Some(ChunkIdentifier(1)),
2465 },
2466 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2467 EndReattachItems,
2468 ]
2469 );
2470
2471 Ok(())
2472 }
2473
2474 #[test]
2475 fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2476 use super::Update::*;
2477
2478 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2479
2480 let _ = linked_chunk.updates().unwrap().take();
2482
2483 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2484 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2485 assert_eq!(
2486 linked_chunk.updates().unwrap().take(),
2487 &[
2488 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2489 NewItemsChunk {
2490 previous: Some(ChunkIdentifier(0)),
2491 new: ChunkIdentifier(1),
2492 next: None,
2493 },
2494 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2495 NewItemsChunk {
2496 previous: Some(ChunkIdentifier(1)),
2497 new: ChunkIdentifier(2),
2498 next: None,
2499 },
2500 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2501 ]
2502 );
2503
2504 let pos_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2505 linked_chunk.insert_items_at(pos_d, ['r', 's'])?;
2506
2507 assert_items_eq!(
2508 linked_chunk,
2509 ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2510 );
2511 assert_eq!(linked_chunk.num_items(), 10);
2512 assert_eq!(
2513 linked_chunk.updates().unwrap().take(),
2514 &[
2515 DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2516 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2517 StartReattachItems,
2518 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2519 NewItemsChunk {
2520 previous: Some(ChunkIdentifier(1)),
2521 new: ChunkIdentifier(3),
2522 next: Some(ChunkIdentifier(2)),
2523 },
2524 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2525 EndReattachItems,
2526 ]
2527 );
2528
2529 Ok(())
2530 }
2531
2532 #[test]
2533 fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2534 use super::Update::*;
2535
2536 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2537
2538 let _ = linked_chunk.updates().unwrap().take();
2540
2541 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2542 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2543 assert_eq!(
2544 linked_chunk.updates().unwrap().take(),
2545 &[
2546 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2547 NewItemsChunk {
2548 previous: Some(ChunkIdentifier(0)),
2549 new: ChunkIdentifier(1),
2550 next: None,
2551 },
2552 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2553 ]
2554 );
2555
2556 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2558 let pos_after_e = Position(pos_e.chunk_identifier(), pos_e.index() + 1);
2559
2560 linked_chunk.insert_items_at(pos_after_e, ['p', 'q'])?;
2561 assert_items_eq!(
2562 linked_chunk,
2563 ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2564 );
2565 assert_eq!(
2566 linked_chunk.updates().unwrap().take(),
2567 &[
2568 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2569 NewItemsChunk {
2570 previous: Some(ChunkIdentifier(1)),
2571 new: ChunkIdentifier(2),
2572 next: None
2573 },
2574 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2575 ]
2576 );
2577 assert_eq!(linked_chunk.num_items(), 7);
2578
2579 Ok(())
2580 }
2581
2582 #[test]
2583 fn test_insert_items_at_errs() -> Result<(), Error> {
2584 use super::Update::*;
2585
2586 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2587
2588 let _ = linked_chunk.updates().unwrap().take();
2590
2591 linked_chunk.push_items_back(['a', 'b', 'c']);
2592 linked_chunk.push_gap_back(());
2593 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2594 assert_eq!(
2595 linked_chunk.updates().unwrap().take(),
2596 &[
2597 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2598 NewGapChunk {
2599 previous: Some(ChunkIdentifier(0)),
2600 new: ChunkIdentifier(1),
2601 next: None,
2602 gap: (),
2603 },
2604 ]
2605 );
2606
2607 {
2609 assert_matches!(
2610 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2611 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2612 );
2613 assert!(linked_chunk.updates().unwrap().take().is_empty());
2614 }
2615
2616 {
2618 assert_matches!(
2619 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2620 Err(Error::InvalidItemIndex { index: 128 })
2621 );
2622 assert!(linked_chunk.updates().unwrap().take().is_empty());
2623 }
2624
2625 {
2627 assert_matches!(
2628 linked_chunk.insert_items_at(Position(ChunkIdentifier(1), 0), ['u', 'v'],),
2629 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2630 );
2631 }
2632
2633 Ok(())
2634 }
2635
2636 #[test]
2637 fn test_remove_item_at() -> Result<(), Error> {
2638 use super::Update::*;
2639
2640 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2641
2642 let _ = linked_chunk.updates().unwrap().take();
2644
2645 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2646 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2647 assert_eq!(linked_chunk.num_items(), 11);
2648
2649 let _ = linked_chunk.updates().unwrap().take();
2651
2652 {
2655 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2656 let removed_item = linked_chunk.remove_item_at(position_of_f)?;
2657
2658 assert_eq!(removed_item, 'f');
2659 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2660 assert_eq!(linked_chunk.num_items(), 10);
2661
2662 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2663 let removed_item = linked_chunk.remove_item_at(position_of_e)?;
2664
2665 assert_eq!(removed_item, 'e');
2666 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2667 assert_eq!(linked_chunk.num_items(), 9);
2668
2669 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2670 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2671
2672 assert_eq!(removed_item, 'd');
2673 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2674 assert_eq!(linked_chunk.num_items(), 8);
2675
2676 assert_eq!(
2677 linked_chunk.updates().unwrap().take(),
2678 &[
2679 RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2680 RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2681 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2682 RemoveChunk(ChunkIdentifier(1)),
2683 ]
2684 );
2685 }
2686
2687 {
2690 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2691 let removed_item = linked_chunk.remove_item_at(first_position)?;
2692
2693 assert_eq!(removed_item, 'a');
2694 assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2695 assert_eq!(linked_chunk.num_items(), 7);
2696
2697 let removed_item = linked_chunk.remove_item_at(first_position)?;
2698
2699 assert_eq!(removed_item, 'b');
2700 assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2701 assert_eq!(linked_chunk.num_items(), 6);
2702
2703 let removed_item = linked_chunk.remove_item_at(first_position)?;
2704
2705 assert_eq!(removed_item, 'c');
2706 assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2707 assert_eq!(linked_chunk.num_items(), 5);
2708
2709 assert_eq!(
2710 linked_chunk.updates().unwrap().take(),
2711 &[
2712 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2713 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2714 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2715 ]
2716 );
2717 }
2718
2719 {
2722 let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2723 let removed_item = linked_chunk.remove_item_at(first_position)?;
2724
2725 assert_eq!(removed_item, 'g');
2726 assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2727 assert_eq!(linked_chunk.num_items(), 4);
2728
2729 let removed_item = linked_chunk.remove_item_at(first_position)?;
2730
2731 assert_eq!(removed_item, 'h');
2732 assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2733 assert_eq!(linked_chunk.num_items(), 3);
2734
2735 let removed_item = linked_chunk.remove_item_at(first_position)?;
2736
2737 assert_eq!(removed_item, 'i');
2738 assert_items_eq!(linked_chunk, [] ['j', 'k']);
2739 assert_eq!(linked_chunk.num_items(), 2);
2740
2741 assert_eq!(
2742 linked_chunk.updates().unwrap().take(),
2743 &[
2744 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2745 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2746 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2747 RemoveChunk(ChunkIdentifier(2)),
2748 ]
2749 );
2750 }
2751
2752 {
2755 let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2756 let removed_item = linked_chunk.remove_item_at(position_of_k)?;
2757
2758 assert_eq!(removed_item, 'k');
2759 #[rustfmt::skip]
2760 assert_items_eq!(linked_chunk, [] ['j']);
2761 assert_eq!(linked_chunk.num_items(), 1);
2762
2763 let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2764 let removed_item = linked_chunk.remove_item_at(position_of_j)?;
2765
2766 assert_eq!(removed_item, 'j');
2767 assert_items_eq!(linked_chunk, []);
2768 assert_eq!(linked_chunk.num_items(), 0);
2769
2770 assert_eq!(
2771 linked_chunk.updates().unwrap().take(),
2772 &[
2773 RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2774 RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2775 RemoveChunk(ChunkIdentifier(3)),
2776 ]
2777 );
2778 }
2779
2780 {
2782 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2783
2784 #[rustfmt::skip]
2785 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2786 assert_eq!(linked_chunk.num_items(), 4);
2787
2788 assert_matches!(
2790 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 3)),
2791 Err(Error::InvalidItemIndex { index: 3 })
2792 );
2793
2794 assert_matches!(
2796 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 42)),
2797 Err(Error::InvalidItemIndex { index: 42 })
2798 );
2799
2800 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2801 linked_chunk.insert_gap_at((), position_of_c)?;
2802
2803 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2804 assert_eq!(linked_chunk.num_items(), 4);
2805
2806 let _ = linked_chunk.updates().unwrap().take();
2808
2809 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2810 let removed_item = linked_chunk.remove_item_at(position_of_c)?;
2811
2812 assert_eq!(removed_item, 'c');
2813 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2814 assert_eq!(linked_chunk.num_items(), 3);
2815
2816 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2817 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2818
2819 assert_eq!(removed_item, 'd');
2820 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2821 assert_eq!(linked_chunk.num_items(), 2);
2822
2823 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2824 let removed_item = linked_chunk.remove_item_at(first_position)?;
2825
2826 assert_eq!(removed_item, 'a');
2827 assert_items_eq!(linked_chunk, ['b'] [-]);
2828 assert_eq!(linked_chunk.num_items(), 1);
2829
2830 let removed_item = linked_chunk.remove_item_at(first_position)?;
2831
2832 assert_eq!(removed_item, 'b');
2833 assert_items_eq!(linked_chunk, [] [-]);
2834 assert_eq!(linked_chunk.num_items(), 0);
2835
2836 assert_eq!(
2837 linked_chunk.updates().unwrap().take(),
2838 &[
2839 RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2840 RemoveChunk(ChunkIdentifier(6)),
2841 RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2842 RemoveChunk(ChunkIdentifier(4)),
2843 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2844 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2845 ]
2846 );
2847 }
2848
2849 Ok(())
2850 }
2851
2852 #[test]
2853 fn test_insert_gap_at() -> Result<(), Error> {
2854 use super::Update::*;
2855
2856 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2857
2858 let _ = linked_chunk.updates().unwrap().take();
2860
2861 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2862 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2863 assert_eq!(
2864 linked_chunk.updates().unwrap().take(),
2865 &[
2866 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2867 NewItemsChunk {
2868 previous: Some(ChunkIdentifier(0)),
2869 new: ChunkIdentifier(1),
2870 next: None
2871 },
2872 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2873 ]
2874 );
2875
2876 {
2878 let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
2879 linked_chunk.insert_gap_at((), position_of_b)?;
2880
2881 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2882 assert_eq!(
2883 linked_chunk.updates().unwrap().take(),
2884 &[
2885 DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
2886 NewGapChunk {
2887 previous: Some(ChunkIdentifier(0)),
2888 new: ChunkIdentifier(2),
2889 next: Some(ChunkIdentifier(1)),
2890 gap: (),
2891 },
2892 StartReattachItems,
2893 NewItemsChunk {
2894 previous: Some(ChunkIdentifier(2)),
2895 new: ChunkIdentifier(3),
2896 next: Some(ChunkIdentifier(1)),
2897 },
2898 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
2899 EndReattachItems,
2900 ]
2901 );
2902 }
2903
2904 {
2907 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2908 linked_chunk.insert_gap_at((), position_of_a)?;
2909
2910 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2913 assert_eq!(
2914 linked_chunk.updates().unwrap().take(),
2915 &[NewGapChunk {
2916 previous: None,
2917 new: ChunkIdentifier(4),
2918 next: Some(ChunkIdentifier(0)),
2919 gap: (),
2920 },]
2921 );
2922 }
2923
2924 {
2927 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2928 linked_chunk.insert_gap_at((), position_of_d)?;
2929
2930 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
2934 assert_eq!(
2935 linked_chunk.updates().unwrap().take(),
2936 &[NewGapChunk {
2937 previous: Some(ChunkIdentifier(3)),
2938 new: ChunkIdentifier(5),
2939 next: Some(ChunkIdentifier(1)),
2940 gap: (),
2941 }]
2942 );
2943 }
2944
2945 {
2947 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
2949 let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
2950
2951 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
2952
2953 assert_eq!(
2954 linked_chunk.updates().unwrap().take(),
2955 &[
2956 NewItemsChunk {
2957 previous: Some(ChunkIdentifier(5)),
2958 new: ChunkIdentifier(6),
2959 next: Some(ChunkIdentifier(1)),
2960 },
2961 RemoveChunk(ChunkIdentifier(5)),
2962 ]
2963 );
2964
2965 linked_chunk.insert_gap_at((), position)?;
2966
2967 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
2968 assert_eq!(
2969 linked_chunk.updates().unwrap().take(),
2970 &[NewGapChunk {
2971 previous: Some(ChunkIdentifier(3)),
2972 new: ChunkIdentifier(7),
2973 next: Some(ChunkIdentifier(6)),
2974 gap: (),
2975 }]
2976 );
2977 }
2978
2979 {
2981 assert_matches!(
2982 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2983 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2984 );
2985 assert!(linked_chunk.updates().unwrap().take().is_empty());
2986 }
2987
2988 {
2990 assert_matches!(
2991 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2992 Err(Error::InvalidItemIndex { index: 128 })
2993 );
2994 assert!(linked_chunk.updates().unwrap().take().is_empty());
2995 }
2996
2997 {
2999 let position_of_a_gap = Position(ChunkIdentifier(2), 0);
3002 assert_matches!(
3003 linked_chunk.insert_gap_at((), position_of_a_gap),
3004 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
3005 );
3006 assert!(linked_chunk.updates().unwrap().take().is_empty());
3007 }
3008
3009 assert_eq!(linked_chunk.num_items(), 6);
3010
3011 Ok(())
3012 }
3013
3014 #[test]
3015 fn test_replace_gap_at_middle() -> Result<(), Error> {
3016 use super::Update::*;
3017
3018 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3019
3020 let _ = linked_chunk.updates().unwrap().take();
3022
3023 linked_chunk.push_items_back(['a', 'b']);
3024 linked_chunk.push_gap_back(());
3025 linked_chunk.push_items_back(['l', 'm']);
3026 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
3027 assert_eq!(
3028 linked_chunk.updates().unwrap().take(),
3029 &[
3030 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3031 NewGapChunk {
3032 previous: Some(ChunkIdentifier(0)),
3033 new: ChunkIdentifier(1),
3034 next: None,
3035 gap: (),
3036 },
3037 NewItemsChunk {
3038 previous: Some(ChunkIdentifier(1)),
3039 new: ChunkIdentifier(2),
3040 next: None,
3041 },
3042 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
3043 ]
3044 );
3045
3046 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3048 assert_eq!(gap_identifier, ChunkIdentifier(1));
3049
3050 let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
3051 assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
3052 assert_items_eq!(
3053 linked_chunk,
3054 ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
3055 );
3056 assert_eq!(
3057 linked_chunk.updates().unwrap().take(),
3058 &[
3059 NewItemsChunk {
3060 previous: Some(ChunkIdentifier(1)),
3061 new: ChunkIdentifier(3),
3062 next: Some(ChunkIdentifier(2)),
3063 },
3064 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
3065 NewItemsChunk {
3066 previous: Some(ChunkIdentifier(3)),
3067 new: ChunkIdentifier(4),
3068 next: Some(ChunkIdentifier(2)),
3069 },
3070 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
3071 RemoveChunk(ChunkIdentifier(1)),
3072 ]
3073 );
3074
3075 assert_eq!(linked_chunk.num_items(), 9);
3076
3077 Ok(())
3078 }
3079
3080 #[test]
3081 fn test_replace_gap_at_end() -> Result<(), Error> {
3082 use super::Update::*;
3083
3084 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3085
3086 let _ = linked_chunk.updates().unwrap().take();
3088
3089 linked_chunk.push_items_back(['a', 'b']);
3090 linked_chunk.push_gap_back(());
3091 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3092 assert_eq!(
3093 linked_chunk.updates().unwrap().take(),
3094 &[
3095 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3096 NewGapChunk {
3097 previous: Some(ChunkIdentifier(0)),
3098 new: ChunkIdentifier(1),
3099 next: None,
3100 gap: (),
3101 },
3102 ]
3103 );
3104
3105 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3107 assert_eq!(gap_identifier, ChunkIdentifier(1));
3108
3109 let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3110 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3111 assert_items_eq!(
3112 linked_chunk,
3113 ['a', 'b'] ['w', 'x', 'y'] ['z']
3114 );
3115 assert_eq!(
3116 linked_chunk.updates().unwrap().take(),
3117 &[
3118 NewItemsChunk {
3119 previous: Some(ChunkIdentifier(1)),
3120 new: ChunkIdentifier(2),
3121 next: None,
3122 },
3123 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3124 NewItemsChunk {
3125 previous: Some(ChunkIdentifier(2)),
3126 new: ChunkIdentifier(3),
3127 next: None,
3128 },
3129 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3130 RemoveChunk(ChunkIdentifier(1)),
3131 ]
3132 );
3133
3134 assert_eq!(linked_chunk.num_items(), 6);
3135
3136 Ok(())
3137 }
3138
3139 #[test]
3140 fn test_replace_gap_at_beginning() -> Result<(), Error> {
3141 use super::Update::*;
3142
3143 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3144
3145 let _ = linked_chunk.updates().unwrap().take();
3147
3148 linked_chunk.push_items_back(['a', 'b']);
3149 assert_items_eq!(linked_chunk, ['a', 'b']);
3150 assert_eq!(
3151 linked_chunk.updates().unwrap().take(),
3152 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },]
3153 );
3154
3155 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3157 linked_chunk.insert_gap_at((), position_of_a).unwrap();
3158 assert_items_eq!(
3159 linked_chunk,
3160 [-] ['a', 'b']
3161 );
3162 assert_eq!(
3163 linked_chunk.updates().unwrap().take(),
3164 &[NewGapChunk {
3165 previous: None,
3166 new: ChunkIdentifier(1),
3167 next: Some(ChunkIdentifier(0)),
3168 gap: (),
3169 }]
3170 );
3171
3172 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3173 assert_eq!(gap_identifier, ChunkIdentifier(1));
3174
3175 let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3176 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3177 assert_items_eq!(
3178 linked_chunk,
3179 ['x'] ['a', 'b']
3180 );
3181 assert_eq!(
3182 linked_chunk.updates().unwrap().take(),
3183 &[
3184 NewItemsChunk {
3185 previous: Some(ChunkIdentifier(1)),
3186 new: ChunkIdentifier(2),
3187 next: Some(ChunkIdentifier(0)),
3188 },
3189 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3190 RemoveChunk(ChunkIdentifier(1)),
3191 ]
3192 );
3193
3194 assert_eq!(linked_chunk.num_items(), 3);
3195
3196 Ok(())
3197 }
3198
3199 #[test]
3200 fn test_remove_empty_chunk_at() -> Result<(), Error> {
3201 use super::Update::*;
3202
3203 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3204
3205 let _ = linked_chunk.updates().unwrap().take();
3207
3208 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3209 linked_chunk.push_items_back(['a', 'b']);
3210 linked_chunk.push_gap_back(());
3211 linked_chunk.push_items_back(['l', 'm']);
3212 linked_chunk.push_gap_back(());
3213 assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3214 assert_eq!(
3215 linked_chunk.updates().unwrap().take(),
3216 &[
3217 NewGapChunk {
3218 previous: None,
3219 new: ChunkIdentifier(1),
3220 next: Some(ChunkIdentifier(0)),
3221 gap: (),
3222 },
3223 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3224 NewGapChunk {
3225 previous: Some(ChunkIdentifier(0)),
3226 new: ChunkIdentifier(2),
3227 next: None,
3228 gap: (),
3229 },
3230 NewItemsChunk {
3231 previous: Some(ChunkIdentifier(2)),
3232 new: ChunkIdentifier(3),
3233 next: None,
3234 },
3235 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3236 NewGapChunk {
3237 previous: Some(ChunkIdentifier(3)),
3238 new: ChunkIdentifier(4),
3239 next: None,
3240 gap: (),
3241 },
3242 ]
3243 );
3244
3245 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3247 assert_matches!(err, Error::RemovingNonEmptyItemsChunk { .. });
3248
3249 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(42)).unwrap_err();
3251 assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3252
3253 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(2)).unwrap();
3255 let next = maybe_next.unwrap();
3256 assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3258 assert_eq!(next.index(), 0);
3259 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3260 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3261
3262 let next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(4)).unwrap();
3264 assert!(next.is_none());
3266 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3267 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3268
3269 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(1)).unwrap();
3271 let next = maybe_next.unwrap();
3272 assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3273 assert_eq!(next.index(), 0);
3274 assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3275 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3276
3277 Ok(())
3278 }
3279
3280 #[test]
3281 fn test_remove_empty_last_chunk() {
3282 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3283
3284 let _ = linked_chunk.updates().unwrap().take();
3286
3287 assert_items_eq!(linked_chunk, []);
3288 assert!(linked_chunk.updates().unwrap().take().is_empty());
3289
3290 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3292 assert_matches!(err, Error::RemovingLastChunk);
3293 }
3294
3295 #[test]
3296 fn test_chunk_item_positions() {
3297 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3298 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3299 linked_chunk.push_gap_back(());
3300 linked_chunk.push_items_back(['f']);
3301
3302 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3303
3304 let mut iterator = linked_chunk.chunks();
3305
3306 {
3308 let chunk = iterator.next().unwrap();
3309 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3310 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3311 }
3312
3313 {
3315 let chunk = iterator.next().unwrap();
3316 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3317 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3318 }
3319
3320 {
3322 let chunk = iterator.next().unwrap();
3323 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3324 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3325 }
3326
3327 {
3329 let chunk = iterator.next().unwrap();
3330 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3331 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3332 }
3333 }
3334
3335 #[test]
3336 fn test_is_first_and_last_chunk() {
3337 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3338
3339 let mut chunks = linked_chunk.chunks().peekable();
3340 assert!(chunks.peek().unwrap().is_first_chunk());
3341 assert!(chunks.next().unwrap().is_last_chunk());
3342 assert!(chunks.next().is_none());
3343
3344 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3345
3346 let mut chunks = linked_chunk.chunks().peekable();
3347 assert!(chunks.next().unwrap().is_first_chunk());
3348 assert!(chunks.peek().unwrap().is_first_chunk().not());
3349 assert!(chunks.next().unwrap().is_last_chunk().not());
3350 assert!(chunks.next().unwrap().is_last_chunk());
3351 assert!(chunks.next().is_none());
3352 }
3353
3354 #[test]
3359 fn test_clear() {
3360 let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3361
3362 let item = Arc::new('a');
3363 let gap = Arc::new(());
3364
3365 linked_chunk.push_items_back([
3366 item.clone(),
3367 item.clone(),
3368 item.clone(),
3369 item.clone(),
3370 item.clone(),
3371 ]);
3372 linked_chunk.push_gap_back(gap.clone());
3373 linked_chunk.push_items_back([item.clone()]);
3374
3375 assert_eq!(Arc::strong_count(&item), 7);
3376 assert_eq!(Arc::strong_count(&gap), 2);
3377 assert_eq!(linked_chunk.num_items(), 6);
3378 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3379
3380 linked_chunk.clear();
3382
3383 assert_eq!(Arc::strong_count(&item), 1);
3384 assert_eq!(Arc::strong_count(&gap), 1);
3385 assert_eq!(linked_chunk.num_items(), 0);
3386 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3387 }
3388
3389 #[test]
3390 fn test_clear_emit_an_update_clear() {
3391 use super::Update::*;
3392
3393 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3394
3395 assert_eq!(
3396 linked_chunk.updates().unwrap().take(),
3397 &[NewItemsChunk {
3398 previous: None,
3399 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3400 next: None
3401 }]
3402 );
3403
3404 linked_chunk.clear();
3405
3406 assert_eq!(
3407 linked_chunk.updates().unwrap().take(),
3408 &[
3409 Clear,
3410 NewItemsChunk {
3411 previous: None,
3412 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3413 next: None
3414 }
3415 ]
3416 );
3417 }
3418
3419 #[test]
3420 fn test_replace_item() {
3421 use super::Update::*;
3422
3423 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3424
3425 linked_chunk.push_items_back(['a', 'b', 'c']);
3426 linked_chunk.push_gap_back(());
3427 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3429
3430 let _ = linked_chunk.updates().unwrap().take();
3432
3433 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3435 assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3436
3437 assert_eq!(
3438 linked_chunk.updates().unwrap().take(),
3439 &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3440 );
3441
3442 assert_matches!(
3444 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3445 Err(Error::InvalidItemIndex { index: 3 })
3446 );
3447
3448 assert_matches!(
3450 linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3451 Err(Error::ChunkIsAGap { .. })
3452 );
3453 }
3454
3455 #[test]
3456 fn test_lazy_previous() {
3457 use std::marker::PhantomData;
3458
3459 use super::{Ends, ObservableUpdates, Update::*};
3460
3461 let first_chunk_identifier = ChunkIdentifier(0);
3463 let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3464 unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3465
3466 let mut linked_chunk = LinkedChunk::<3, char, ()> {
3467 links: Ends { first: first_loaded_chunk, last: None },
3468 chunk_identifier_generator:
3469 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3470 updates: Some(ObservableUpdates::new()),
3471 marker: PhantomData,
3472 };
3473
3474 {
3477 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3478
3479 assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3480
3481 {
3483 let mut chunks = linked_chunk.chunks();
3484
3485 assert_matches!(chunks.next(), Some(chunk) => {
3486 assert_eq!(chunk.identifier(), 1);
3487 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3488 });
3489 assert_matches!(chunks.next(), Some(chunk) => {
3490 assert_eq!(chunk.identifier(), 2);
3491 assert!(chunk.lazy_previous.is_none());
3492 });
3493 assert!(chunks.next().is_none());
3494 }
3495
3496 assert_eq!(
3498 linked_chunk.updates().unwrap().take(),
3499 &[
3500 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3501 NewItemsChunk {
3502 previous: Some(ChunkIdentifier(1)),
3503 new: ChunkIdentifier(2),
3504 next: None,
3505 },
3506 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3507 ]
3508 );
3509 }
3510
3511 {
3513 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3514
3515 assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3516
3517 {
3519 let mut chunks = linked_chunk.chunks();
3520
3521 assert_matches!(chunks.next(), Some(chunk) => {
3522 assert_eq!(chunk.identifier(), 3);
3523 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3525 });
3526 assert_matches!(chunks.next(), Some(chunk) => {
3527 assert_eq!(chunk.identifier(), 1);
3528 assert!(chunk.lazy_previous.is_none());
3530 });
3531 assert_matches!(chunks.next(), Some(chunk) => {
3532 assert_eq!(chunk.identifier(), 2);
3533 assert!(chunk.lazy_previous.is_none());
3534 });
3535 assert!(chunks.next().is_none());
3536 }
3537
3538 assert_eq!(
3540 linked_chunk.updates().unwrap().take(),
3541 &[NewGapChunk {
3542 previous: Some(ChunkIdentifier(0)),
3544 new: ChunkIdentifier(3),
3545 next: Some(ChunkIdentifier(1)),
3546 gap: ()
3547 }]
3548 );
3549 }
3550
3551 {
3553 linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3554
3555 assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3556
3557 {
3559 let mut chunks = linked_chunk.chunks();
3560
3561 assert_matches!(chunks.next(), Some(chunk) => {
3562 assert_eq!(chunk.identifier(), 4);
3563 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3565 });
3566 assert_matches!(chunks.next(), Some(chunk) => {
3567 assert_eq!(chunk.identifier(), 5);
3568 assert!(chunk.lazy_previous.is_none());
3569 });
3570 assert_matches!(chunks.next(), Some(chunk) => {
3571 assert_eq!(chunk.identifier(), 1);
3572 assert!(chunk.lazy_previous.is_none());
3573 });
3574 assert_matches!(chunks.next(), Some(chunk) => {
3575 assert_eq!(chunk.identifier(), 2);
3576 assert!(chunk.lazy_previous.is_none());
3577 });
3578 assert!(chunks.next().is_none());
3579 }
3580
3581 assert_eq!(
3583 linked_chunk.updates().unwrap().take(),
3584 &[
3585 NewItemsChunk {
3587 previous: Some(ChunkIdentifier(3)),
3588 new: ChunkIdentifier(4),
3589 next: Some(ChunkIdentifier(1)),
3590 },
3591 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3593 NewItemsChunk {
3595 previous: Some(ChunkIdentifier(4)),
3596 new: ChunkIdentifier(5),
3597 next: Some(ChunkIdentifier(1)),
3598 },
3599 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3601 RemoveChunk(ChunkIdentifier(3)),
3603 ]
3604 );
3605 }
3606
3607 {
3611 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3612
3613 assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3614
3615 {
3617 let mut chunks = linked_chunk.chunks();
3618
3619 assert_matches!(chunks.next(), Some(chunk) => {
3620 assert_eq!(chunk.identifier(), 6);
3621 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3623 });
3624 assert_matches!(chunks.next(), Some(chunk) => {
3625 assert_eq!(chunk.identifier(), 4);
3626 assert!(chunk.lazy_previous.is_none());
3628 });
3629 assert_matches!(chunks.next(), Some(chunk) => {
3630 assert_eq!(chunk.identifier(), 5);
3631 assert!(chunk.lazy_previous.is_none());
3632 });
3633 assert_matches!(chunks.next(), Some(chunk) => {
3634 assert_eq!(chunk.identifier(), 1);
3635 assert!(chunk.lazy_previous.is_none());
3636 });
3637 assert_matches!(chunks.next(), Some(chunk) => {
3638 assert_eq!(chunk.identifier(), 2);
3639 assert!(chunk.lazy_previous.is_none());
3640 });
3641 assert!(chunks.next().is_none());
3642 }
3643
3644 assert_eq!(
3646 linked_chunk.updates().unwrap().take(),
3647 &[NewGapChunk {
3648 previous: Some(ChunkIdentifier(0)),
3650 new: ChunkIdentifier(6),
3651 next: Some(ChunkIdentifier(4)),
3652 gap: ()
3653 }]
3654 );
3655 }
3656 }
3657}