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::{
106 OnceLock,
107 atomic::{self, AtomicU64},
108 },
109};
110
111pub use self::{as_vector::*, identifiers::*, order_tracker::OrderTracker, updates::*};
112
113#[derive(thiserror::Error, Debug)]
115pub enum Error {
116 #[error("The chunk identifier is invalid: `{identifier:?}`")]
118 InvalidChunkIdentifier {
119 identifier: ChunkIdentifier,
121 },
122
123 #[error("The chunk is a gap: `{identifier:?}`")]
125 ChunkIsAGap {
126 identifier: ChunkIdentifier,
128 },
129
130 #[error("The chunk is an item: `{identifier:?}`")]
132 ChunkIsItems {
133 identifier: ChunkIdentifier,
135 },
136
137 #[error("The chunk is a non-empty item chunk: `{identifier:?}`")]
139 RemovingNonEmptyItemsChunk {
140 identifier: ChunkIdentifier,
142 },
143
144 #[error("Trying to remove the only chunk, but a linked chunk can't be empty")]
147 RemovingLastChunk,
148
149 #[error("The item index is invalid: `{index}`")]
151 InvalidItemIndex {
152 index: usize,
154 },
155}
156
157struct Ends<const CHUNK_CAPACITY: usize, Item, Gap> {
162 first: OnceLock<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
164
165 last: Option<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
167
168 updates_pusher: Option<ObservableUpdatesPusher<Item, Gap>>,
169}
170
171impl<const CAP: usize, Item, Gap> Ends<CAP, Item, Gap> {
172 fn new(updates: &Option<ObservableUpdates<Item, Gap>>) -> Self {
174 Self {
175 first: OnceLock::new(),
176 last: None,
177 updates_pusher: updates.as_ref().map(ObservableUpdates::new_pusher),
178 }
179 }
180
181 fn new_with_first_chunk(
183 first_chunk: NonNull<Chunk<CAP, Item, Gap>>,
184 updates: &Option<ObservableUpdates<Item, Gap>>,
185 ) -> Self {
186 Self {
187 first: {
188 let first = OnceLock::new();
189
190 first.get_or_init(|| first_chunk);
192
193 first
194 },
195 last: None,
196 updates_pusher: updates.as_ref().map(ObservableUpdates::new_pusher),
197 }
198 }
199
200 fn first_chunk_ptr(&self) -> &NonNull<Chunk<CAP, Item, Gap>> {
202 self.first
203 .get_or_init(|| {
205 let identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
206
207 if let Some(updates) = self.updates_pusher.as_ref() {
208 updates.push(Update::NewItemsChunk {
209 previous: None,
210 new: identifier,
211 next: None,
212 });
213 }
214
215 Chunk::new_items_leaked(identifier)
216 })
217 }
218
219 fn first_chunk_mut_ptr(&mut self) -> &mut NonNull<Chunk<CAP, Item, Gap>> {
221 let _ = self.first_chunk_ptr();
224
225 self.first
226 .get_mut()
227 .expect("`first` must have been initialised")
232 }
233
234 fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
236 unsafe { self.first_chunk_ptr().as_ref() }
239 }
240
241 fn first_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
243 unsafe { self.first_chunk_mut_ptr().as_mut() }
246 }
247
248 fn latest_chunk(&self) -> &Chunk<CAP, Item, Gap> {
250 if let Some(last) = &self.last {
251 unsafe { last.as_ref() }
254 } else {
255 self.first_chunk()
256 }
257 }
258
259 fn latest_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
261 if let Some(last) = &mut self.last {
262 unsafe { last.as_mut() }
265 } else {
266 self.first_chunk_mut()
267 }
268 }
269
270 fn chunk(&self, identifier: ChunkIdentifier) -> Option<&Chunk<CAP, Item, Gap>> {
272 let mut chunk = self.latest_chunk();
273
274 loop {
275 if chunk.identifier() == identifier {
276 return Some(chunk);
277 }
278
279 chunk = chunk.previous()?;
280 }
281 }
282
283 fn chunk_mut(&mut self, identifier: ChunkIdentifier) -> Option<&mut Chunk<CAP, Item, Gap>> {
285 let mut chunk = self.latest_chunk_mut();
286
287 loop {
288 if chunk.identifier() == identifier {
289 return Some(chunk);
290 }
291
292 chunk = chunk.previous_mut()?;
293 }
294 }
295
296 fn clear(&mut self) {
299 let mut current_chunk_ptr = self.last.or_else(|| self.first.get().copied());
302
303 while let Some(chunk_ptr) = current_chunk_ptr {
305 let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
307
308 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
310
311 current_chunk_ptr = previous_ptr;
313 }
314
315 self.first.take();
317 self.last = None;
318 }
319
320 unsafe fn replace_with(&mut self, first_chunk: NonNull<Chunk<CAP, Item, Gap>>) {
329 self.clear();
330
331 let mut first_chunk = Some(first_chunk);
334 self.first.get_or_init(|| first_chunk.take().unwrap());
335
336 if first_chunk.is_some() {
337 unreachable!(
338 "`first` must be initialised to `first_chunk` because `clear` has been called"
339 );
340 }
341 }
342}
343
344pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
351 links: Ends<CHUNK_CAPACITY, Item, Gap>,
353
354 chunk_identifier_generator: ChunkIdentifierGenerator,
356
357 updates: Option<ObservableUpdates<Item, Gap>>,
361
362 marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
364}
365
366impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
367 fn default() -> Self {
368 Self::new()
369 }
370}
371
372impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
373 pub fn new() -> Self {
375 let updates = None;
376
377 Self {
378 links: Ends::new(&updates),
379 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
380 updates,
381 marker: PhantomData,
382 }
383 }
384
385 pub fn new_with_update_history() -> Self {
391 let updates = Some(ObservableUpdates::new());
392
393 Self {
394 links: Ends::new(&updates),
395 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
396 updates,
397 marker: PhantomData,
398 }
399 }
400
401 pub fn clear(&mut self) {
403 self.links.clear();
405
406 self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
408
409 if let Some(updates) = self.updates.as_mut() {
411 updates.clear_pending();
414 updates.push(Update::Clear);
415 }
416 }
417
418 pub fn push_items_back<I>(&mut self, items: I)
424 where
425 Item: Clone,
426 Gap: Clone,
427 I: IntoIterator<Item = Item>,
428 I::IntoIter: ExactSizeIterator,
429 {
430 let items = items.into_iter();
431
432 let last_chunk = self.links.latest_chunk_mut();
433
434 let last_chunk =
436 last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
437
438 debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
439
440 if !last_chunk.is_first_chunk() {
444 self.links.last = Some(last_chunk.as_ptr());
447 }
448 }
449
450 pub fn push_gap_back(&mut self, content: Gap)
453 where
454 Item: Clone,
455 Gap: Clone,
456 {
457 let last_chunk = self.links.latest_chunk_mut();
458 last_chunk.insert_next(
459 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
460 &mut self.updates,
461 );
462
463 self.links.last = last_chunk.next;
464 }
465
466 pub fn insert_items_at<I>(&mut self, position: Position, items: I) -> Result<(), Error>
471 where
472 Item: Clone,
473 Gap: Clone,
474 I: IntoIterator<Item = Item>,
475 I::IntoIter: ExactSizeIterator,
476 {
477 let chunk_identifier = position.chunk_identifier();
478 let item_index = position.index();
479
480 let chunk = self
481 .links
482 .chunk_mut(chunk_identifier)
483 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
484
485 let chunk = match &mut chunk.content {
486 ChunkContent::Gap(..) => {
487 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
488 }
489
490 ChunkContent::Items(current_items) => {
491 let current_items_length = current_items.len();
492
493 if item_index > current_items_length {
494 return Err(Error::InvalidItemIndex { index: item_index });
495 }
496
497 let items = items.into_iter();
499
500 if item_index == current_items_length {
502 chunk
503 .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
505 }
506 else {
508 if let Some(updates) = self.updates.as_mut() {
509 updates.push(Update::DetachLastItems {
510 at: Position(chunk_identifier, item_index),
511 });
512 }
513
514 let detached_items = current_items.split_off(item_index);
516
517 let chunk = chunk
518 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
520
521 if let Some(updates) = self.updates.as_mut() {
522 updates.push(Update::StartReattachItems);
523 }
524
525 let chunk = chunk
526 .push_items(
528 detached_items.into_iter(),
529 &self.chunk_identifier_generator,
530 &mut self.updates,
531 );
532
533 if let Some(updates) = self.updates.as_mut() {
534 updates.push(Update::EndReattachItems);
535 }
536
537 chunk
538 }
539 }
540 };
541
542 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
545 self.links.last = Some(chunk.as_ptr());
548 }
549
550 Ok(())
551 }
552
553 pub fn remove_item_at(&mut self, position: Position) -> Result<Item, Error> {
561 let chunk_identifier = position.chunk_identifier();
562 let item_index = position.index();
563
564 let mut chunk_ptr = None;
565 let removed_item;
566
567 {
568 let chunk = self
569 .links
570 .chunk_mut(chunk_identifier)
571 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
572
573 let current_items = match &mut chunk.content {
574 ChunkContent::Gap(..) => {
575 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
576 }
577 ChunkContent::Items(current_items) => current_items,
578 };
579
580 if item_index >= current_items.len() {
581 return Err(Error::InvalidItemIndex { index: item_index });
582 }
583
584 removed_item = current_items.remove(item_index);
585
586 if let Some(updates) = self.updates.as_mut() {
587 updates.push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
588 }
589
590 if current_items.is_empty() && !chunk.is_first_chunk() {
592 chunk.unlink(self.updates.as_mut());
594
595 chunk_ptr = Some(chunk.as_ptr());
596
597 if chunk.is_last_chunk() {
600 self.links.last = chunk.previous;
601 }
602 }
603
604 }
606
607 if let Some(chunk_ptr) = chunk_ptr {
608 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
615 }
616
617 Ok(removed_item)
618 }
619
620 pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
625 where
626 Item: Clone,
627 {
628 let chunk_identifier = position.chunk_identifier();
629 let item_index = position.index();
630
631 let chunk = self
632 .links
633 .chunk_mut(chunk_identifier)
634 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
635
636 match &mut chunk.content {
637 ChunkContent::Gap(..) => {
638 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
639 }
640
641 ChunkContent::Items(current_items) => {
642 if item_index >= current_items.len() {
643 return Err(Error::InvalidItemIndex { index: item_index });
644 }
645
646 if let Some(updates) = self.updates.as_mut() {
648 updates.push(Update::ReplaceItem {
649 at: Position(chunk_identifier, item_index),
650 item: item.clone(),
651 });
652 }
653
654 current_items[item_index] = item;
655 }
656 }
657
658 Ok(())
659 }
660
661 pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
666 where
667 Item: Clone,
668 Gap: Clone,
669 {
670 let chunk_identifier = position.chunk_identifier();
671 let item_index = position.index();
672
673 let chunk = self
674 .links
675 .chunk_mut(chunk_identifier)
676 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
677
678 let chunk = match &mut chunk.content {
679 ChunkContent::Gap(..) => {
680 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
681 }
682
683 ChunkContent::Items(current_items) => {
684 if item_index == 0 {
688 let chunk_was_first = chunk.is_first_chunk();
689 let chunk_was_last = chunk.is_last_chunk();
690
691 let new_chunk = chunk.insert_before(
692 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
693 self.updates.as_mut(),
694 );
695
696 let new_chunk_ptr = new_chunk.as_ptr();
697 let chunk_ptr = chunk.as_ptr();
698
699 if chunk_was_first {
704 *self.links.first_chunk_mut_ptr() = new_chunk_ptr;
705
706 if chunk_was_last {
708 self.links.last = Some(chunk_ptr);
709 }
710 }
711
712 return Ok(());
713 }
714
715 let current_items_length = current_items.len();
716
717 if item_index >= current_items_length {
718 return Err(Error::InvalidItemIndex { index: item_index });
719 }
720
721 if let Some(updates) = self.updates.as_mut() {
722 updates.push(Update::DetachLastItems {
723 at: Position(chunk_identifier, item_index),
724 });
725 }
726
727 let detached_items = current_items.split_off(item_index);
729
730 let chunk = chunk
731 .insert_next(
733 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
734 &mut self.updates,
735 );
736
737 if let Some(updates) = self.updates.as_mut() {
738 updates.push(Update::StartReattachItems);
739 }
740
741 let chunk = chunk
742 .insert_next(
744 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
745 &mut self.updates,
746 )
747 .push_items(
749 detached_items.into_iter(),
750 &self.chunk_identifier_generator,
751 &mut self.updates,
752 );
753
754 if let Some(updates) = self.updates.as_mut() {
755 updates.push(Update::EndReattachItems);
756 }
757
758 chunk
759 }
760 };
761
762 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
765 self.links.last = Some(chunk.as_ptr());
768 }
769
770 Ok(())
771 }
772
773 pub fn remove_empty_chunk_at(
782 &mut self,
783 chunk_identifier: ChunkIdentifier,
784 ) -> Result<Option<Position>, Error> {
785 if self.links.first_chunk().is_last_chunk() {
787 return Err(Error::RemovingLastChunk);
788 }
789
790 let chunk = self
791 .links
792 .chunk_mut(chunk_identifier)
793 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
794
795 if chunk.num_items() > 0 {
796 return Err(Error::RemovingNonEmptyItemsChunk { identifier: chunk_identifier });
797 }
798
799 let chunk_was_first = chunk.is_first_chunk();
800 let chunk_was_last = chunk.is_last_chunk();
801 let next_ptr = chunk.next;
802 let previous_ptr = chunk.previous;
803 let position_of_next = chunk.next().map(|next| next.first_position());
804
805 chunk.unlink(self.updates.as_mut());
806
807 let chunk_ptr = chunk.as_ptr();
808
809 if chunk_was_first {
811 if let Some(next_ptr) = next_ptr {
813 *self.links.first_chunk_mut_ptr() = next_ptr;
814 }
815 }
816
817 if chunk_was_last {
818 self.links.last = previous_ptr;
819 }
820
821 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
824
825 Ok(position_of_next)
827 }
828
829 pub fn replace_gap_at<I>(
837 &mut self,
838 items: I,
839 chunk_identifier: ChunkIdentifier,
840 ) -> Result<&Chunk<CAP, Item, Gap>, Error>
841 where
842 Item: Clone,
843 Gap: Clone,
844 I: IntoIterator<Item = Item>,
845 I::IntoIter: ExactSizeIterator,
846 {
847 let chunk_ptr;
848 let new_chunk_ptr;
849
850 {
851 let chunk = self
852 .links
853 .chunk_mut(chunk_identifier)
854 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
855
856 if chunk.is_items() {
857 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
858 }
859
860 let chunk_was_first = chunk.is_first_chunk();
861
862 let maybe_last_chunk_ptr = {
863 let items = items.into_iter();
864
865 let last_inserted_chunk = chunk
866 .insert_next(
868 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
869 &mut self.updates,
870 )
871 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
873
874 last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
875 };
876
877 new_chunk_ptr = chunk
878 .next
879 .unwrap();
881
882 chunk.unlink(self.updates.as_mut());
884
885 chunk_ptr = chunk.as_ptr();
887
888 if chunk_was_first {
890 *self.links.first_chunk_mut_ptr() = new_chunk_ptr;
891 }
892
893 if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
896 self.links.last = Some(last_chunk_ptr);
897 }
898
899 }
901
902 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
907
908 Ok(
909 unsafe { new_chunk_ptr.as_ref() },
913 )
914 }
915
916 pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
918 where
919 P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
920 {
921 self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
922 }
923
924 pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
926 where
927 P: FnMut(&'a Item) -> bool,
928 {
929 self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
930 }
931
932 pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
936 IterBackward::new(self.links.latest_chunk())
937 }
938
939 pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
943 Iter::new(self.links.first_chunk())
944 }
945
946 pub fn rchunks_from(
951 &self,
952 identifier: ChunkIdentifier,
953 ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
954 Ok(IterBackward::new(
955 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
956 ))
957 }
958
959 pub fn chunks_from(
964 &self,
965 identifier: ChunkIdentifier,
966 ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
967 Ok(Iter::new(
968 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
969 ))
970 }
971
972 pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
976 self.ritems_from(self.links.latest_chunk().last_position())
977 .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
978 }
979
980 pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
984 let first_chunk = self.links.first_chunk();
985
986 self.items_from(first_chunk.first_position())
987 .expect("`items` cannot fail because at least one empty chunk must exist")
988 }
989
990 pub fn ritems_from(
994 &self,
995 position: Position,
996 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
997 Ok(self
998 .rchunks_from(position.chunk_identifier())?
999 .filter_map(|chunk| match &chunk.content {
1000 ChunkContent::Gap(..) => None,
1001 ChunkContent::Items(items) => {
1002 let identifier = chunk.identifier();
1003
1004 Some(
1005 items.iter().enumerate().rev().map(move |(item_index, item)| {
1006 (Position(identifier, item_index), item)
1007 }),
1008 )
1009 }
1010 })
1011 .flatten()
1012 .skip_while({
1013 let expected_index = position.index();
1014
1015 move |(Position(chunk_identifier, item_index), _item)| {
1016 *chunk_identifier == position.chunk_identifier()
1017 && *item_index != expected_index
1018 }
1019 }))
1020 }
1021
1022 pub fn items_from(
1026 &self,
1027 position: Position,
1028 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1029 Ok(self
1030 .chunks_from(position.chunk_identifier())?
1031 .filter_map(|chunk| match &chunk.content {
1032 ChunkContent::Gap(..) => None,
1033 ChunkContent::Items(items) => {
1034 let identifier = chunk.identifier();
1035
1036 Some(
1037 items.iter().enumerate().map(move |(item_index, item)| {
1038 (Position(identifier, item_index), item)
1039 }),
1040 )
1041 }
1042 })
1043 .flatten()
1044 .skip(position.index()))
1045 }
1046
1047 pub fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
1049 self.links.first_chunk()
1050 }
1051
1052 #[must_use]
1064 pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
1065 self.updates.as_mut()
1066 }
1067
1068 pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
1075 let (updates, token) = self
1076 .updates
1077 .as_mut()
1078 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1079 let chunk_iterator = self.chunks();
1080
1081 Some(AsVector::new(updates, token, chunk_iterator))
1082 }
1083
1084 pub fn order_tracker(
1092 &mut self,
1093 all_chunks: Option<Vec<ChunkMetadata>>,
1094 ) -> Option<OrderTracker<Item, Gap>>
1095 where
1096 Item: Clone,
1097 {
1098 let (updates, token) = self
1099 .updates
1100 .as_mut()
1101 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1102
1103 Some(OrderTracker::new(
1104 updates,
1105 token,
1106 all_chunks.unwrap_or_else(|| {
1107 self.chunks()
1109 .map(|chunk| ChunkMetadata {
1110 identifier: chunk.identifier(),
1111 num_items: chunk.num_items(),
1112 previous: chunk.previous().map(|prev| prev.identifier()),
1113 next: chunk.next().map(|next| next.identifier()),
1114 })
1115 .collect()
1116 }),
1117 ))
1118 }
1119
1120 pub fn num_items(&self) -> usize {
1122 self.items().count()
1123 }
1124}
1125
1126impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
1127 fn drop(&mut self) {
1128 self.links.clear();
1135 }
1136}
1137
1138unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1142
1143unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1147
1148#[derive(Debug)]
1158pub struct ChunkIdentifierGenerator {
1159 next: AtomicU64,
1160}
1161
1162impl ChunkIdentifierGenerator {
1163 const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1165
1166 pub fn new_from_scratch() -> Self {
1169 Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1170 }
1171
1172 pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1175 Self { next: AtomicU64::new(last_chunk_identifier.0) }
1176 }
1177
1178 fn next(&self) -> ChunkIdentifier {
1183 let previous = self.next.fetch_add(1, atomic::Ordering::Relaxed);
1184
1185 if previous == u64::MAX {
1188 panic!(
1189 "No more chunk identifiers available. Congrats, you did it. \
1190 2^64 identifiers have been consumed."
1191 )
1192 }
1193
1194 ChunkIdentifier(previous + 1)
1195 }
1196
1197 #[doc(hidden)]
1201 pub fn current(&self) -> ChunkIdentifier {
1202 ChunkIdentifier(self.next.load(atomic::Ordering::Relaxed))
1203 }
1204}
1205
1206#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1212#[repr(transparent)]
1213pub struct ChunkIdentifier(u64);
1214
1215impl ChunkIdentifier {
1216 pub fn new(identifier: u64) -> Self {
1218 Self(identifier)
1219 }
1220
1221 pub fn index(&self) -> u64 {
1223 self.0
1224 }
1225}
1226
1227impl PartialEq<u64> for ChunkIdentifier {
1228 fn eq(&self, other: &u64) -> bool {
1229 self.0 == *other
1230 }
1231}
1232
1233#[derive(Copy, Clone, Debug, PartialEq)]
1237pub struct Position(ChunkIdentifier, usize);
1238
1239impl Position {
1240 pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1242 Self(chunk_identifier, index)
1243 }
1244
1245 pub fn chunk_identifier(&self) -> ChunkIdentifier {
1247 self.0
1248 }
1249
1250 pub fn index(&self) -> usize {
1252 self.1
1253 }
1254
1255 pub fn decrement_index(&mut self) {
1261 self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1262 }
1263
1264 pub fn increment_index(&mut self) {
1271 self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1272 }
1273}
1274
1275#[derive(Debug)]
1278pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1279 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1280}
1281
1282impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1283 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1285 Self { chunk: Some(from_chunk) }
1286 }
1287}
1288
1289impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1290 type Item = &'a Chunk<CAP, Item, Gap>;
1291
1292 fn next(&mut self) -> Option<Self::Item> {
1293 self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1294 }
1295}
1296
1297#[derive(Debug)]
1300pub struct Iter<'a, const CAP: usize, Item, Gap> {
1301 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1302}
1303
1304impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1305 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1307 Self { chunk: Some(from_chunk) }
1308 }
1309}
1310
1311impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1312 type Item = &'a Chunk<CAP, Item, Gap>;
1313
1314 fn next(&mut self) -> Option<Self::Item> {
1315 self.chunk.inspect(|chunk| self.chunk = chunk.next())
1316 }
1317}
1318
1319#[derive(Clone, Debug)]
1321pub enum ChunkContent<Item, Gap> {
1322 Gap(Gap),
1325
1326 Items(Vec<Item>),
1328}
1329
1330pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1332 previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1334
1335 lazy_previous: Option<ChunkIdentifier>,
1340
1341 next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1343
1344 identifier: ChunkIdentifier,
1346
1347 content: ChunkContent<Item, Gap>,
1349}
1350
1351impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1352 fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1354 Self::new(identifier, ChunkContent::Gap(content))
1355 }
1356
1357 fn new_items(identifier: ChunkIdentifier) -> Self {
1359 Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1360 }
1361
1362 fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1363 Self { previous: None, lazy_previous: None, next: None, identifier, content }
1364 }
1365
1366 fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1368 let chunk = Self::new(identifier, content);
1369 let chunk_box = Box::new(chunk);
1370
1371 NonNull::from(Box::leak(chunk_box))
1372 }
1373
1374 fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1376 let chunk = Self::new_gap(identifier, content);
1377 let chunk_box = Box::new(chunk);
1378
1379 NonNull::from(Box::leak(chunk_box))
1380 }
1381
1382 fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1384 let chunk = Self::new_items(identifier);
1385 let chunk_box = Box::new(chunk);
1386
1387 NonNull::from(Box::leak(chunk_box))
1388 }
1389
1390 pub fn as_ptr(&self) -> NonNull<Self> {
1392 NonNull::from(self)
1393 }
1394
1395 pub fn is_gap(&self) -> bool {
1397 matches!(self.content, ChunkContent::Gap(..))
1398 }
1399
1400 pub fn is_items(&self) -> bool {
1402 !self.is_gap()
1403 }
1404
1405 pub fn is_definitive_head(&self) -> bool {
1408 self.previous.is_none() && self.lazy_previous.is_none()
1409 }
1410
1411 fn is_first_chunk(&self) -> bool {
1413 self.previous.is_none()
1414 }
1415
1416 fn is_last_chunk(&self) -> bool {
1418 self.next.is_none()
1419 }
1420
1421 #[doc(hidden)]
1425 pub fn lazy_previous(&self) -> Option<ChunkIdentifier> {
1426 self.lazy_previous
1427 }
1428
1429 pub fn identifier(&self) -> ChunkIdentifier {
1431 self.identifier
1432 }
1433
1434 pub fn content(&self) -> &ChunkContent<Item, Gap> {
1436 &self.content
1437 }
1438
1439 pub fn first_position(&self) -> Position {
1443 Position(self.identifier(), 0)
1444 }
1445
1446 pub fn last_position(&self) -> Position {
1450 let identifier = self.identifier();
1451
1452 match &self.content {
1453 ChunkContent::Gap(..) => Position(identifier, 0),
1454 ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1455 }
1456 }
1457
1458 pub fn num_items(&self) -> usize {
1462 match &self.content {
1463 ChunkContent::Gap(..) => 0,
1464 ChunkContent::Items(items) => items.len(),
1465 }
1466 }
1467
1468 fn push_items<I>(
1480 &mut self,
1481 mut new_items: I,
1482 chunk_identifier_generator: &ChunkIdentifierGenerator,
1483 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1484 ) -> &mut Self
1485 where
1486 I: Iterator<Item = Item> + ExactSizeIterator,
1487 Item: Clone,
1488 Gap: Clone,
1489 {
1490 if new_items.len() == 0 {
1492 return self;
1493 }
1494
1495 let identifier = self.identifier();
1496 let prev_num_items = self.num_items();
1497
1498 match &mut self.content {
1499 ChunkContent::Gap(..) => {
1502 self
1503 .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1505 .push_items(new_items, chunk_identifier_generator, updates)
1508 }
1509
1510 ChunkContent::Items(items) => {
1511 let free_space = CAPACITY.saturating_sub(prev_num_items);
1513
1514 if new_items.len() <= free_space {
1516 let start = items.len();
1517 items.extend(new_items);
1518
1519 if let Some(updates) = updates.as_mut() {
1520 updates.push(Update::PushItems {
1521 at: Position(identifier, start),
1522 items: items[start..].to_vec(),
1523 });
1524 }
1525
1526 self
1528 } else {
1529 if free_space > 0 {
1530 let start = items.len();
1532 items.extend(new_items.by_ref().take(free_space));
1533
1534 if let Some(updates) = updates.as_mut() {
1535 updates.push(Update::PushItems {
1536 at: Position(identifier, start),
1537 items: items[start..].to_vec(),
1538 });
1539 }
1540 }
1541
1542 self
1543 .insert_next(
1545 Self::new_items_leaked(chunk_identifier_generator.next()),
1546 updates,
1547 )
1548 .push_items(new_items, chunk_identifier_generator, updates)
1551 }
1552 }
1553 }
1554 }
1555
1556 fn insert_next(
1561 &mut self,
1562 mut new_chunk_ptr: NonNull<Self>,
1563 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1564 ) -> &mut Self
1565 where
1566 Gap: Clone,
1567 {
1568 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1569
1570 if let Some(next_chunk) = self.next_mut() {
1572 next_chunk.previous = Some(new_chunk_ptr);
1574
1575 new_chunk.next = self.next;
1577 }
1578
1579 self.next = Some(new_chunk_ptr);
1581 new_chunk.previous = Some(self.as_ptr());
1583
1584 if let Some(updates) = updates.as_mut() {
1585 let previous = new_chunk.previous().map(Chunk::identifier);
1586 let new = new_chunk.identifier();
1587 let next = new_chunk.next().map(Chunk::identifier);
1588
1589 match new_chunk.content() {
1590 ChunkContent::Gap(gap) => {
1591 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1592 }
1593
1594 ChunkContent::Items(..) => {
1595 updates.push(Update::NewItemsChunk { previous, new, next })
1596 }
1597 }
1598 }
1599
1600 new_chunk
1601 }
1602
1603 fn insert_before(
1608 &mut self,
1609 mut new_chunk_ptr: NonNull<Self>,
1610 updates: Option<&mut 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(previous_chunk) = self.previous_mut() {
1619 previous_chunk.next = Some(new_chunk_ptr);
1621
1622 new_chunk.previous = self.previous;
1624 }
1625 else {
1628 new_chunk.lazy_previous = self.lazy_previous.take();
1629 }
1630
1631 self.previous = Some(new_chunk_ptr);
1633 new_chunk.next = Some(self.as_ptr());
1635
1636 if let Some(updates) = updates {
1637 let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1638 let new = new_chunk.identifier();
1639 let next = new_chunk.next().map(Chunk::identifier);
1640
1641 match new_chunk.content() {
1642 ChunkContent::Gap(gap) => {
1643 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1644 }
1645
1646 ChunkContent::Items(..) => {
1647 updates.push(Update::NewItemsChunk { previous, new, next })
1648 }
1649 }
1650 }
1651
1652 new_chunk
1653 }
1654
1655 fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1660 let previous_ptr = self.previous;
1661 let next_ptr = self.next;
1662 let lazy_previous = self.lazy_previous.take();
1666
1667 if let Some(previous) = self.previous_mut() {
1668 previous.next = next_ptr;
1669 }
1670
1671 if let Some(next) = self.next_mut() {
1672 next.previous = previous_ptr;
1673 next.lazy_previous = lazy_previous;
1674 }
1675
1676 if let Some(updates) = updates {
1677 updates.push(Update::RemoveChunk(self.identifier()));
1678 }
1679 }
1680
1681 fn previous(&self) -> Option<&Self> {
1683 self.previous.map(|non_null| unsafe { non_null.as_ref() })
1684 }
1685
1686 fn previous_mut(&mut self) -> Option<&mut Self> {
1688 self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1689 }
1690
1691 fn next(&self) -> Option<&Self> {
1693 self.next.map(|non_null| unsafe { non_null.as_ref() })
1694 }
1695
1696 fn next_mut(&mut self) -> Option<&mut Self> {
1698 self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1699 }
1700}
1701
1702impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<CAP, Item, Gap>
1703where
1704 Item: fmt::Debug,
1705 Gap: fmt::Debug,
1706{
1707 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1708 formatter
1709 .debug_struct("LinkedChunk")
1710 .field("first (deref)", self.links.first_chunk())
1711 .field("last", &self.links.last)
1712 .finish_non_exhaustive()
1713 }
1714}
1715
1716impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1717where
1718 Item: fmt::Debug,
1719 Gap: fmt::Debug,
1720{
1721 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1722 formatter
1723 .debug_struct("Chunk")
1724 .field("identifier", &self.identifier)
1725 .field("content", &self.content)
1726 .field("previous", &self.previous)
1727 .field("ptr", &std::ptr::from_ref(self))
1728 .field("next", &self.next)
1729 .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1730 .finish()
1731 }
1732}
1733
1734#[derive(Clone, Debug)]
1740pub struct RawChunk<Item, Gap> {
1741 pub content: ChunkContent<Item, Gap>,
1743
1744 pub previous: Option<ChunkIdentifier>,
1746
1747 pub identifier: ChunkIdentifier,
1749
1750 pub next: Option<ChunkIdentifier>,
1752}
1753
1754#[derive(Clone, Debug)]
1757pub struct ChunkMetadata {
1758 pub num_items: usize,
1762
1763 pub previous: Option<ChunkIdentifier>,
1765
1766 pub identifier: ChunkIdentifier,
1768
1769 pub next: Option<ChunkIdentifier>,
1771}
1772
1773#[cfg(test)]
1774mod tests {
1775 use std::{
1776 ops::Not,
1777 sync::{Arc, atomic::Ordering},
1778 };
1779
1780 use assert_matches::assert_matches;
1781
1782 use super::{
1783 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Error, LinkedChunk,
1784 Position, Update::*,
1785 };
1786
1787 #[test]
1788 fn test_chunk_identifier_generator() {
1789 let generator = ChunkIdentifierGenerator::new_from_scratch();
1790
1791 assert_eq!(generator.next(), ChunkIdentifier(1));
1792 assert_eq!(generator.next(), ChunkIdentifier(2));
1793 assert_eq!(generator.next(), ChunkIdentifier(3));
1794 assert_eq!(generator.next(), ChunkIdentifier(4));
1795
1796 let generator =
1797 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1798
1799 assert_eq!(generator.next(), ChunkIdentifier(43));
1800 assert_eq!(generator.next(), ChunkIdentifier(44));
1801 assert_eq!(generator.next(), ChunkIdentifier(45));
1802 assert_eq!(generator.next(), ChunkIdentifier(46));
1803 }
1804
1805 #[test]
1806 fn test_empty() {
1807 let items = LinkedChunk::<3, char, ()>::new();
1808
1809 assert_eq!(items.num_items(), 0);
1810
1811 }
1814
1815 #[test]
1816 fn test_updates() {
1817 assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1818 assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1819 }
1820
1821 #[test]
1822 fn test_new_with_initial_update() {
1823 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1824
1825 assert!(linked_chunk.updates().unwrap().take().is_empty());
1827
1828 let _ = linked_chunk.first_chunk();
1830
1831 assert_eq!(
1832 linked_chunk.updates().unwrap().take(),
1833 &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1834 );
1835 }
1836
1837 #[test]
1838 fn test_push_items() {
1839 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1840
1841 linked_chunk.push_items_back(['a']);
1842
1843 assert_items_eq!(linked_chunk, ['a']);
1844 assert_eq!(
1845 linked_chunk.updates().unwrap().take(),
1846 &[
1847 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
1848 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }
1849 ]
1850 );
1851
1852 linked_chunk.push_items_back(['b', 'c']);
1853 assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1854 assert_eq!(
1855 linked_chunk.updates().unwrap().take(),
1856 &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1857 );
1858
1859 linked_chunk.push_items_back(['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(0)),
1866 new: ChunkIdentifier(1),
1867 next: None
1868 },
1869 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1870 ]
1871 );
1872
1873 linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1874 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1875 assert_eq!(
1876 linked_chunk.updates().unwrap().take(),
1877 &[
1878 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1879 NewItemsChunk {
1880 previous: Some(ChunkIdentifier(1)),
1881 new: ChunkIdentifier(2),
1882 next: None,
1883 },
1884 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1885 NewItemsChunk {
1886 previous: Some(ChunkIdentifier(2)),
1887 new: ChunkIdentifier(3),
1888 next: None,
1889 },
1890 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1891 ]
1892 );
1893
1894 assert_eq!(linked_chunk.num_items(), 10);
1895 }
1896
1897 #[test]
1898 fn test_push_gap() {
1899 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1900
1901 linked_chunk.push_items_back(['a']);
1902 assert_items_eq!(linked_chunk, ['a']);
1903 assert_eq!(
1904 linked_chunk.updates().unwrap().take(),
1905 &[
1906 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
1907 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }
1908 ]
1909 );
1910
1911 linked_chunk.push_gap_back(());
1912 assert_items_eq!(linked_chunk, ['a'] [-]);
1913 assert_eq!(
1914 linked_chunk.updates().unwrap().take(),
1915 &[NewGapChunk {
1916 previous: Some(ChunkIdentifier(0)),
1917 new: ChunkIdentifier(1),
1918 next: None,
1919 gap: (),
1920 }]
1921 );
1922
1923 linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1924 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1925 assert_eq!(
1926 linked_chunk.updates().unwrap().take(),
1927 &[
1928 NewItemsChunk {
1929 previous: Some(ChunkIdentifier(1)),
1930 new: ChunkIdentifier(2),
1931 next: None,
1932 },
1933 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
1934 NewItemsChunk {
1935 previous: Some(ChunkIdentifier(2)),
1936 new: ChunkIdentifier(3),
1937 next: None,
1938 },
1939 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
1940 ]
1941 );
1942
1943 linked_chunk.push_gap_back(());
1944 linked_chunk.push_gap_back(()); assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
1946 assert_eq!(
1947 linked_chunk.updates().unwrap().take(),
1948 &[
1949 NewGapChunk {
1950 previous: Some(ChunkIdentifier(3)),
1951 new: ChunkIdentifier(4),
1952 next: None,
1953 gap: (),
1954 },
1955 NewGapChunk {
1956 previous: Some(ChunkIdentifier(4)),
1957 new: ChunkIdentifier(5),
1958 next: None,
1959 gap: (),
1960 }
1961 ]
1962 );
1963
1964 linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
1965 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
1966 assert_eq!(
1967 linked_chunk.updates().unwrap().take(),
1968 &[
1969 NewItemsChunk {
1970 previous: Some(ChunkIdentifier(5)),
1971 new: ChunkIdentifier(6),
1972 next: None,
1973 },
1974 PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
1975 NewItemsChunk {
1976 previous: Some(ChunkIdentifier(6)),
1977 new: ChunkIdentifier(7),
1978 next: None,
1979 },
1980 PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
1981 ]
1982 );
1983
1984 assert_eq!(linked_chunk.num_items(), 9);
1985 }
1986
1987 #[test]
1988 fn test_identifiers_and_positions() {
1989 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
1990 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
1991 linked_chunk.push_gap_back(());
1992 linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
1993 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
1994
1995 assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
1996 assert_eq!(
1997 linked_chunk.item_position(|item| *item == 'e'),
1998 Some(Position(ChunkIdentifier(1), 1))
1999 );
2000 }
2001
2002 #[test]
2003 fn test_rchunks() {
2004 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2005 linked_chunk.push_items_back(['a', 'b']);
2006 linked_chunk.push_gap_back(());
2007 linked_chunk.push_items_back(['c', 'd', 'e']);
2008
2009 let mut iterator = linked_chunk.rchunks();
2010
2011 assert_matches!(
2012 iterator.next(),
2013 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2014 assert_eq!(items, &['e']);
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
2036 #[test]
2037 fn test_chunks() {
2038 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2039 linked_chunk.push_items_back(['a', 'b']);
2040 linked_chunk.push_gap_back(());
2041 linked_chunk.push_items_back(['c', 'd', 'e']);
2042
2043 let mut iterator = linked_chunk.chunks();
2044
2045 assert_matches!(
2046 iterator.next(),
2047 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2048 assert_eq!(items, &['a', 'b']);
2049 }
2050 );
2051 assert_matches!(
2052 iterator.next(),
2053 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2054 );
2055 assert_matches!(
2056 iterator.next(),
2057 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2058 assert_eq!(items, &['c', 'd']);
2059 }
2060 );
2061 assert_matches!(
2062 iterator.next(),
2063 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2064 assert_eq!(items, &['e']);
2065 }
2066 );
2067 assert_matches!(iterator.next(), None);
2068 }
2069
2070 #[test]
2071 fn test_rchunks_from() -> Result<(), Error> {
2072 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2073 linked_chunk.push_items_back(['a', 'b']);
2074 linked_chunk.push_gap_back(());
2075 linked_chunk.push_items_back(['c', 'd', 'e']);
2076
2077 let mut iterator = linked_chunk.rchunks_from(
2078 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2079 )?;
2080
2081 assert_matches!(
2082 iterator.next(),
2083 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2084 assert_eq!(items, &['c', 'd']);
2085 }
2086 );
2087 assert_matches!(
2088 iterator.next(),
2089 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2090 );
2091 assert_matches!(
2092 iterator.next(),
2093 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2094 assert_eq!(items, &['a', 'b']);
2095 }
2096 );
2097 assert_matches!(iterator.next(), None);
2098
2099 Ok(())
2100 }
2101
2102 #[test]
2103 fn test_chunks_from() -> Result<(), Error> {
2104 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2105 linked_chunk.push_items_back(['a', 'b']);
2106 linked_chunk.push_gap_back(());
2107 linked_chunk.push_items_back(['c', 'd', 'e']);
2108
2109 let mut iterator = linked_chunk.chunks_from(
2110 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2111 )?;
2112
2113 assert_matches!(
2114 iterator.next(),
2115 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2116 assert_eq!(items, &['c', 'd']);
2117 }
2118 );
2119 assert_matches!(
2120 iterator.next(),
2121 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2122 assert_eq!(items, &['e']);
2123 }
2124 );
2125 assert_matches!(iterator.next(), None);
2126
2127 Ok(())
2128 }
2129
2130 #[test]
2131 fn test_ritems() {
2132 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2133 linked_chunk.push_items_back(['a', 'b']);
2134 linked_chunk.push_gap_back(());
2135 linked_chunk.push_items_back(['c', 'd', 'e']);
2136
2137 let mut iterator = linked_chunk.ritems();
2138
2139 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2140 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2141 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2142 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2143 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2144 assert_matches!(iterator.next(), None);
2145 }
2146
2147 #[test]
2148 fn test_ritems_with_final_gap() -> Result<(), Error> {
2149 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2150 linked_chunk.push_items_back(['a', 'b']);
2151 linked_chunk.push_gap_back(());
2152 linked_chunk.push_items_back(['c', 'd', 'e']);
2153 linked_chunk.push_gap_back(());
2154
2155 let mut iterator = linked_chunk.ritems();
2156
2157 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
2158 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2159 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2160 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2161 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2162 assert_matches!(iterator.next(), None);
2163
2164 Ok(())
2165 }
2166
2167 #[test]
2168 fn test_ritems_empty() {
2169 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2170 let mut iterator = linked_chunk.ritems();
2171
2172 assert_matches!(iterator.next(), None);
2173 }
2174
2175 #[test]
2176 fn test_items() {
2177 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2178 linked_chunk.push_items_back(['a', 'b']);
2179 linked_chunk.push_gap_back(());
2180 linked_chunk.push_items_back(['c', 'd', 'e']);
2181
2182 let mut iterator = linked_chunk.items();
2183
2184 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2185 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2186 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2187 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2188 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2189 assert_matches!(iterator.next(), None);
2190 }
2191
2192 #[test]
2193 fn test_items_empty() {
2194 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2195 let mut iterator = linked_chunk.items();
2196
2197 assert_matches!(iterator.next(), None);
2198 }
2199
2200 #[test]
2201 fn test_ritems_from() -> Result<(), Error> {
2202 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2203 linked_chunk.push_items_back(['a', 'b']);
2204 linked_chunk.push_gap_back(());
2205 linked_chunk.push_items_back(['c', 'd', 'e']);
2206
2207 let mut iterator =
2208 linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2209
2210 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2211 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2212 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2213 assert_matches!(iterator.next(), None);
2214
2215 Ok(())
2216 }
2217
2218 #[test]
2219 fn test_items_from() -> Result<(), Error> {
2220 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2221 linked_chunk.push_items_back(['a', 'b']);
2222 linked_chunk.push_gap_back(());
2223 linked_chunk.push_items_back(['c', 'd', 'e']);
2224
2225 let mut iterator =
2226 linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2227
2228 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2229 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2230 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2231 assert_matches!(iterator.next(), None);
2232
2233 Ok(())
2234 }
2235
2236 #[test]
2237 fn test_insert_items_at() -> Result<(), Error> {
2238 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2239
2240 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2241 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2242 assert_eq!(
2243 linked_chunk.updates().unwrap().take(),
2244 &[
2245 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2246 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2247 NewItemsChunk {
2248 previous: Some(ChunkIdentifier(0)),
2249 new: ChunkIdentifier(1),
2250 next: None,
2251 },
2252 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2253 ]
2254 );
2255
2256 {
2258 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2259
2260 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2263
2264 assert_items_eq!(
2265 linked_chunk,
2266 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2267 );
2268 assert_eq!(linked_chunk.num_items(), 10);
2269 assert_eq!(
2270 linked_chunk.updates().unwrap().take(),
2271 &[
2272 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2273 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2274 NewItemsChunk {
2275 previous: Some(ChunkIdentifier(1)),
2276 new: ChunkIdentifier(2),
2277 next: None,
2278 },
2279 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2280 StartReattachItems,
2281 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2282 NewItemsChunk {
2283 previous: Some(ChunkIdentifier(2)),
2284 new: ChunkIdentifier(3),
2285 next: None,
2286 },
2287 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2288 EndReattachItems,
2289 ]
2290 );
2291 }
2292
2293 {
2295 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2296 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2297
2298 assert_items_eq!(
2299 linked_chunk,
2300 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2301 );
2302 assert_eq!(linked_chunk.num_items(), 14);
2303 assert_eq!(
2304 linked_chunk.updates().unwrap().take(),
2305 &[
2306 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2307 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2308 NewItemsChunk {
2309 previous: Some(ChunkIdentifier(0)),
2310 new: ChunkIdentifier(4),
2311 next: Some(ChunkIdentifier(1)),
2312 },
2313 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2314 StartReattachItems,
2315 PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2316 NewItemsChunk {
2317 previous: Some(ChunkIdentifier(4)),
2318 new: ChunkIdentifier(5),
2319 next: Some(ChunkIdentifier(1)),
2320 },
2321 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2322 EndReattachItems,
2323 ]
2324 );
2325 }
2326
2327 {
2329 let pos_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2330 linked_chunk.insert_items_at(pos_c, ['r', 's'])?;
2331
2332 assert_items_eq!(
2333 linked_chunk,
2334 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2335 );
2336 assert_eq!(linked_chunk.num_items(), 16);
2337 assert_eq!(
2338 linked_chunk.updates().unwrap().take(),
2339 &[
2340 DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2341 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2342 StartReattachItems,
2343 PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2344 EndReattachItems,
2345 ]
2346 );
2347 }
2348
2349 {
2351 let pos_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2352 let pos_f = Position(pos_f.chunk_identifier(), pos_f.index() + 1);
2353
2354 linked_chunk.insert_items_at(pos_f, ['p', 'q'])?;
2355 assert_items_eq!(
2356 linked_chunk,
2357 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2358 );
2359 assert_eq!(
2360 linked_chunk.updates().unwrap().take(),
2361 &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2362 );
2363 assert_eq!(linked_chunk.num_items(), 18);
2364 }
2365
2366 {
2368 assert_matches!(
2369 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2370 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2371 );
2372 assert!(linked_chunk.updates().unwrap().take().is_empty());
2373 }
2374
2375 {
2377 assert_matches!(
2378 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2379 Err(Error::InvalidItemIndex { index: 128 })
2380 );
2381 assert!(linked_chunk.updates().unwrap().take().is_empty());
2382 }
2383
2384 {
2386 linked_chunk.push_gap_back(());
2388 assert_items_eq!(
2389 linked_chunk,
2390 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2391 );
2392 assert_eq!(
2393 linked_chunk.updates().unwrap().take(),
2394 &[NewGapChunk {
2395 previous: Some(ChunkIdentifier(3)),
2396 new: ChunkIdentifier(6),
2397 next: None,
2398 gap: ()
2399 }]
2400 );
2401
2402 assert_matches!(
2403 linked_chunk.insert_items_at(Position(ChunkIdentifier(6), 0), ['u', 'v'],),
2404 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2405 );
2406 }
2407
2408 assert_eq!(linked_chunk.num_items(), 18);
2409
2410 Ok(())
2411 }
2412
2413 #[test]
2414 fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2415 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2416
2417 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2418 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2419 assert_eq!(
2420 linked_chunk.updates().unwrap().take(),
2421 &[
2422 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2423 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2424 NewItemsChunk {
2425 previous: Some(ChunkIdentifier(0)),
2426 new: ChunkIdentifier(1),
2427 next: None,
2428 },
2429 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2430 ]
2431 );
2432
2433 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2435
2436 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2439
2440 assert_items_eq!(
2441 linked_chunk,
2442 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2443 );
2444 assert_eq!(linked_chunk.num_items(), 10);
2445 assert_eq!(
2446 linked_chunk.updates().unwrap().take(),
2447 &[
2448 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2449 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2450 NewItemsChunk {
2451 previous: Some(ChunkIdentifier(1)),
2452 new: ChunkIdentifier(2),
2453 next: None,
2454 },
2455 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2456 StartReattachItems,
2457 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2458 NewItemsChunk {
2459 previous: Some(ChunkIdentifier(2)),
2460 new: ChunkIdentifier(3),
2461 next: None,
2462 },
2463 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2464 EndReattachItems,
2465 ]
2466 );
2467
2468 Ok(())
2469 }
2470
2471 #[test]
2472 fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2473 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2474
2475 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2476 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2477 assert_eq!(
2478 linked_chunk.updates().unwrap().take(),
2479 &[
2480 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2481 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2482 NewItemsChunk {
2483 previous: Some(ChunkIdentifier(0)),
2484 new: ChunkIdentifier(1),
2485 next: None,
2486 },
2487 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2488 ]
2489 );
2490
2491 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2493 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2494
2495 assert_items_eq!(
2496 linked_chunk,
2497 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', '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(0), 0) },
2504 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2505 NewItemsChunk {
2506 previous: Some(ChunkIdentifier(0)),
2507 new: ChunkIdentifier(2),
2508 next: Some(ChunkIdentifier(1)),
2509 },
2510 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2511 StartReattachItems,
2512 PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2513 NewItemsChunk {
2514 previous: Some(ChunkIdentifier(2)),
2515 new: ChunkIdentifier(3),
2516 next: Some(ChunkIdentifier(1)),
2517 },
2518 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2519 EndReattachItems,
2520 ]
2521 );
2522
2523 Ok(())
2524 }
2525
2526 #[test]
2527 fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2528 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2529
2530 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2531 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2532 assert_eq!(
2533 linked_chunk.updates().unwrap().take(),
2534 &[
2535 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2536 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2537 NewItemsChunk {
2538 previous: Some(ChunkIdentifier(0)),
2539 new: ChunkIdentifier(1),
2540 next: None,
2541 },
2542 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2543 NewItemsChunk {
2544 previous: Some(ChunkIdentifier(1)),
2545 new: ChunkIdentifier(2),
2546 next: None,
2547 },
2548 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2549 ]
2550 );
2551
2552 let pos_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2553 linked_chunk.insert_items_at(pos_d, ['r', 's'])?;
2554
2555 assert_items_eq!(
2556 linked_chunk,
2557 ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2558 );
2559 assert_eq!(linked_chunk.num_items(), 10);
2560 assert_eq!(
2561 linked_chunk.updates().unwrap().take(),
2562 &[
2563 DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2564 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2565 StartReattachItems,
2566 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2567 NewItemsChunk {
2568 previous: Some(ChunkIdentifier(1)),
2569 new: ChunkIdentifier(3),
2570 next: Some(ChunkIdentifier(2)),
2571 },
2572 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2573 EndReattachItems,
2574 ]
2575 );
2576
2577 Ok(())
2578 }
2579
2580 #[test]
2581 fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2582 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2583
2584 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2585 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2586 assert_eq!(
2587 linked_chunk.updates().unwrap().take(),
2588 &[
2589 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2590 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2591 NewItemsChunk {
2592 previous: Some(ChunkIdentifier(0)),
2593 new: ChunkIdentifier(1),
2594 next: None,
2595 },
2596 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2597 ]
2598 );
2599
2600 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2602 let pos_after_e = Position(pos_e.chunk_identifier(), pos_e.index() + 1);
2603
2604 linked_chunk.insert_items_at(pos_after_e, ['p', 'q'])?;
2605 assert_items_eq!(
2606 linked_chunk,
2607 ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2608 );
2609 assert_eq!(
2610 linked_chunk.updates().unwrap().take(),
2611 &[
2612 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2613 NewItemsChunk {
2614 previous: Some(ChunkIdentifier(1)),
2615 new: ChunkIdentifier(2),
2616 next: None
2617 },
2618 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2619 ]
2620 );
2621 assert_eq!(linked_chunk.num_items(), 7);
2622
2623 Ok(())
2624 }
2625
2626 #[test]
2627 fn test_insert_items_at_errs() -> Result<(), Error> {
2628 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2629
2630 linked_chunk.push_items_back(['a', 'b', 'c']);
2631 linked_chunk.push_gap_back(());
2632 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2633 assert_eq!(
2634 linked_chunk.updates().unwrap().take(),
2635 &[
2636 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2637 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2638 NewGapChunk {
2639 previous: Some(ChunkIdentifier(0)),
2640 new: ChunkIdentifier(1),
2641 next: None,
2642 gap: (),
2643 },
2644 ]
2645 );
2646
2647 {
2649 assert_matches!(
2650 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2651 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2652 );
2653 assert!(linked_chunk.updates().unwrap().take().is_empty());
2654 }
2655
2656 {
2658 assert_matches!(
2659 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2660 Err(Error::InvalidItemIndex { index: 128 })
2661 );
2662 assert!(linked_chunk.updates().unwrap().take().is_empty());
2663 }
2664
2665 {
2667 assert_matches!(
2668 linked_chunk.insert_items_at(Position(ChunkIdentifier(1), 0), ['u', 'v'],),
2669 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2670 );
2671 }
2672
2673 Ok(())
2674 }
2675
2676 #[test]
2677 fn test_remove_item_at() -> Result<(), Error> {
2678 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2679
2680 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2681 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2682 assert_eq!(linked_chunk.num_items(), 11);
2683
2684 let _ = linked_chunk.updates().unwrap().take();
2686
2687 {
2690 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2691 let removed_item = linked_chunk.remove_item_at(position_of_f)?;
2692
2693 assert_eq!(removed_item, 'f');
2694 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2695 assert_eq!(linked_chunk.num_items(), 10);
2696
2697 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2698 let removed_item = linked_chunk.remove_item_at(position_of_e)?;
2699
2700 assert_eq!(removed_item, 'e');
2701 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2702 assert_eq!(linked_chunk.num_items(), 9);
2703
2704 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2705 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2706
2707 assert_eq!(removed_item, 'd');
2708 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2709 assert_eq!(linked_chunk.num_items(), 8);
2710
2711 assert_eq!(
2712 linked_chunk.updates().unwrap().take(),
2713 &[
2714 RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2715 RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2716 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2717 RemoveChunk(ChunkIdentifier(1)),
2718 ]
2719 );
2720 }
2721
2722 {
2725 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2726 let removed_item = linked_chunk.remove_item_at(first_position)?;
2727
2728 assert_eq!(removed_item, 'a');
2729 assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2730 assert_eq!(linked_chunk.num_items(), 7);
2731
2732 let removed_item = linked_chunk.remove_item_at(first_position)?;
2733
2734 assert_eq!(removed_item, 'b');
2735 assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2736 assert_eq!(linked_chunk.num_items(), 6);
2737
2738 let removed_item = linked_chunk.remove_item_at(first_position)?;
2739
2740 assert_eq!(removed_item, 'c');
2741 assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2742 assert_eq!(linked_chunk.num_items(), 5);
2743
2744 assert_eq!(
2745 linked_chunk.updates().unwrap().take(),
2746 &[
2747 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2748 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2749 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2750 ]
2751 );
2752 }
2753
2754 {
2757 let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2758 let removed_item = linked_chunk.remove_item_at(first_position)?;
2759
2760 assert_eq!(removed_item, 'g');
2761 assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2762 assert_eq!(linked_chunk.num_items(), 4);
2763
2764 let removed_item = linked_chunk.remove_item_at(first_position)?;
2765
2766 assert_eq!(removed_item, 'h');
2767 assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2768 assert_eq!(linked_chunk.num_items(), 3);
2769
2770 let removed_item = linked_chunk.remove_item_at(first_position)?;
2771
2772 assert_eq!(removed_item, 'i');
2773 assert_items_eq!(linked_chunk, [] ['j', 'k']);
2774 assert_eq!(linked_chunk.num_items(), 2);
2775
2776 assert_eq!(
2777 linked_chunk.updates().unwrap().take(),
2778 &[
2779 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2780 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2781 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2782 RemoveChunk(ChunkIdentifier(2)),
2783 ]
2784 );
2785 }
2786
2787 {
2790 let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2791 let removed_item = linked_chunk.remove_item_at(position_of_k)?;
2792
2793 assert_eq!(removed_item, 'k');
2794 #[rustfmt::skip]
2795 assert_items_eq!(linked_chunk, [] ['j']);
2796 assert_eq!(linked_chunk.num_items(), 1);
2797
2798 let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2799 let removed_item = linked_chunk.remove_item_at(position_of_j)?;
2800
2801 assert_eq!(removed_item, 'j');
2802 assert_items_eq!(linked_chunk, []);
2803 assert_eq!(linked_chunk.num_items(), 0);
2804
2805 assert_eq!(
2806 linked_chunk.updates().unwrap().take(),
2807 &[
2808 RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2809 RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2810 RemoveChunk(ChunkIdentifier(3)),
2811 ]
2812 );
2813 }
2814
2815 {
2817 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2818
2819 #[rustfmt::skip]
2820 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2821 assert_eq!(linked_chunk.num_items(), 4);
2822
2823 assert_matches!(
2825 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 3)),
2826 Err(Error::InvalidItemIndex { index: 3 })
2827 );
2828
2829 assert_matches!(
2831 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 42)),
2832 Err(Error::InvalidItemIndex { index: 42 })
2833 );
2834
2835 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2836 linked_chunk.insert_gap_at((), position_of_c)?;
2837
2838 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2839 assert_eq!(linked_chunk.num_items(), 4);
2840
2841 let _ = linked_chunk.updates().unwrap().take();
2843
2844 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2845 let removed_item = linked_chunk.remove_item_at(position_of_c)?;
2846
2847 assert_eq!(removed_item, 'c');
2848 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2849 assert_eq!(linked_chunk.num_items(), 3);
2850
2851 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2852 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2853
2854 assert_eq!(removed_item, 'd');
2855 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2856 assert_eq!(linked_chunk.num_items(), 2);
2857
2858 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2859 let removed_item = linked_chunk.remove_item_at(first_position)?;
2860
2861 assert_eq!(removed_item, 'a');
2862 assert_items_eq!(linked_chunk, ['b'] [-]);
2863 assert_eq!(linked_chunk.num_items(), 1);
2864
2865 let removed_item = linked_chunk.remove_item_at(first_position)?;
2866
2867 assert_eq!(removed_item, 'b');
2868 assert_items_eq!(linked_chunk, [] [-]);
2869 assert_eq!(linked_chunk.num_items(), 0);
2870
2871 assert_eq!(
2872 linked_chunk.updates().unwrap().take(),
2873 &[
2874 RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2875 RemoveChunk(ChunkIdentifier(6)),
2876 RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2877 RemoveChunk(ChunkIdentifier(4)),
2878 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2879 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2880 ]
2881 );
2882 }
2883
2884 Ok(())
2885 }
2886
2887 #[test]
2888 fn test_insert_gap_at() -> Result<(), Error> {
2889 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2890
2891 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2892 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2893 assert_eq!(
2894 linked_chunk.updates().unwrap().take(),
2895 &[
2896 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2897 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2898 NewItemsChunk {
2899 previous: Some(ChunkIdentifier(0)),
2900 new: ChunkIdentifier(1),
2901 next: None
2902 },
2903 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2904 ]
2905 );
2906
2907 {
2909 let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
2910 linked_chunk.insert_gap_at((), position_of_b)?;
2911
2912 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2913 assert_eq!(
2914 linked_chunk.updates().unwrap().take(),
2915 &[
2916 DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
2917 NewGapChunk {
2918 previous: Some(ChunkIdentifier(0)),
2919 new: ChunkIdentifier(2),
2920 next: Some(ChunkIdentifier(1)),
2921 gap: (),
2922 },
2923 StartReattachItems,
2924 NewItemsChunk {
2925 previous: Some(ChunkIdentifier(2)),
2926 new: ChunkIdentifier(3),
2927 next: Some(ChunkIdentifier(1)),
2928 },
2929 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
2930 EndReattachItems,
2931 ]
2932 );
2933 }
2934
2935 {
2938 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2939 linked_chunk.insert_gap_at((), position_of_a)?;
2940
2941 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2944 assert_eq!(
2945 linked_chunk.updates().unwrap().take(),
2946 &[NewGapChunk {
2947 previous: None,
2948 new: ChunkIdentifier(4),
2949 next: Some(ChunkIdentifier(0)),
2950 gap: (),
2951 },]
2952 );
2953 }
2954
2955 {
2958 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2959 linked_chunk.insert_gap_at((), position_of_d)?;
2960
2961 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
2965 assert_eq!(
2966 linked_chunk.updates().unwrap().take(),
2967 &[NewGapChunk {
2968 previous: Some(ChunkIdentifier(3)),
2969 new: ChunkIdentifier(5),
2970 next: Some(ChunkIdentifier(1)),
2971 gap: (),
2972 }]
2973 );
2974 }
2975
2976 {
2978 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
2980 let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
2981
2982 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
2983
2984 assert_eq!(
2985 linked_chunk.updates().unwrap().take(),
2986 &[
2987 NewItemsChunk {
2988 previous: Some(ChunkIdentifier(5)),
2989 new: ChunkIdentifier(6),
2990 next: Some(ChunkIdentifier(1)),
2991 },
2992 RemoveChunk(ChunkIdentifier(5)),
2993 ]
2994 );
2995
2996 linked_chunk.insert_gap_at((), position)?;
2997
2998 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
2999 assert_eq!(
3000 linked_chunk.updates().unwrap().take(),
3001 &[NewGapChunk {
3002 previous: Some(ChunkIdentifier(3)),
3003 new: ChunkIdentifier(7),
3004 next: Some(ChunkIdentifier(6)),
3005 gap: (),
3006 }]
3007 );
3008 }
3009
3010 {
3012 assert_matches!(
3013 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
3014 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
3015 );
3016 assert!(linked_chunk.updates().unwrap().take().is_empty());
3017 }
3018
3019 {
3021 assert_matches!(
3022 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
3023 Err(Error::InvalidItemIndex { index: 128 })
3024 );
3025 assert!(linked_chunk.updates().unwrap().take().is_empty());
3026 }
3027
3028 {
3030 let position_of_a_gap = Position(ChunkIdentifier(2), 0);
3033 assert_matches!(
3034 linked_chunk.insert_gap_at((), position_of_a_gap),
3035 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
3036 );
3037 assert!(linked_chunk.updates().unwrap().take().is_empty());
3038 }
3039
3040 assert_eq!(linked_chunk.num_items(), 6);
3041
3042 Ok(())
3043 }
3044
3045 #[test]
3046 fn test_replace_gap_at_middle() -> Result<(), Error> {
3047 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3048
3049 linked_chunk.push_items_back(['a', 'b']);
3050 linked_chunk.push_gap_back(());
3051 linked_chunk.push_items_back(['l', 'm']);
3052 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
3053 assert_eq!(
3054 linked_chunk.updates().unwrap().take(),
3055 &[
3056 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
3057 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3058 NewGapChunk {
3059 previous: Some(ChunkIdentifier(0)),
3060 new: ChunkIdentifier(1),
3061 next: None,
3062 gap: (),
3063 },
3064 NewItemsChunk {
3065 previous: Some(ChunkIdentifier(1)),
3066 new: ChunkIdentifier(2),
3067 next: None,
3068 },
3069 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
3070 ]
3071 );
3072
3073 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3075 assert_eq!(gap_identifier, ChunkIdentifier(1));
3076
3077 let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
3078 assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
3079 assert_items_eq!(
3080 linked_chunk,
3081 ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
3082 );
3083 assert_eq!(
3084 linked_chunk.updates().unwrap().take(),
3085 &[
3086 NewItemsChunk {
3087 previous: Some(ChunkIdentifier(1)),
3088 new: ChunkIdentifier(3),
3089 next: Some(ChunkIdentifier(2)),
3090 },
3091 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
3092 NewItemsChunk {
3093 previous: Some(ChunkIdentifier(3)),
3094 new: ChunkIdentifier(4),
3095 next: Some(ChunkIdentifier(2)),
3096 },
3097 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
3098 RemoveChunk(ChunkIdentifier(1)),
3099 ]
3100 );
3101
3102 assert_eq!(linked_chunk.num_items(), 9);
3103
3104 Ok(())
3105 }
3106
3107 #[test]
3108 fn test_replace_gap_at_end() -> Result<(), Error> {
3109 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3110
3111 linked_chunk.push_items_back(['a', 'b']);
3112 linked_chunk.push_gap_back(());
3113 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3114 assert_eq!(
3115 linked_chunk.updates().unwrap().take(),
3116 &[
3117 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
3118 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3119 NewGapChunk {
3120 previous: Some(ChunkIdentifier(0)),
3121 new: ChunkIdentifier(1),
3122 next: None,
3123 gap: (),
3124 },
3125 ]
3126 );
3127
3128 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3130 assert_eq!(gap_identifier, ChunkIdentifier(1));
3131
3132 let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3133 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3134 assert_items_eq!(
3135 linked_chunk,
3136 ['a', 'b'] ['w', 'x', 'y'] ['z']
3137 );
3138 assert_eq!(
3139 linked_chunk.updates().unwrap().take(),
3140 &[
3141 NewItemsChunk {
3142 previous: Some(ChunkIdentifier(1)),
3143 new: ChunkIdentifier(2),
3144 next: None,
3145 },
3146 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3147 NewItemsChunk {
3148 previous: Some(ChunkIdentifier(2)),
3149 new: ChunkIdentifier(3),
3150 next: None,
3151 },
3152 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3153 RemoveChunk(ChunkIdentifier(1)),
3154 ]
3155 );
3156
3157 assert_eq!(linked_chunk.num_items(), 6);
3158
3159 Ok(())
3160 }
3161
3162 #[test]
3163 fn test_replace_gap_at_beginning() -> Result<(), Error> {
3164 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3165
3166 linked_chunk.push_items_back(['a', 'b']);
3167 assert_items_eq!(linked_chunk, ['a', 'b']);
3168 assert_eq!(
3169 linked_chunk.updates().unwrap().take(),
3170 &[
3171 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
3172 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3173 ]
3174 );
3175
3176 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3178 linked_chunk.insert_gap_at((), position_of_a).unwrap();
3179 assert_items_eq!(
3180 linked_chunk,
3181 [-] ['a', 'b']
3182 );
3183 assert_eq!(
3184 linked_chunk.updates().unwrap().take(),
3185 &[NewGapChunk {
3186 previous: None,
3187 new: ChunkIdentifier(1),
3188 next: Some(ChunkIdentifier(0)),
3189 gap: (),
3190 }]
3191 );
3192
3193 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3194 assert_eq!(gap_identifier, ChunkIdentifier(1));
3195
3196 let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3197 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3198 assert_items_eq!(
3199 linked_chunk,
3200 ['x'] ['a', 'b']
3201 );
3202 assert_eq!(
3203 linked_chunk.updates().unwrap().take(),
3204 &[
3205 NewItemsChunk {
3206 previous: Some(ChunkIdentifier(1)),
3207 new: ChunkIdentifier(2),
3208 next: Some(ChunkIdentifier(0)),
3209 },
3210 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3211 RemoveChunk(ChunkIdentifier(1)),
3212 ]
3213 );
3214
3215 assert_eq!(linked_chunk.num_items(), 3);
3216
3217 Ok(())
3218 }
3219
3220 #[test]
3221 fn test_remove_empty_chunk_at() -> Result<(), Error> {
3222 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3223
3224 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3225 linked_chunk.push_items_back(['a', 'b']);
3226 linked_chunk.push_gap_back(());
3227 linked_chunk.push_items_back(['l', 'm']);
3228 linked_chunk.push_gap_back(());
3229 assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3230 assert_eq!(
3231 linked_chunk.updates().unwrap().take(),
3232 &[
3233 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
3234 NewGapChunk {
3235 previous: None,
3236 new: ChunkIdentifier(1),
3237 next: Some(ChunkIdentifier(0)),
3238 gap: (),
3239 },
3240 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3241 NewGapChunk {
3242 previous: Some(ChunkIdentifier(0)),
3243 new: ChunkIdentifier(2),
3244 next: None,
3245 gap: (),
3246 },
3247 NewItemsChunk {
3248 previous: Some(ChunkIdentifier(2)),
3249 new: ChunkIdentifier(3),
3250 next: None,
3251 },
3252 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3253 NewGapChunk {
3254 previous: Some(ChunkIdentifier(3)),
3255 new: ChunkIdentifier(4),
3256 next: None,
3257 gap: (),
3258 },
3259 ]
3260 );
3261
3262 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3264 assert_matches!(err, Error::RemovingNonEmptyItemsChunk { .. });
3265
3266 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(42)).unwrap_err();
3268 assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3269
3270 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(2)).unwrap();
3272 let next = maybe_next.unwrap();
3273 assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3275 assert_eq!(next.index(), 0);
3276 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3277 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3278
3279 let next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(4)).unwrap();
3281 assert!(next.is_none());
3283 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3284 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3285
3286 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(1)).unwrap();
3288 let next = maybe_next.unwrap();
3289 assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3290 assert_eq!(next.index(), 0);
3291 assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3292 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3293
3294 Ok(())
3295 }
3296
3297 #[test]
3298 fn test_remove_empty_last_chunk() {
3299 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3300
3301 assert!(linked_chunk.updates().unwrap().take().is_empty());
3302
3303 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3305 assert_matches!(err, Error::RemovingLastChunk);
3306 }
3307
3308 #[test]
3309 fn test_chunk_item_positions() {
3310 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3311 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3312 linked_chunk.push_gap_back(());
3313 linked_chunk.push_items_back(['f']);
3314
3315 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3316
3317 let mut iterator = linked_chunk.chunks();
3318
3319 {
3321 let chunk = iterator.next().unwrap();
3322 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3323 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3324 }
3325
3326 {
3328 let chunk = iterator.next().unwrap();
3329 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3330 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3331 }
3332
3333 {
3335 let chunk = iterator.next().unwrap();
3336 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3337 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3338 }
3339
3340 {
3342 let chunk = iterator.next().unwrap();
3343 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3344 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3345 }
3346 }
3347
3348 #[test]
3349 fn test_is_first_and_last_chunk() {
3350 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3351
3352 let mut chunks = linked_chunk.chunks().peekable();
3353 assert!(chunks.peek().unwrap().is_first_chunk());
3354 assert!(chunks.next().unwrap().is_last_chunk());
3355 assert!(chunks.next().is_none());
3356
3357 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3358
3359 let mut chunks = linked_chunk.chunks().peekable();
3360 assert!(chunks.next().unwrap().is_first_chunk());
3361 assert!(chunks.peek().unwrap().is_first_chunk().not());
3362 assert!(chunks.next().unwrap().is_last_chunk().not());
3363 assert!(chunks.next().unwrap().is_last_chunk());
3364 assert!(chunks.next().is_none());
3365 }
3366
3367 #[test]
3372 fn test_clear() {
3373 let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3374
3375 let item = Arc::new('a');
3376 let gap = Arc::new(());
3377
3378 linked_chunk.push_items_back([
3379 item.clone(),
3380 item.clone(),
3381 item.clone(),
3382 item.clone(),
3383 item.clone(),
3384 ]);
3385 linked_chunk.push_gap_back(gap.clone());
3386 linked_chunk.push_items_back([item.clone()]);
3387
3388 assert_eq!(Arc::strong_count(&item), 7);
3389 assert_eq!(Arc::strong_count(&gap), 2);
3390 assert_eq!(linked_chunk.chunks().filter(|chunk| chunk.is_items()).count(), 3);
3391 assert_eq!(linked_chunk.chunks().filter(|chunk| chunk.is_gap()).count(), 1);
3392 assert_eq!(linked_chunk.num_items(), 6);
3393 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3394
3395 linked_chunk.clear();
3397
3398 assert_eq!(Arc::strong_count(&item), 1);
3399 assert_eq!(Arc::strong_count(&gap), 1);
3400 assert_eq!(linked_chunk.chunks().filter(|chunk| chunk.is_items()).count(), 1);
3403 assert_eq!(linked_chunk.chunks().filter(|chunk| chunk.is_gap()).count(), 0);
3404 assert_eq!(linked_chunk.num_items(), 0);
3405 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3406 }
3407
3408 #[test]
3409 fn test_clear_emits_an_update_clear() {
3410 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3411
3412 linked_chunk.push_items_back(['a']);
3414
3415 assert_eq!(
3417 linked_chunk.updates().unwrap().take(),
3418 &[
3419 NewItemsChunk {
3420 previous: None,
3421 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3422 next: None
3423 },
3424 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }
3425 ]
3426 );
3427
3428 linked_chunk.clear();
3430
3431 assert_eq!(linked_chunk.updates().unwrap().take(), &[Clear]);
3433 }
3434
3435 #[test]
3436 fn test_clear_emits_an_update_clear_and_forget_about_pending_updates() {
3437 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3438
3439 linked_chunk.push_items_back(['a']);
3441
3442 linked_chunk.clear();
3444
3445 assert_eq!(linked_chunk.updates().unwrap().take(), &[Clear]);
3447 }
3448
3449 #[test]
3450 fn test_clear_emits_no_new_items_chunk_if_already_clear() {
3451 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3452
3453 linked_chunk.clear();
3455
3456 assert_eq!(linked_chunk.updates().unwrap().take(), &[Clear]);
3459 }
3460
3461 #[test]
3462 fn test_replace_item() {
3463 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3464
3465 linked_chunk.push_items_back(['a', 'b', 'c']);
3466 linked_chunk.push_gap_back(());
3467 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3469
3470 let _ = linked_chunk.updates().unwrap().take();
3472
3473 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3475 assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3476
3477 assert_eq!(
3478 linked_chunk.updates().unwrap().take(),
3479 &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3480 );
3481
3482 assert_matches!(
3484 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3485 Err(Error::InvalidItemIndex { index: 3 })
3486 );
3487
3488 assert_matches!(
3490 linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3491 Err(Error::ChunkIsAGap { .. })
3492 );
3493 }
3494
3495 #[test]
3496 fn test_lazy_previous() {
3497 use std::marker::PhantomData;
3498
3499 use super::{Ends, ObservableUpdates};
3500
3501 let first_chunk_identifier = ChunkIdentifier(0);
3503 let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3504 unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3505
3506 let updates = Some(ObservableUpdates::new());
3507
3508 let mut linked_chunk = LinkedChunk::<3, char, ()> {
3509 links: Ends::new_with_first_chunk(first_loaded_chunk, &updates),
3510 chunk_identifier_generator:
3511 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3512 updates,
3513 marker: PhantomData,
3514 };
3515
3516 {
3519 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3520
3521 assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3522
3523 {
3525 let mut chunks = linked_chunk.chunks();
3526
3527 assert_matches!(chunks.next(), Some(chunk) => {
3528 assert_eq!(chunk.identifier(), 1);
3529 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
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 &[
3542 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3543 NewItemsChunk {
3544 previous: Some(ChunkIdentifier(1)),
3545 new: ChunkIdentifier(2),
3546 next: None,
3547 },
3548 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3549 ]
3550 );
3551 }
3552
3553 {
3555 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3556
3557 assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3558
3559 {
3561 let mut chunks = linked_chunk.chunks();
3562
3563 assert_matches!(chunks.next(), Some(chunk) => {
3564 assert_eq!(chunk.identifier(), 3);
3565 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3567 });
3568 assert_matches!(chunks.next(), Some(chunk) => {
3569 assert_eq!(chunk.identifier(), 1);
3570 assert!(chunk.lazy_previous.is_none());
3572 });
3573 assert_matches!(chunks.next(), Some(chunk) => {
3574 assert_eq!(chunk.identifier(), 2);
3575 assert!(chunk.lazy_previous.is_none());
3576 });
3577 assert!(chunks.next().is_none());
3578 }
3579
3580 assert_eq!(
3582 linked_chunk.updates().unwrap().take(),
3583 &[NewGapChunk {
3584 previous: Some(ChunkIdentifier(0)),
3586 new: ChunkIdentifier(3),
3587 next: Some(ChunkIdentifier(1)),
3588 gap: ()
3589 }]
3590 );
3591 }
3592
3593 {
3595 linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3596
3597 assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3598
3599 {
3601 let mut chunks = linked_chunk.chunks();
3602
3603 assert_matches!(chunks.next(), Some(chunk) => {
3604 assert_eq!(chunk.identifier(), 4);
3605 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3607 });
3608 assert_matches!(chunks.next(), Some(chunk) => {
3609 assert_eq!(chunk.identifier(), 5);
3610 assert!(chunk.lazy_previous.is_none());
3611 });
3612 assert_matches!(chunks.next(), Some(chunk) => {
3613 assert_eq!(chunk.identifier(), 1);
3614 assert!(chunk.lazy_previous.is_none());
3615 });
3616 assert_matches!(chunks.next(), Some(chunk) => {
3617 assert_eq!(chunk.identifier(), 2);
3618 assert!(chunk.lazy_previous.is_none());
3619 });
3620 assert!(chunks.next().is_none());
3621 }
3622
3623 assert_eq!(
3625 linked_chunk.updates().unwrap().take(),
3626 &[
3627 NewItemsChunk {
3629 previous: Some(ChunkIdentifier(3)),
3630 new: ChunkIdentifier(4),
3631 next: Some(ChunkIdentifier(1)),
3632 },
3633 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3635 NewItemsChunk {
3637 previous: Some(ChunkIdentifier(4)),
3638 new: ChunkIdentifier(5),
3639 next: Some(ChunkIdentifier(1)),
3640 },
3641 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3643 RemoveChunk(ChunkIdentifier(3)),
3645 ]
3646 );
3647 }
3648
3649 {
3653 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3654
3655 assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3656
3657 {
3659 let mut chunks = linked_chunk.chunks();
3660
3661 assert_matches!(chunks.next(), Some(chunk) => {
3662 assert_eq!(chunk.identifier(), 6);
3663 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3665 });
3666 assert_matches!(chunks.next(), Some(chunk) => {
3667 assert_eq!(chunk.identifier(), 4);
3668 assert!(chunk.lazy_previous.is_none());
3670 });
3671 assert_matches!(chunks.next(), Some(chunk) => {
3672 assert_eq!(chunk.identifier(), 5);
3673 assert!(chunk.lazy_previous.is_none());
3674 });
3675 assert_matches!(chunks.next(), Some(chunk) => {
3676 assert_eq!(chunk.identifier(), 1);
3677 assert!(chunk.lazy_previous.is_none());
3678 });
3679 assert_matches!(chunks.next(), Some(chunk) => {
3680 assert_eq!(chunk.identifier(), 2);
3681 assert!(chunk.lazy_previous.is_none());
3682 });
3683 assert!(chunks.next().is_none());
3684 }
3685
3686 assert_eq!(
3688 linked_chunk.updates().unwrap().take(),
3689 &[NewGapChunk {
3690 previous: Some(ChunkIdentifier(0)),
3692 new: ChunkIdentifier(6),
3693 next: Some(ChunkIdentifier(4)),
3694 gap: ()
3695 }]
3696 );
3697 }
3698 }
3699}