1#![allow(rustdoc::private_intra_doc_links)]
16
17#[cfg(test)]
32macro_rules! assert_items_eq {
33 ( @_ [ $iterator:ident ] { [-] $( $rest:tt )* } { $( $accumulator:tt )* } ) => {
34 assert_items_eq!(
35 @_
36 [ $iterator ]
37 { $( $rest )* }
38 {
39 $( $accumulator )*
40 {
41 let chunk = $iterator .next().expect("next chunk (expect gap)");
42 assert!(chunk.is_gap(), "chunk should be a gap");
43 }
44 }
45 )
46 };
47
48 ( @_ [ $iterator:ident ] { [ $( $item:expr ),* ] $( $rest:tt )* } { $( $accumulator:tt )* } ) => {
49 assert_items_eq!(
50 @_
51 [ $iterator ]
52 { $( $rest )* }
53 {
54 $( $accumulator )*
55 {
56 let chunk = $iterator .next().expect("next chunk (expect items)");
57 assert!(chunk.is_items(), "chunk should contain items");
58
59 let $crate::linked_chunk::ChunkContent::Items(items) = chunk.content() else {
60 unreachable!()
61 };
62
63 let mut items_iterator = items.iter();
64
65 $(
66 assert_eq!(items_iterator.next(), Some(& $item ));
67 )*
68
69 assert!(items_iterator.next().is_none(), "no more items");
70 }
71 }
72 )
73 };
74
75 ( @_ [ $iterator:ident ] {} { $( $accumulator:tt )* } ) => {
76 {
77 $( $accumulator )*
78 assert!( $iterator .next().is_none(), "no more chunks");
79 }
80 };
81
82 ( $linked_chunk:expr, $( $all:tt )* ) => {
83 assert_items_eq!(
84 @_
85 [ iterator ]
86 { $( $all )* }
87 {
88 let mut iterator = $linked_chunk.chunks();
89 }
90 )
91 }
92}
93
94mod as_vector;
95pub mod lazy_loader;
96pub mod relational;
97mod updates;
98
99use std::{
100 fmt,
101 marker::PhantomData,
102 ptr::NonNull,
103 sync::atomic::{AtomicU64, Ordering},
104};
105
106pub use as_vector::*;
107pub use updates::*;
108
109#[derive(thiserror::Error, Debug)]
111pub enum Error {
112 #[error("The chunk identifier is invalid: `{identifier:?}`")]
114 InvalidChunkIdentifier {
115 identifier: ChunkIdentifier,
117 },
118
119 #[error("The chunk is a gap: `{identifier:?}`")]
121 ChunkIsAGap {
122 identifier: ChunkIdentifier,
124 },
125
126 #[error("The chunk is an item: `{identifier:?}`")]
128 ChunkIsItems {
129 identifier: ChunkIdentifier,
131 },
132
133 #[error("The chunk is a non-empty item chunk: `{identifier:?}`")]
135 RemovingNonEmptyItemsChunk {
136 identifier: ChunkIdentifier,
138 },
139
140 #[error("Trying to remove the only chunk, but a linked chunk can't be empty")]
143 RemovingLastChunk,
144
145 #[error("The item index is invalid: `{index}`")]
147 InvalidItemIndex {
148 index: usize,
150 },
151}
152
153struct Ends<const CHUNK_CAPACITY: usize, Item, Gap> {
158 first: NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>,
160 last: Option<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
162}
163
164impl<const CAP: usize, Item, Gap> Ends<CAP, Item, Gap> {
165 fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
167 unsafe { self.first.as_ref() }
168 }
169
170 fn first_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
172 unsafe { self.first.as_mut() }
173 }
174
175 fn latest_chunk(&self) -> &Chunk<CAP, Item, Gap> {
177 unsafe { self.last.unwrap_or(self.first).as_ref() }
178 }
179
180 fn latest_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
182 unsafe { self.last.as_mut().unwrap_or(&mut self.first).as_mut() }
183 }
184
185 fn chunk(&self, identifier: ChunkIdentifier) -> Option<&Chunk<CAP, Item, Gap>> {
187 let mut chunk = self.latest_chunk();
188
189 loop {
190 if chunk.identifier() == identifier {
191 return Some(chunk);
192 }
193
194 chunk = chunk.previous()?;
195 }
196 }
197
198 fn chunk_mut(&mut self, identifier: ChunkIdentifier) -> Option<&mut Chunk<CAP, Item, Gap>> {
200 let mut chunk = self.latest_chunk_mut();
201
202 loop {
203 if chunk.identifier() == identifier {
204 return Some(chunk);
205 }
206
207 chunk = chunk.previous_mut()?;
208 }
209 }
210
211 unsafe fn clear(&mut self) {
218 let mut current_chunk_ptr = self.last.or(Some(self.first));
221
222 while let Some(chunk_ptr) = current_chunk_ptr {
224 let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
226
227 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
229
230 current_chunk_ptr = previous_ptr;
232 }
233
234 self.first = NonNull::dangling();
236 self.last = None;
237 }
238
239 fn replace_with(&mut self, first_chunk: NonNull<Chunk<CAP, Item, Gap>>) {
242 unsafe {
244 self.clear();
245 }
246
247 self.first = first_chunk;
249 }
250
251 fn reset(&mut self) {
256 self.replace_with(Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER));
257 }
258}
259
260pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
267 links: Ends<CHUNK_CAPACITY, Item, Gap>,
269
270 chunk_identifier_generator: ChunkIdentifierGenerator,
272
273 updates: Option<ObservableUpdates<Item, Gap>>,
277
278 marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
280}
281
282impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
283 fn default() -> Self {
284 Self::new()
285 }
286}
287
288impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
289 pub fn new() -> Self {
291 Self {
292 links: Ends {
293 first: Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER),
294 last: None,
295 },
296 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
297 updates: None,
298 marker: PhantomData,
299 }
300 }
301
302 pub fn new_with_update_history() -> Self {
308 let first_chunk_identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
309
310 let mut updates = ObservableUpdates::new();
311 updates.push(Update::NewItemsChunk {
312 previous: None,
313 new: first_chunk_identifier,
314 next: None,
315 });
316
317 Self {
318 links: Ends { first: Chunk::new_items_leaked(first_chunk_identifier), last: None },
319 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
320 updates: Some(updates),
321 marker: PhantomData,
322 }
323 }
324
325 pub fn clear(&mut self) {
327 self.links.reset();
329
330 self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
332
333 if let Some(updates) = self.updates.as_mut() {
335 updates.clear_pending();
338 updates.push(Update::Clear);
339 updates.push(Update::NewItemsChunk {
340 previous: None,
341 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
342 next: None,
343 })
344 }
345 }
346
347 pub fn push_items_back<I>(&mut self, items: I)
353 where
354 Item: Clone,
355 Gap: Clone,
356 I: IntoIterator<Item = Item>,
357 I::IntoIter: ExactSizeIterator,
358 {
359 let items = items.into_iter();
360
361 let last_chunk = self.links.latest_chunk_mut();
362
363 let last_chunk =
365 last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
366
367 debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
368
369 if !last_chunk.is_first_chunk() {
373 self.links.last = Some(last_chunk.as_ptr());
376 }
377 }
378
379 pub fn push_gap_back(&mut self, content: Gap)
382 where
383 Item: Clone,
384 Gap: Clone,
385 {
386 let last_chunk = self.links.latest_chunk_mut();
387 last_chunk.insert_next(
388 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
389 &mut self.updates,
390 );
391
392 self.links.last = last_chunk.next;
393 }
394
395 pub fn insert_items_at<I>(&mut self, items: I, position: Position) -> Result<(), Error>
400 where
401 Item: Clone,
402 Gap: Clone,
403 I: IntoIterator<Item = Item>,
404 I::IntoIter: ExactSizeIterator,
405 {
406 let chunk_identifier = position.chunk_identifier();
407 let item_index = position.index();
408
409 let chunk = self
410 .links
411 .chunk_mut(chunk_identifier)
412 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
413
414 let chunk = match &mut chunk.content {
415 ChunkContent::Gap(..) => {
416 return Err(Error::ChunkIsAGap { identifier: chunk_identifier })
417 }
418
419 ChunkContent::Items(current_items) => {
420 let current_items_length = current_items.len();
421
422 if item_index > current_items_length {
423 return Err(Error::InvalidItemIndex { index: item_index });
424 }
425
426 let items = items.into_iter();
428
429 if item_index == current_items_length {
431 chunk
432 .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
434 }
435 else {
437 if let Some(updates) = self.updates.as_mut() {
438 updates.push(Update::DetachLastItems {
439 at: Position(chunk_identifier, item_index),
440 });
441 }
442
443 let detached_items = current_items.split_off(item_index);
445
446 let chunk = chunk
447 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
449
450 if let Some(updates) = self.updates.as_mut() {
451 updates.push(Update::StartReattachItems);
452 }
453
454 let chunk = chunk
455 .push_items(
457 detached_items.into_iter(),
458 &self.chunk_identifier_generator,
459 &mut self.updates,
460 );
461
462 if let Some(updates) = self.updates.as_mut() {
463 updates.push(Update::EndReattachItems);
464 }
465
466 chunk
467 }
468 }
469 };
470
471 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
474 self.links.last = Some(chunk.as_ptr());
477 }
478
479 Ok(())
480 }
481
482 pub fn remove_item_at(&mut self, position: Position) -> Result<Item, Error> {
490 let chunk_identifier = position.chunk_identifier();
491 let item_index = position.index();
492
493 let mut chunk_ptr = None;
494 let removed_item;
495
496 {
497 let chunk = self
498 .links
499 .chunk_mut(chunk_identifier)
500 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
501
502 let can_unlink_chunk = match &mut chunk.content {
503 ChunkContent::Gap(..) => {
504 return Err(Error::ChunkIsAGap { identifier: chunk_identifier })
505 }
506
507 ChunkContent::Items(current_items) => {
508 let current_items_length = current_items.len();
509
510 if item_index > current_items_length {
511 return Err(Error::InvalidItemIndex { index: item_index });
512 }
513
514 removed_item = current_items.remove(item_index);
515
516 if let Some(updates) = self.updates.as_mut() {
517 updates
518 .push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
519 }
520
521 current_items.is_empty()
522 }
523 };
524
525 if can_unlink_chunk && !chunk.is_first_chunk() {
528 chunk.unlink(self.updates.as_mut());
530
531 chunk_ptr = Some(chunk.as_ptr());
532
533 if chunk.is_last_chunk() {
536 self.links.last = chunk.previous;
537 }
538 }
539
540 }
542
543 if let Some(chunk_ptr) = chunk_ptr {
544 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
551 }
552
553 Ok(removed_item)
554 }
555
556 pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
561 where
562 Item: Clone,
563 {
564 let chunk_identifier = position.chunk_identifier();
565 let item_index = position.index();
566
567 let chunk = self
568 .links
569 .chunk_mut(chunk_identifier)
570 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
571
572 match &mut chunk.content {
573 ChunkContent::Gap(..) => {
574 return Err(Error::ChunkIsAGap { identifier: chunk_identifier })
575 }
576
577 ChunkContent::Items(current_items) => {
578 if item_index >= current_items.len() {
579 return Err(Error::InvalidItemIndex { index: item_index });
580 }
581
582 if let Some(updates) = self.updates.as_mut() {
584 updates.push(Update::ReplaceItem {
585 at: Position(chunk_identifier, item_index),
586 item: item.clone(),
587 });
588 }
589
590 current_items[item_index] = item;
591 }
592 }
593
594 Ok(())
595 }
596
597 pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
602 where
603 Item: Clone,
604 Gap: Clone,
605 {
606 let chunk_identifier = position.chunk_identifier();
607 let item_index = position.index();
608
609 let chunk = self
610 .links
611 .chunk_mut(chunk_identifier)
612 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
613
614 let chunk = match &mut chunk.content {
615 ChunkContent::Gap(..) => {
616 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
617 }
618
619 ChunkContent::Items(current_items) => {
620 if item_index == 0 {
624 let chunk_was_first = chunk.is_first_chunk();
625 let chunk_was_last = chunk.is_last_chunk();
626
627 let new_chunk = chunk.insert_before(
628 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
629 self.updates.as_mut(),
630 );
631
632 let new_chunk_ptr = new_chunk.as_ptr();
633 let chunk_ptr = chunk.as_ptr();
634
635 if chunk_was_first {
640 self.links.first = new_chunk_ptr;
641
642 if chunk_was_last {
644 self.links.last = Some(chunk_ptr);
645 }
646 }
647
648 return Ok(());
649 }
650
651 let current_items_length = current_items.len();
652
653 if item_index >= current_items_length {
654 return Err(Error::InvalidItemIndex { index: item_index });
655 }
656
657 if let Some(updates) = self.updates.as_mut() {
658 updates.push(Update::DetachLastItems {
659 at: Position(chunk_identifier, item_index),
660 });
661 }
662
663 let detached_items = current_items.split_off(item_index);
665
666 let chunk = chunk
667 .insert_next(
669 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
670 &mut self.updates,
671 );
672
673 if let Some(updates) = self.updates.as_mut() {
674 updates.push(Update::StartReattachItems);
675 }
676
677 let chunk = chunk
678 .insert_next(
680 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
681 &mut self.updates,
682 )
683 .push_items(
685 detached_items.into_iter(),
686 &self.chunk_identifier_generator,
687 &mut self.updates,
688 );
689
690 if let Some(updates) = self.updates.as_mut() {
691 updates.push(Update::EndReattachItems);
692 }
693
694 chunk
695 }
696 };
697
698 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
701 self.links.last = Some(chunk.as_ptr());
704 }
705
706 Ok(())
707 }
708
709 pub fn remove_empty_chunk_at(
718 &mut self,
719 chunk_identifier: ChunkIdentifier,
720 ) -> Result<Option<Position>, Error> {
721 if self.links.first_chunk().is_last_chunk() {
723 return Err(Error::RemovingLastChunk);
724 }
725
726 let chunk = self
727 .links
728 .chunk_mut(chunk_identifier)
729 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
730
731 if chunk.num_items() > 0 {
732 return Err(Error::RemovingNonEmptyItemsChunk { identifier: chunk_identifier });
733 }
734
735 let chunk_was_first = chunk.is_first_chunk();
736 let chunk_was_last = chunk.is_last_chunk();
737 let next_ptr = chunk.next;
738 let previous_ptr = chunk.previous;
739 let position_of_next = chunk.next().map(|next| next.first_position());
740
741 chunk.unlink(self.updates.as_mut());
742
743 let chunk_ptr = chunk.as_ptr();
744
745 if chunk_was_first {
747 if let Some(next_ptr) = next_ptr {
749 self.links.first = next_ptr;
750 }
751 }
752
753 if chunk_was_last {
754 self.links.last = previous_ptr;
755 }
756
757 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
760
761 Ok(position_of_next)
763 }
764
765 pub fn replace_gap_at<I>(
773 &mut self,
774 items: I,
775 chunk_identifier: ChunkIdentifier,
776 ) -> Result<&Chunk<CAP, Item, Gap>, Error>
777 where
778 Item: Clone,
779 Gap: Clone,
780 I: IntoIterator<Item = Item>,
781 I::IntoIter: ExactSizeIterator,
782 {
783 let chunk_ptr;
784 let new_chunk_ptr;
785
786 {
787 let chunk = self
788 .links
789 .chunk_mut(chunk_identifier)
790 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
791
792 if chunk.is_items() {
793 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
794 };
795
796 let chunk_was_first = chunk.is_first_chunk();
797
798 let maybe_last_chunk_ptr = {
799 let items = items.into_iter();
800
801 let last_inserted_chunk = chunk
802 .insert_next(
804 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
805 &mut self.updates,
806 )
807 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
809
810 last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
811 };
812
813 new_chunk_ptr = chunk
814 .next
815 .unwrap();
817
818 chunk.unlink(self.updates.as_mut());
820
821 chunk_ptr = chunk.as_ptr();
823
824 if chunk_was_first {
826 self.links.first = new_chunk_ptr;
827 }
828
829 if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
832 self.links.last = Some(last_chunk_ptr);
833 }
834
835 }
837
838 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
843
844 Ok(
845 unsafe { new_chunk_ptr.as_ref() },
849 )
850 }
851
852 pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
854 where
855 P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
856 {
857 self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
858 }
859
860 pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
862 where
863 P: FnMut(&'a Item) -> bool,
864 {
865 self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
866 }
867
868 pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
872 IterBackward::new(self.links.latest_chunk())
873 }
874
875 pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
879 Iter::new(self.links.first_chunk())
880 }
881
882 pub fn rchunks_from(
887 &self,
888 identifier: ChunkIdentifier,
889 ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
890 Ok(IterBackward::new(
891 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
892 ))
893 }
894
895 pub fn chunks_from(
900 &self,
901 identifier: ChunkIdentifier,
902 ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
903 Ok(Iter::new(
904 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
905 ))
906 }
907
908 pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
912 self.ritems_from(self.links.latest_chunk().last_position())
913 .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
914 }
915
916 pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
920 let first_chunk = self.links.first_chunk();
921
922 self.items_from(first_chunk.first_position())
923 .expect("`items` cannot fail because at least one empty chunk must exist")
924 }
925
926 pub fn ritems_from(
930 &self,
931 position: Position,
932 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
933 Ok(self
934 .rchunks_from(position.chunk_identifier())?
935 .filter_map(|chunk| match &chunk.content {
936 ChunkContent::Gap(..) => None,
937 ChunkContent::Items(items) => {
938 let identifier = chunk.identifier();
939
940 Some(
941 items.iter().enumerate().rev().map(move |(item_index, item)| {
942 (Position(identifier, item_index), item)
943 }),
944 )
945 }
946 })
947 .flatten()
948 .skip_while({
949 let expected_index = position.index();
950
951 move |(Position(chunk_identifier, item_index), _item)| {
952 *chunk_identifier == position.chunk_identifier()
953 && *item_index != expected_index
954 }
955 }))
956 }
957
958 pub fn items_from(
962 &self,
963 position: Position,
964 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
965 Ok(self
966 .chunks_from(position.chunk_identifier())?
967 .filter_map(|chunk| match &chunk.content {
968 ChunkContent::Gap(..) => None,
969 ChunkContent::Items(items) => {
970 let identifier = chunk.identifier();
971
972 Some(
973 items.iter().enumerate().map(move |(item_index, item)| {
974 (Position(identifier, item_index), item)
975 }),
976 )
977 }
978 })
979 .flatten()
980 .skip(position.index()))
981 }
982
983 #[must_use]
995 pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
996 self.updates.as_mut()
997 }
998
999 pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
1006 let (updates, token) = self
1007 .updates
1008 .as_mut()
1009 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1010 let chunk_iterator = self.chunks();
1011
1012 Some(AsVector::new(updates, token, chunk_iterator))
1013 }
1014
1015 pub fn num_items(&self) -> usize {
1017 self.items().count()
1018 }
1019}
1020
1021impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
1022 fn drop(&mut self) {
1023 unsafe {
1033 self.links.clear();
1034 }
1035 }
1036}
1037
1038unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1042
1043unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1047
1048#[derive(Debug)]
1058pub struct ChunkIdentifierGenerator {
1059 next: AtomicU64,
1060}
1061
1062impl ChunkIdentifierGenerator {
1063 const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1065
1066 pub fn new_from_scratch() -> Self {
1069 Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1070 }
1071
1072 pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1075 Self { next: AtomicU64::new(last_chunk_identifier.0) }
1076 }
1077
1078 fn next(&self) -> ChunkIdentifier {
1083 let previous = self.next.fetch_add(1, Ordering::Relaxed);
1084
1085 if previous == u64::MAX {
1088 panic!("No more chunk identifiers available. Congrats, you did it. 2^64 identifiers have been consumed.")
1089 }
1090
1091 ChunkIdentifier(previous + 1)
1092 }
1093
1094 #[doc(hidden)]
1098 pub fn current(&self) -> ChunkIdentifier {
1099 ChunkIdentifier(self.next.load(Ordering::Relaxed))
1100 }
1101}
1102
1103#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1109#[repr(transparent)]
1110pub struct ChunkIdentifier(u64);
1111
1112impl ChunkIdentifier {
1113 pub fn new(identifier: u64) -> Self {
1115 Self(identifier)
1116 }
1117
1118 pub fn index(&self) -> u64 {
1120 self.0
1121 }
1122}
1123
1124impl PartialEq<u64> for ChunkIdentifier {
1125 fn eq(&self, other: &u64) -> bool {
1126 self.0 == *other
1127 }
1128}
1129
1130#[derive(Copy, Clone, Debug, PartialEq)]
1134pub struct Position(ChunkIdentifier, usize);
1135
1136impl Position {
1137 pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1139 Self(chunk_identifier, index)
1140 }
1141
1142 pub fn chunk_identifier(&self) -> ChunkIdentifier {
1144 self.0
1145 }
1146
1147 pub fn index(&self) -> usize {
1149 self.1
1150 }
1151
1152 pub fn decrement_index(&mut self) {
1158 self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1159 }
1160
1161 pub fn increment_index(&mut self) {
1168 self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1169 }
1170}
1171
1172#[derive(Debug)]
1175pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1176 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1177}
1178
1179impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1180 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1182 Self { chunk: Some(from_chunk) }
1183 }
1184}
1185
1186impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1187 type Item = &'a Chunk<CAP, Item, Gap>;
1188
1189 fn next(&mut self) -> Option<Self::Item> {
1190 self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1191 }
1192}
1193
1194#[derive(Debug)]
1197pub struct Iter<'a, const CAP: usize, Item, Gap> {
1198 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1199}
1200
1201impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1202 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1204 Self { chunk: Some(from_chunk) }
1205 }
1206}
1207
1208impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1209 type Item = &'a Chunk<CAP, Item, Gap>;
1210
1211 fn next(&mut self) -> Option<Self::Item> {
1212 self.chunk.inspect(|chunk| self.chunk = chunk.next())
1213 }
1214}
1215
1216#[derive(Clone, Debug)]
1218pub enum ChunkContent<Item, Gap> {
1219 Gap(Gap),
1222
1223 Items(Vec<Item>),
1225}
1226
1227pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1229 previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1231
1232 lazy_previous: Option<ChunkIdentifier>,
1237
1238 next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1240
1241 identifier: ChunkIdentifier,
1243
1244 content: ChunkContent<Item, Gap>,
1246}
1247
1248impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1249 fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1251 Self::new(identifier, ChunkContent::Gap(content))
1252 }
1253
1254 fn new_items(identifier: ChunkIdentifier) -> Self {
1256 Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1257 }
1258
1259 fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1260 Self { previous: None, lazy_previous: None, next: None, identifier, content }
1261 }
1262
1263 fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1265 let chunk = Self::new(identifier, content);
1266 let chunk_box = Box::new(chunk);
1267
1268 NonNull::from(Box::leak(chunk_box))
1269 }
1270
1271 fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1273 let chunk = Self::new_gap(identifier, content);
1274 let chunk_box = Box::new(chunk);
1275
1276 NonNull::from(Box::leak(chunk_box))
1277 }
1278
1279 fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1281 let chunk = Self::new_items(identifier);
1282 let chunk_box = Box::new(chunk);
1283
1284 NonNull::from(Box::leak(chunk_box))
1285 }
1286
1287 pub fn as_ptr(&self) -> NonNull<Self> {
1289 NonNull::from(self)
1290 }
1291
1292 pub fn is_gap(&self) -> bool {
1294 matches!(self.content, ChunkContent::Gap(..))
1295 }
1296
1297 pub fn is_items(&self) -> bool {
1299 !self.is_gap()
1300 }
1301
1302 pub fn is_definitive_head(&self) -> bool {
1305 self.previous.is_none() && self.lazy_previous.is_none()
1306 }
1307
1308 fn is_first_chunk(&self) -> bool {
1310 self.previous.is_none()
1311 }
1312
1313 fn is_last_chunk(&self) -> bool {
1315 self.next.is_none()
1316 }
1317
1318 #[doc(hidden)]
1322 pub fn lazy_previous(&self) -> Option<ChunkIdentifier> {
1323 self.lazy_previous
1324 }
1325
1326 pub fn identifier(&self) -> ChunkIdentifier {
1328 self.identifier
1329 }
1330
1331 pub fn content(&self) -> &ChunkContent<Item, Gap> {
1333 &self.content
1334 }
1335
1336 pub fn first_position(&self) -> Position {
1340 Position(self.identifier(), 0)
1341 }
1342
1343 pub fn last_position(&self) -> Position {
1347 let identifier = self.identifier();
1348
1349 match &self.content {
1350 ChunkContent::Gap(..) => Position(identifier, 0),
1351 ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1352 }
1353 }
1354
1355 pub fn num_items(&self) -> usize {
1359 match &self.content {
1360 ChunkContent::Gap(..) => 0,
1361 ChunkContent::Items(items) => items.len(),
1362 }
1363 }
1364
1365 fn push_items<I>(
1377 &mut self,
1378 mut new_items: I,
1379 chunk_identifier_generator: &ChunkIdentifierGenerator,
1380 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1381 ) -> &mut Self
1382 where
1383 I: Iterator<Item = Item> + ExactSizeIterator,
1384 Item: Clone,
1385 Gap: Clone,
1386 {
1387 if new_items.len() == 0 {
1389 return self;
1390 }
1391
1392 let identifier = self.identifier();
1393 let prev_num_items = self.num_items();
1394
1395 match &mut self.content {
1396 ChunkContent::Gap(..) => {
1399 self
1400 .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1402 .push_items(new_items, chunk_identifier_generator, updates)
1405 }
1406
1407 ChunkContent::Items(items) => {
1408 let free_space = CAPACITY.saturating_sub(prev_num_items);
1410
1411 if new_items.len() <= free_space {
1413 let start = items.len();
1414 items.extend(new_items);
1415
1416 if let Some(updates) = updates.as_mut() {
1417 updates.push(Update::PushItems {
1418 at: Position(identifier, start),
1419 items: items[start..].to_vec(),
1420 });
1421 }
1422
1423 self
1425 } else {
1426 if free_space > 0 {
1427 let start = items.len();
1429 items.extend(new_items.by_ref().take(free_space));
1430
1431 if let Some(updates) = updates.as_mut() {
1432 updates.push(Update::PushItems {
1433 at: Position(identifier, start),
1434 items: items[start..].to_vec(),
1435 });
1436 }
1437 }
1438
1439 self
1440 .insert_next(
1442 Self::new_items_leaked(chunk_identifier_generator.next()),
1443 updates,
1444 )
1445 .push_items(new_items, chunk_identifier_generator, updates)
1448 }
1449 }
1450 }
1451 }
1452
1453 fn insert_next(
1458 &mut self,
1459 mut new_chunk_ptr: NonNull<Self>,
1460 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1461 ) -> &mut Self
1462 where
1463 Gap: Clone,
1464 {
1465 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1466
1467 if let Some(next_chunk) = self.next_mut() {
1469 next_chunk.previous = Some(new_chunk_ptr);
1471
1472 new_chunk.next = self.next;
1474 }
1475
1476 self.next = Some(new_chunk_ptr);
1478 new_chunk.previous = Some(self.as_ptr());
1480
1481 if let Some(updates) = updates.as_mut() {
1482 let previous = new_chunk.previous().map(Chunk::identifier);
1483 let new = new_chunk.identifier();
1484 let next = new_chunk.next().map(Chunk::identifier);
1485
1486 match new_chunk.content() {
1487 ChunkContent::Gap(gap) => {
1488 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1489 }
1490
1491 ChunkContent::Items(..) => {
1492 updates.push(Update::NewItemsChunk { previous, new, next })
1493 }
1494 }
1495 }
1496
1497 new_chunk
1498 }
1499
1500 fn insert_before(
1505 &mut self,
1506 mut new_chunk_ptr: NonNull<Self>,
1507 updates: Option<&mut ObservableUpdates<Item, Gap>>,
1508 ) -> &mut Self
1509 where
1510 Gap: Clone,
1511 {
1512 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1513
1514 if let Some(previous_chunk) = self.previous_mut() {
1516 previous_chunk.next = Some(new_chunk_ptr);
1518
1519 new_chunk.previous = self.previous;
1521 }
1522 else {
1525 new_chunk.lazy_previous = self.lazy_previous.take();
1526 }
1527
1528 self.previous = Some(new_chunk_ptr);
1530 new_chunk.next = Some(self.as_ptr());
1532
1533 if let Some(updates) = updates {
1534 let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1535 let new = new_chunk.identifier();
1536 let next = new_chunk.next().map(Chunk::identifier);
1537
1538 match new_chunk.content() {
1539 ChunkContent::Gap(gap) => {
1540 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1541 }
1542
1543 ChunkContent::Items(..) => {
1544 updates.push(Update::NewItemsChunk { previous, new, next })
1545 }
1546 }
1547 }
1548
1549 new_chunk
1550 }
1551
1552 fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1557 let previous_ptr = self.previous;
1558 let next_ptr = self.next;
1559 let lazy_previous = self.lazy_previous.take();
1563
1564 if let Some(previous) = self.previous_mut() {
1565 previous.next = next_ptr;
1566 }
1567
1568 if let Some(next) = self.next_mut() {
1569 next.previous = previous_ptr;
1570 next.lazy_previous = lazy_previous;
1571 }
1572
1573 if let Some(updates) = updates {
1574 updates.push(Update::RemoveChunk(self.identifier()));
1575 }
1576 }
1577
1578 fn previous(&self) -> Option<&Self> {
1580 self.previous.map(|non_null| unsafe { non_null.as_ref() })
1581 }
1582
1583 fn previous_mut(&mut self) -> Option<&mut Self> {
1585 self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1586 }
1587
1588 fn next(&self) -> Option<&Self> {
1590 self.next.map(|non_null| unsafe { non_null.as_ref() })
1591 }
1592
1593 fn next_mut(&mut self) -> Option<&mut Self> {
1595 self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1596 }
1597}
1598
1599impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<CAP, Item, Gap>
1600where
1601 Item: fmt::Debug,
1602 Gap: fmt::Debug,
1603{
1604 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1605 formatter
1606 .debug_struct("LinkedChunk")
1607 .field("first (deref)", unsafe { self.links.first.as_ref() })
1608 .field("last", &self.links.last)
1609 .finish_non_exhaustive()
1610 }
1611}
1612
1613impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1614where
1615 Item: fmt::Debug,
1616 Gap: fmt::Debug,
1617{
1618 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1619 formatter
1620 .debug_struct("Chunk")
1621 .field("identifier", &self.identifier)
1622 .field("content", &self.content)
1623 .field("previous", &self.previous)
1624 .field("ptr", &std::ptr::from_ref(self))
1625 .field("next", &self.next)
1626 .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1627 .finish()
1628 }
1629}
1630
1631#[derive(Clone, Debug)]
1637pub struct RawChunk<Item, Gap> {
1638 pub content: ChunkContent<Item, Gap>,
1640
1641 pub previous: Option<ChunkIdentifier>,
1643
1644 pub identifier: ChunkIdentifier,
1646
1647 pub next: Option<ChunkIdentifier>,
1649}
1650
1651#[cfg(test)]
1652mod tests {
1653 use std::{
1654 ops::Not,
1655 sync::{atomic::Ordering, Arc},
1656 };
1657
1658 use assert_matches::assert_matches;
1659
1660 use super::{
1661 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Error, LinkedChunk,
1662 Position,
1663 };
1664
1665 #[test]
1666 fn test_chunk_identifier_generator() {
1667 let generator = ChunkIdentifierGenerator::new_from_scratch();
1668
1669 assert_eq!(generator.next(), ChunkIdentifier(1));
1670 assert_eq!(generator.next(), ChunkIdentifier(2));
1671 assert_eq!(generator.next(), ChunkIdentifier(3));
1672 assert_eq!(generator.next(), ChunkIdentifier(4));
1673
1674 let generator =
1675 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1676
1677 assert_eq!(generator.next(), ChunkIdentifier(43));
1678 assert_eq!(generator.next(), ChunkIdentifier(44));
1679 assert_eq!(generator.next(), ChunkIdentifier(45));
1680 assert_eq!(generator.next(), ChunkIdentifier(46));
1681 }
1682
1683 #[test]
1684 fn test_empty() {
1685 let items = LinkedChunk::<3, char, ()>::new();
1686
1687 assert_eq!(items.num_items(), 0);
1688
1689 }
1692
1693 #[test]
1694 fn test_updates() {
1695 assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1696 assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1697 }
1698
1699 #[test]
1700 fn test_new_with_initial_update() {
1701 use super::Update::*;
1702
1703 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1704
1705 assert_eq!(
1706 linked_chunk.updates().unwrap().take(),
1707 &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1708 );
1709 }
1710
1711 #[test]
1712 fn test_push_items() {
1713 use super::Update::*;
1714
1715 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1716
1717 let _ = linked_chunk.updates().unwrap().take();
1719
1720 linked_chunk.push_items_back(['a']);
1721
1722 assert_items_eq!(linked_chunk, ['a']);
1723 assert_eq!(
1724 linked_chunk.updates().unwrap().take(),
1725 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1726 );
1727
1728 linked_chunk.push_items_back(['b', 'c']);
1729 assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1730 assert_eq!(
1731 linked_chunk.updates().unwrap().take(),
1732 &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1733 );
1734
1735 linked_chunk.push_items_back(['d', 'e']);
1736 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
1737 assert_eq!(
1738 linked_chunk.updates().unwrap().take(),
1739 &[
1740 NewItemsChunk {
1741 previous: Some(ChunkIdentifier(0)),
1742 new: ChunkIdentifier(1),
1743 next: None
1744 },
1745 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1746 ]
1747 );
1748
1749 linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1750 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1751 assert_eq!(
1752 linked_chunk.updates().unwrap().take(),
1753 &[
1754 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1755 NewItemsChunk {
1756 previous: Some(ChunkIdentifier(1)),
1757 new: ChunkIdentifier(2),
1758 next: None,
1759 },
1760 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1761 NewItemsChunk {
1762 previous: Some(ChunkIdentifier(2)),
1763 new: ChunkIdentifier(3),
1764 next: None,
1765 },
1766 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1767 ]
1768 );
1769
1770 assert_eq!(linked_chunk.num_items(), 10);
1771 }
1772
1773 #[test]
1774 fn test_push_gap() {
1775 use super::Update::*;
1776
1777 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1778
1779 let _ = linked_chunk.updates().unwrap().take();
1781
1782 linked_chunk.push_items_back(['a']);
1783 assert_items_eq!(linked_chunk, ['a']);
1784 assert_eq!(
1785 linked_chunk.updates().unwrap().take(),
1786 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1787 );
1788
1789 linked_chunk.push_gap_back(());
1790 assert_items_eq!(linked_chunk, ['a'] [-]);
1791 assert_eq!(
1792 linked_chunk.updates().unwrap().take(),
1793 &[NewGapChunk {
1794 previous: Some(ChunkIdentifier(0)),
1795 new: ChunkIdentifier(1),
1796 next: None,
1797 gap: (),
1798 }]
1799 );
1800
1801 linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1802 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1803 assert_eq!(
1804 linked_chunk.updates().unwrap().take(),
1805 &[
1806 NewItemsChunk {
1807 previous: Some(ChunkIdentifier(1)),
1808 new: ChunkIdentifier(2),
1809 next: None,
1810 },
1811 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
1812 NewItemsChunk {
1813 previous: Some(ChunkIdentifier(2)),
1814 new: ChunkIdentifier(3),
1815 next: None,
1816 },
1817 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
1818 ]
1819 );
1820
1821 linked_chunk.push_gap_back(());
1822 linked_chunk.push_gap_back(()); assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
1824 assert_eq!(
1825 linked_chunk.updates().unwrap().take(),
1826 &[
1827 NewGapChunk {
1828 previous: Some(ChunkIdentifier(3)),
1829 new: ChunkIdentifier(4),
1830 next: None,
1831 gap: (),
1832 },
1833 NewGapChunk {
1834 previous: Some(ChunkIdentifier(4)),
1835 new: ChunkIdentifier(5),
1836 next: None,
1837 gap: (),
1838 }
1839 ]
1840 );
1841
1842 linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
1843 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
1844 assert_eq!(
1845 linked_chunk.updates().unwrap().take(),
1846 &[
1847 NewItemsChunk {
1848 previous: Some(ChunkIdentifier(5)),
1849 new: ChunkIdentifier(6),
1850 next: None,
1851 },
1852 PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
1853 NewItemsChunk {
1854 previous: Some(ChunkIdentifier(6)),
1855 new: ChunkIdentifier(7),
1856 next: None,
1857 },
1858 PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
1859 ]
1860 );
1861
1862 assert_eq!(linked_chunk.num_items(), 9);
1863 }
1864
1865 #[test]
1866 fn test_identifiers_and_positions() {
1867 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
1868 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
1869 linked_chunk.push_gap_back(());
1870 linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
1871 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
1872
1873 assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
1874 assert_eq!(
1875 linked_chunk.item_position(|item| *item == 'e'),
1876 Some(Position(ChunkIdentifier(1), 1))
1877 );
1878 }
1879
1880 #[test]
1881 fn test_rchunks() {
1882 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1883 linked_chunk.push_items_back(['a', 'b']);
1884 linked_chunk.push_gap_back(());
1885 linked_chunk.push_items_back(['c', 'd', 'e']);
1886
1887 let mut iterator = linked_chunk.rchunks();
1888
1889 assert_matches!(
1890 iterator.next(),
1891 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
1892 assert_eq!(items, &['e']);
1893 }
1894 );
1895 assert_matches!(
1896 iterator.next(),
1897 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1898 assert_eq!(items, &['c', 'd']);
1899 }
1900 );
1901 assert_matches!(
1902 iterator.next(),
1903 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
1904 );
1905 assert_matches!(
1906 iterator.next(),
1907 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
1908 assert_eq!(items, &['a', 'b']);
1909 }
1910 );
1911 assert_matches!(iterator.next(), None);
1912 }
1913
1914 #[test]
1915 fn test_chunks() {
1916 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1917 linked_chunk.push_items_back(['a', 'b']);
1918 linked_chunk.push_gap_back(());
1919 linked_chunk.push_items_back(['c', 'd', 'e']);
1920
1921 let mut iterator = linked_chunk.chunks();
1922
1923 assert_matches!(
1924 iterator.next(),
1925 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
1926 assert_eq!(items, &['a', 'b']);
1927 }
1928 );
1929 assert_matches!(
1930 iterator.next(),
1931 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
1932 );
1933 assert_matches!(
1934 iterator.next(),
1935 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1936 assert_eq!(items, &['c', 'd']);
1937 }
1938 );
1939 assert_matches!(
1940 iterator.next(),
1941 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
1942 assert_eq!(items, &['e']);
1943 }
1944 );
1945 assert_matches!(iterator.next(), None);
1946 }
1947
1948 #[test]
1949 fn test_rchunks_from() -> Result<(), Error> {
1950 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1951 linked_chunk.push_items_back(['a', 'b']);
1952 linked_chunk.push_gap_back(());
1953 linked_chunk.push_items_back(['c', 'd', 'e']);
1954
1955 let mut iterator = linked_chunk.rchunks_from(
1956 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
1957 )?;
1958
1959 assert_matches!(
1960 iterator.next(),
1961 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1962 assert_eq!(items, &['c', 'd']);
1963 }
1964 );
1965 assert_matches!(
1966 iterator.next(),
1967 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
1968 );
1969 assert_matches!(
1970 iterator.next(),
1971 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
1972 assert_eq!(items, &['a', 'b']);
1973 }
1974 );
1975 assert_matches!(iterator.next(), None);
1976
1977 Ok(())
1978 }
1979
1980 #[test]
1981 fn test_chunks_from() -> Result<(), Error> {
1982 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1983 linked_chunk.push_items_back(['a', 'b']);
1984 linked_chunk.push_gap_back(());
1985 linked_chunk.push_items_back(['c', 'd', 'e']);
1986
1987 let mut iterator = linked_chunk.chunks_from(
1988 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
1989 )?;
1990
1991 assert_matches!(
1992 iterator.next(),
1993 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1994 assert_eq!(items, &['c', 'd']);
1995 }
1996 );
1997 assert_matches!(
1998 iterator.next(),
1999 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2000 assert_eq!(items, &['e']);
2001 }
2002 );
2003 assert_matches!(iterator.next(), None);
2004
2005 Ok(())
2006 }
2007
2008 #[test]
2009 fn test_ritems() {
2010 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2011 linked_chunk.push_items_back(['a', 'b']);
2012 linked_chunk.push_gap_back(());
2013 linked_chunk.push_items_back(['c', 'd', 'e']);
2014
2015 let mut iterator = linked_chunk.ritems();
2016
2017 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2018 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2019 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2020 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2021 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2022 assert_matches!(iterator.next(), None);
2023 }
2024
2025 #[test]
2026 fn test_ritems_with_final_gap() -> Result<(), Error> {
2027 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2028 linked_chunk.push_items_back(['a', 'b']);
2029 linked_chunk.push_gap_back(());
2030 linked_chunk.push_items_back(['c', 'd', 'e']);
2031 linked_chunk.push_gap_back(());
2032
2033 let mut iterator = linked_chunk.ritems();
2034
2035 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
2036 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2037 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2038 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2039 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2040 assert_matches!(iterator.next(), None);
2041
2042 Ok(())
2043 }
2044
2045 #[test]
2046 fn test_ritems_empty() {
2047 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2048 let mut iterator = linked_chunk.ritems();
2049
2050 assert_matches!(iterator.next(), None);
2051 }
2052
2053 #[test]
2054 fn test_items() {
2055 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2056 linked_chunk.push_items_back(['a', 'b']);
2057 linked_chunk.push_gap_back(());
2058 linked_chunk.push_items_back(['c', 'd', 'e']);
2059
2060 let mut iterator = linked_chunk.items();
2061
2062 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2063 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2064 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2065 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2066 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2067 assert_matches!(iterator.next(), None);
2068 }
2069
2070 #[test]
2071 fn test_items_empty() {
2072 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2073 let mut iterator = linked_chunk.items();
2074
2075 assert_matches!(iterator.next(), None);
2076 }
2077
2078 #[test]
2079 fn test_ritems_from() -> Result<(), Error> {
2080 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2081 linked_chunk.push_items_back(['a', 'b']);
2082 linked_chunk.push_gap_back(());
2083 linked_chunk.push_items_back(['c', 'd', 'e']);
2084
2085 let mut iterator =
2086 linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2087
2088 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2089 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2090 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2091 assert_matches!(iterator.next(), None);
2092
2093 Ok(())
2094 }
2095
2096 #[test]
2097 fn test_items_from() -> Result<(), Error> {
2098 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2099 linked_chunk.push_items_back(['a', 'b']);
2100 linked_chunk.push_gap_back(());
2101 linked_chunk.push_items_back(['c', 'd', 'e']);
2102
2103 let mut iterator =
2104 linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2105
2106 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2107 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2108 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2109 assert_matches!(iterator.next(), None);
2110
2111 Ok(())
2112 }
2113
2114 #[test]
2115 fn test_insert_items_at() -> Result<(), Error> {
2116 use super::Update::*;
2117
2118 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2119
2120 let _ = linked_chunk.updates().unwrap().take();
2122
2123 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2124 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2125 assert_eq!(
2126 linked_chunk.updates().unwrap().take(),
2127 &[
2128 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2129 NewItemsChunk {
2130 previous: Some(ChunkIdentifier(0)),
2131 new: ChunkIdentifier(1),
2132 next: None,
2133 },
2134 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2135 ]
2136 );
2137
2138 {
2140 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2141
2142 linked_chunk.insert_items_at(['w', 'x', 'y', 'z'], position_of_e)?;
2145
2146 assert_items_eq!(
2147 linked_chunk,
2148 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2149 );
2150 assert_eq!(linked_chunk.num_items(), 10);
2151 assert_eq!(
2152 linked_chunk.updates().unwrap().take(),
2153 &[
2154 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2155 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2156 NewItemsChunk {
2157 previous: Some(ChunkIdentifier(1)),
2158 new: ChunkIdentifier(2),
2159 next: None,
2160 },
2161 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2162 StartReattachItems,
2163 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2164 NewItemsChunk {
2165 previous: Some(ChunkIdentifier(2)),
2166 new: ChunkIdentifier(3),
2167 next: None,
2168 },
2169 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2170 EndReattachItems,
2171 ]
2172 );
2173 }
2174
2175 {
2177 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2178 linked_chunk.insert_items_at(['l', 'm', 'n', 'o'], position_of_a)?;
2179
2180 assert_items_eq!(
2181 linked_chunk,
2182 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2183 );
2184 assert_eq!(linked_chunk.num_items(), 14);
2185 assert_eq!(
2186 linked_chunk.updates().unwrap().take(),
2187 &[
2188 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2189 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2190 NewItemsChunk {
2191 previous: Some(ChunkIdentifier(0)),
2192 new: ChunkIdentifier(4),
2193 next: Some(ChunkIdentifier(1)),
2194 },
2195 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2196 StartReattachItems,
2197 PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2198 NewItemsChunk {
2199 previous: Some(ChunkIdentifier(4)),
2200 new: ChunkIdentifier(5),
2201 next: Some(ChunkIdentifier(1)),
2202 },
2203 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2204 EndReattachItems,
2205 ]
2206 );
2207 }
2208
2209 {
2211 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2212 linked_chunk.insert_items_at(['r', 's'], position_of_c)?;
2213
2214 assert_items_eq!(
2215 linked_chunk,
2216 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2217 );
2218 assert_eq!(linked_chunk.num_items(), 16);
2219 assert_eq!(
2220 linked_chunk.updates().unwrap().take(),
2221 &[
2222 DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2223 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2224 StartReattachItems,
2225 PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2226 EndReattachItems,
2227 ]
2228 );
2229 }
2230
2231 {
2233 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2234 let position_after_f =
2235 Position(position_of_f.chunk_identifier(), position_of_f.index() + 1);
2236
2237 linked_chunk.insert_items_at(['p', 'q'], position_after_f)?;
2238 assert_items_eq!(
2239 linked_chunk,
2240 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2241 );
2242 assert_eq!(
2243 linked_chunk.updates().unwrap().take(),
2244 &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2245 );
2246 assert_eq!(linked_chunk.num_items(), 18);
2247 }
2248
2249 {
2251 assert_matches!(
2252 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)),
2253 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2254 );
2255 assert!(linked_chunk.updates().unwrap().take().is_empty());
2256 }
2257
2258 {
2260 assert_matches!(
2261 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)),
2262 Err(Error::InvalidItemIndex { index: 128 })
2263 );
2264 assert!(linked_chunk.updates().unwrap().take().is_empty());
2265 }
2266
2267 {
2269 linked_chunk.push_gap_back(());
2271 assert_items_eq!(
2272 linked_chunk,
2273 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2274 );
2275 assert_eq!(
2276 linked_chunk.updates().unwrap().take(),
2277 &[NewGapChunk {
2278 previous: Some(ChunkIdentifier(3)),
2279 new: ChunkIdentifier(6),
2280 next: None,
2281 gap: ()
2282 }]
2283 );
2284
2285 assert_matches!(
2286 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(6), 0)),
2287 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2288 );
2289 }
2290
2291 assert_eq!(linked_chunk.num_items(), 18);
2292
2293 Ok(())
2294 }
2295
2296 #[test]
2297 fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2298 use super::Update::*;
2299
2300 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2301
2302 let _ = linked_chunk.updates().unwrap().take();
2304
2305 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2306 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2307 assert_eq!(
2308 linked_chunk.updates().unwrap().take(),
2309 &[
2310 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2311 NewItemsChunk {
2312 previous: Some(ChunkIdentifier(0)),
2313 new: ChunkIdentifier(1),
2314 next: None,
2315 },
2316 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2317 ]
2318 );
2319
2320 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2322
2323 linked_chunk.insert_items_at(['w', 'x', 'y', 'z'], position_of_e)?;
2326
2327 assert_items_eq!(
2328 linked_chunk,
2329 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2330 );
2331 assert_eq!(linked_chunk.num_items(), 10);
2332 assert_eq!(
2333 linked_chunk.updates().unwrap().take(),
2334 &[
2335 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2336 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2337 NewItemsChunk {
2338 previous: Some(ChunkIdentifier(1)),
2339 new: ChunkIdentifier(2),
2340 next: None,
2341 },
2342 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2343 StartReattachItems,
2344 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2345 NewItemsChunk {
2346 previous: Some(ChunkIdentifier(2)),
2347 new: ChunkIdentifier(3),
2348 next: None,
2349 },
2350 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2351 EndReattachItems,
2352 ]
2353 );
2354
2355 Ok(())
2356 }
2357
2358 #[test]
2359 fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2360 use super::Update::*;
2361
2362 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2363
2364 let _ = linked_chunk.updates().unwrap().take();
2366
2367 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2368 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2369 assert_eq!(
2370 linked_chunk.updates().unwrap().take(),
2371 &[
2372 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2373 NewItemsChunk {
2374 previous: Some(ChunkIdentifier(0)),
2375 new: ChunkIdentifier(1),
2376 next: None,
2377 },
2378 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2379 ]
2380 );
2381
2382 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2384 linked_chunk.insert_items_at(['l', 'm', 'n', 'o'], position_of_a)?;
2385
2386 assert_items_eq!(
2387 linked_chunk,
2388 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'e', 'f']
2389 );
2390 assert_eq!(linked_chunk.num_items(), 10);
2391 assert_eq!(
2392 linked_chunk.updates().unwrap().take(),
2393 &[
2394 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2395 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2396 NewItemsChunk {
2397 previous: Some(ChunkIdentifier(0)),
2398 new: ChunkIdentifier(2),
2399 next: Some(ChunkIdentifier(1)),
2400 },
2401 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2402 StartReattachItems,
2403 PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2404 NewItemsChunk {
2405 previous: Some(ChunkIdentifier(2)),
2406 new: ChunkIdentifier(3),
2407 next: Some(ChunkIdentifier(1)),
2408 },
2409 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2410 EndReattachItems,
2411 ]
2412 );
2413
2414 Ok(())
2415 }
2416
2417 #[test]
2418 fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2419 use super::Update::*;
2420
2421 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2422
2423 let _ = linked_chunk.updates().unwrap().take();
2425
2426 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2427 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2428 assert_eq!(
2429 linked_chunk.updates().unwrap().take(),
2430 &[
2431 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2432 NewItemsChunk {
2433 previous: Some(ChunkIdentifier(0)),
2434 new: ChunkIdentifier(1),
2435 next: None,
2436 },
2437 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2438 NewItemsChunk {
2439 previous: Some(ChunkIdentifier(1)),
2440 new: ChunkIdentifier(2),
2441 next: None,
2442 },
2443 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2444 ]
2445 );
2446
2447 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2448 linked_chunk.insert_items_at(['r', 's'], position_of_d)?;
2449
2450 assert_items_eq!(
2451 linked_chunk,
2452 ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2453 );
2454 assert_eq!(linked_chunk.num_items(), 10);
2455 assert_eq!(
2456 linked_chunk.updates().unwrap().take(),
2457 &[
2458 DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2459 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2460 StartReattachItems,
2461 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2462 NewItemsChunk {
2463 previous: Some(ChunkIdentifier(1)),
2464 new: ChunkIdentifier(3),
2465 next: Some(ChunkIdentifier(2)),
2466 },
2467 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2468 EndReattachItems,
2469 ]
2470 );
2471
2472 Ok(())
2473 }
2474
2475 #[test]
2476 fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2477 use super::Update::*;
2478
2479 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2480
2481 let _ = linked_chunk.updates().unwrap().take();
2483
2484 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2485 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2486 assert_eq!(
2487 linked_chunk.updates().unwrap().take(),
2488 &[
2489 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2490 NewItemsChunk {
2491 previous: Some(ChunkIdentifier(0)),
2492 new: ChunkIdentifier(1),
2493 next: None,
2494 },
2495 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2496 ]
2497 );
2498
2499 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2501 let position_after_e =
2502 Position(position_of_e.chunk_identifier(), position_of_e.index() + 1);
2503
2504 linked_chunk.insert_items_at(['p', 'q'], position_after_e)?;
2505 assert_items_eq!(
2506 linked_chunk,
2507 ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2508 );
2509 assert_eq!(
2510 linked_chunk.updates().unwrap().take(),
2511 &[
2512 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2513 NewItemsChunk {
2514 previous: Some(ChunkIdentifier(1)),
2515 new: ChunkIdentifier(2),
2516 next: None
2517 },
2518 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2519 ]
2520 );
2521 assert_eq!(linked_chunk.num_items(), 7);
2522
2523 Ok(())
2524 }
2525
2526 #[test]
2527 fn test_insert_items_at_errs() -> Result<(), Error> {
2528 use super::Update::*;
2529
2530 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2531
2532 let _ = linked_chunk.updates().unwrap().take();
2534
2535 linked_chunk.push_items_back(['a', 'b', 'c']);
2536 linked_chunk.push_gap_back(());
2537 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2538 assert_eq!(
2539 linked_chunk.updates().unwrap().take(),
2540 &[
2541 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2542 NewGapChunk {
2543 previous: Some(ChunkIdentifier(0)),
2544 new: ChunkIdentifier(1),
2545 next: None,
2546 gap: (),
2547 },
2548 ]
2549 );
2550
2551 {
2553 assert_matches!(
2554 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)),
2555 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2556 );
2557 assert!(linked_chunk.updates().unwrap().take().is_empty());
2558 }
2559
2560 {
2562 assert_matches!(
2563 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)),
2564 Err(Error::InvalidItemIndex { index: 128 })
2565 );
2566 assert!(linked_chunk.updates().unwrap().take().is_empty());
2567 }
2568
2569 {
2571 assert_matches!(
2572 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(1), 0)),
2573 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2574 );
2575 }
2576
2577 Ok(())
2578 }
2579
2580 #[test]
2581 fn test_remove_item_at() -> Result<(), Error> {
2582 use super::Update::*;
2583
2584 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2585
2586 let _ = linked_chunk.updates().unwrap().take();
2588
2589 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2590 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2591 assert_eq!(linked_chunk.num_items(), 11);
2592
2593 let _ = linked_chunk.updates().unwrap().take();
2595
2596 {
2599 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2600 let removed_item = linked_chunk.remove_item_at(position_of_f)?;
2601
2602 assert_eq!(removed_item, 'f');
2603 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2604 assert_eq!(linked_chunk.num_items(), 10);
2605
2606 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2607 let removed_item = linked_chunk.remove_item_at(position_of_e)?;
2608
2609 assert_eq!(removed_item, 'e');
2610 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2611 assert_eq!(linked_chunk.num_items(), 9);
2612
2613 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2614 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2615
2616 assert_eq!(removed_item, 'd');
2617 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2618 assert_eq!(linked_chunk.num_items(), 8);
2619
2620 assert_eq!(
2621 linked_chunk.updates().unwrap().take(),
2622 &[
2623 RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2624 RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2625 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2626 RemoveChunk(ChunkIdentifier(1)),
2627 ]
2628 );
2629 }
2630
2631 {
2634 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2635 let removed_item = linked_chunk.remove_item_at(first_position)?;
2636
2637 assert_eq!(removed_item, 'a');
2638 assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2639 assert_eq!(linked_chunk.num_items(), 7);
2640
2641 let removed_item = linked_chunk.remove_item_at(first_position)?;
2642
2643 assert_eq!(removed_item, 'b');
2644 assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2645 assert_eq!(linked_chunk.num_items(), 6);
2646
2647 let removed_item = linked_chunk.remove_item_at(first_position)?;
2648
2649 assert_eq!(removed_item, 'c');
2650 assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2651 assert_eq!(linked_chunk.num_items(), 5);
2652
2653 assert_eq!(
2654 linked_chunk.updates().unwrap().take(),
2655 &[
2656 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2657 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2658 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2659 ]
2660 );
2661 }
2662
2663 {
2666 let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2667 let removed_item = linked_chunk.remove_item_at(first_position)?;
2668
2669 assert_eq!(removed_item, 'g');
2670 assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2671 assert_eq!(linked_chunk.num_items(), 4);
2672
2673 let removed_item = linked_chunk.remove_item_at(first_position)?;
2674
2675 assert_eq!(removed_item, 'h');
2676 assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2677 assert_eq!(linked_chunk.num_items(), 3);
2678
2679 let removed_item = linked_chunk.remove_item_at(first_position)?;
2680
2681 assert_eq!(removed_item, 'i');
2682 assert_items_eq!(linked_chunk, [] ['j', 'k']);
2683 assert_eq!(linked_chunk.num_items(), 2);
2684
2685 assert_eq!(
2686 linked_chunk.updates().unwrap().take(),
2687 &[
2688 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2689 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2690 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2691 RemoveChunk(ChunkIdentifier(2)),
2692 ]
2693 );
2694 }
2695
2696 {
2699 let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2700 let removed_item = linked_chunk.remove_item_at(position_of_k)?;
2701
2702 assert_eq!(removed_item, 'k');
2703 #[rustfmt::skip]
2704 assert_items_eq!(linked_chunk, [] ['j']);
2705 assert_eq!(linked_chunk.num_items(), 1);
2706
2707 let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2708 let removed_item = linked_chunk.remove_item_at(position_of_j)?;
2709
2710 assert_eq!(removed_item, 'j');
2711 assert_items_eq!(linked_chunk, []);
2712 assert_eq!(linked_chunk.num_items(), 0);
2713
2714 assert_eq!(
2715 linked_chunk.updates().unwrap().take(),
2716 &[
2717 RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2718 RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2719 RemoveChunk(ChunkIdentifier(3)),
2720 ]
2721 );
2722 }
2723
2724 {
2726 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2727
2728 #[rustfmt::skip]
2729 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2730 assert_eq!(linked_chunk.num_items(), 4);
2731
2732 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2733 linked_chunk.insert_gap_at((), position_of_c)?;
2734
2735 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2736 assert_eq!(linked_chunk.num_items(), 4);
2737
2738 let _ = linked_chunk.updates().unwrap().take();
2740
2741 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2742 let removed_item = linked_chunk.remove_item_at(position_of_c)?;
2743
2744 assert_eq!(removed_item, 'c');
2745 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2746 assert_eq!(linked_chunk.num_items(), 3);
2747
2748 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2749 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2750
2751 assert_eq!(removed_item, 'd');
2752 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2753 assert_eq!(linked_chunk.num_items(), 2);
2754
2755 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2756 let removed_item = linked_chunk.remove_item_at(first_position)?;
2757
2758 assert_eq!(removed_item, 'a');
2759 assert_items_eq!(linked_chunk, ['b'] [-]);
2760 assert_eq!(linked_chunk.num_items(), 1);
2761
2762 let removed_item = linked_chunk.remove_item_at(first_position)?;
2763
2764 assert_eq!(removed_item, 'b');
2765 assert_items_eq!(linked_chunk, [] [-]);
2766 assert_eq!(linked_chunk.num_items(), 0);
2767
2768 assert_eq!(
2769 linked_chunk.updates().unwrap().take(),
2770 &[
2771 RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2772 RemoveChunk(ChunkIdentifier(6)),
2773 RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2774 RemoveChunk(ChunkIdentifier(4)),
2775 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2776 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2777 ]
2778 );
2779 }
2780
2781 Ok(())
2782 }
2783
2784 #[test]
2785 fn test_insert_gap_at() -> Result<(), Error> {
2786 use super::Update::*;
2787
2788 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2789
2790 let _ = linked_chunk.updates().unwrap().take();
2792
2793 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2794 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2795 assert_eq!(
2796 linked_chunk.updates().unwrap().take(),
2797 &[
2798 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2799 NewItemsChunk {
2800 previous: Some(ChunkIdentifier(0)),
2801 new: ChunkIdentifier(1),
2802 next: None
2803 },
2804 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2805 ]
2806 );
2807
2808 {
2810 let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
2811 linked_chunk.insert_gap_at((), position_of_b)?;
2812
2813 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2814 assert_eq!(
2815 linked_chunk.updates().unwrap().take(),
2816 &[
2817 DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
2818 NewGapChunk {
2819 previous: Some(ChunkIdentifier(0)),
2820 new: ChunkIdentifier(2),
2821 next: Some(ChunkIdentifier(1)),
2822 gap: (),
2823 },
2824 StartReattachItems,
2825 NewItemsChunk {
2826 previous: Some(ChunkIdentifier(2)),
2827 new: ChunkIdentifier(3),
2828 next: Some(ChunkIdentifier(1)),
2829 },
2830 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
2831 EndReattachItems,
2832 ]
2833 );
2834 }
2835
2836 {
2839 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2840 linked_chunk.insert_gap_at((), position_of_a)?;
2841
2842 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2845 assert_eq!(
2846 linked_chunk.updates().unwrap().take(),
2847 &[NewGapChunk {
2848 previous: None,
2849 new: ChunkIdentifier(4),
2850 next: Some(ChunkIdentifier(0)),
2851 gap: (),
2852 },]
2853 );
2854 }
2855
2856 {
2859 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2860 linked_chunk.insert_gap_at((), position_of_d)?;
2861
2862 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
2866 assert_eq!(
2867 linked_chunk.updates().unwrap().take(),
2868 &[NewGapChunk {
2869 previous: Some(ChunkIdentifier(3)),
2870 new: ChunkIdentifier(5),
2871 next: Some(ChunkIdentifier(1)),
2872 gap: (),
2873 }]
2874 );
2875 }
2876
2877 {
2879 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
2881 let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
2882
2883 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
2884
2885 assert_eq!(
2886 linked_chunk.updates().unwrap().take(),
2887 &[
2888 NewItemsChunk {
2889 previous: Some(ChunkIdentifier(5)),
2890 new: ChunkIdentifier(6),
2891 next: Some(ChunkIdentifier(1)),
2892 },
2893 RemoveChunk(ChunkIdentifier(5)),
2894 ]
2895 );
2896
2897 linked_chunk.insert_gap_at((), position)?;
2898
2899 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
2900 assert_eq!(
2901 linked_chunk.updates().unwrap().take(),
2902 &[NewGapChunk {
2903 previous: Some(ChunkIdentifier(3)),
2904 new: ChunkIdentifier(7),
2905 next: Some(ChunkIdentifier(6)),
2906 gap: (),
2907 }]
2908 );
2909 }
2910
2911 {
2913 assert_matches!(
2914 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)),
2915 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2916 );
2917 assert!(linked_chunk.updates().unwrap().take().is_empty());
2918 }
2919
2920 {
2922 assert_matches!(
2923 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)),
2924 Err(Error::InvalidItemIndex { index: 128 })
2925 );
2926 assert!(linked_chunk.updates().unwrap().take().is_empty());
2927 }
2928
2929 {
2931 let position_of_a_gap = Position(ChunkIdentifier(2), 0);
2934 assert_matches!(
2935 linked_chunk.insert_gap_at((), position_of_a_gap),
2936 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
2937 );
2938 assert!(linked_chunk.updates().unwrap().take().is_empty());
2939 }
2940
2941 assert_eq!(linked_chunk.num_items(), 6);
2942
2943 Ok(())
2944 }
2945
2946 #[test]
2947 fn test_replace_gap_at_middle() -> Result<(), Error> {
2948 use super::Update::*;
2949
2950 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2951
2952 let _ = linked_chunk.updates().unwrap().take();
2954
2955 linked_chunk.push_items_back(['a', 'b']);
2956 linked_chunk.push_gap_back(());
2957 linked_chunk.push_items_back(['l', 'm']);
2958 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
2959 assert_eq!(
2960 linked_chunk.updates().unwrap().take(),
2961 &[
2962 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
2963 NewGapChunk {
2964 previous: Some(ChunkIdentifier(0)),
2965 new: ChunkIdentifier(1),
2966 next: None,
2967 gap: (),
2968 },
2969 NewItemsChunk {
2970 previous: Some(ChunkIdentifier(1)),
2971 new: ChunkIdentifier(2),
2972 next: None,
2973 },
2974 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
2975 ]
2976 );
2977
2978 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
2980 assert_eq!(gap_identifier, ChunkIdentifier(1));
2981
2982 let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
2983 assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
2984 assert_items_eq!(
2985 linked_chunk,
2986 ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
2987 );
2988 assert_eq!(
2989 linked_chunk.updates().unwrap().take(),
2990 &[
2991 NewItemsChunk {
2992 previous: Some(ChunkIdentifier(1)),
2993 new: ChunkIdentifier(3),
2994 next: Some(ChunkIdentifier(2)),
2995 },
2996 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
2997 NewItemsChunk {
2998 previous: Some(ChunkIdentifier(3)),
2999 new: ChunkIdentifier(4),
3000 next: Some(ChunkIdentifier(2)),
3001 },
3002 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
3003 RemoveChunk(ChunkIdentifier(1)),
3004 ]
3005 );
3006
3007 assert_eq!(linked_chunk.num_items(), 9);
3008
3009 Ok(())
3010 }
3011
3012 #[test]
3013 fn test_replace_gap_at_end() -> Result<(), Error> {
3014 use super::Update::*;
3015
3016 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3017
3018 let _ = linked_chunk.updates().unwrap().take();
3020
3021 linked_chunk.push_items_back(['a', 'b']);
3022 linked_chunk.push_gap_back(());
3023 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3024 assert_eq!(
3025 linked_chunk.updates().unwrap().take(),
3026 &[
3027 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3028 NewGapChunk {
3029 previous: Some(ChunkIdentifier(0)),
3030 new: ChunkIdentifier(1),
3031 next: None,
3032 gap: (),
3033 },
3034 ]
3035 );
3036
3037 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3039 assert_eq!(gap_identifier, ChunkIdentifier(1));
3040
3041 let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3042 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3043 assert_items_eq!(
3044 linked_chunk,
3045 ['a', 'b'] ['w', 'x', 'y'] ['z']
3046 );
3047 assert_eq!(
3048 linked_chunk.updates().unwrap().take(),
3049 &[
3050 NewItemsChunk {
3051 previous: Some(ChunkIdentifier(1)),
3052 new: ChunkIdentifier(2),
3053 next: None,
3054 },
3055 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3056 NewItemsChunk {
3057 previous: Some(ChunkIdentifier(2)),
3058 new: ChunkIdentifier(3),
3059 next: None,
3060 },
3061 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3062 RemoveChunk(ChunkIdentifier(1)),
3063 ]
3064 );
3065
3066 assert_eq!(linked_chunk.num_items(), 6);
3067
3068 Ok(())
3069 }
3070
3071 #[test]
3072 fn test_replace_gap_at_beginning() -> Result<(), Error> {
3073 use super::Update::*;
3074
3075 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3076
3077 let _ = linked_chunk.updates().unwrap().take();
3079
3080 linked_chunk.push_items_back(['a', 'b']);
3081 assert_items_eq!(linked_chunk, ['a', 'b']);
3082 assert_eq!(
3083 linked_chunk.updates().unwrap().take(),
3084 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },]
3085 );
3086
3087 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3089 linked_chunk.insert_gap_at((), position_of_a).unwrap();
3090 assert_items_eq!(
3091 linked_chunk,
3092 [-] ['a', 'b']
3093 );
3094 assert_eq!(
3095 linked_chunk.updates().unwrap().take(),
3096 &[NewGapChunk {
3097 previous: None,
3098 new: ChunkIdentifier(1),
3099 next: Some(ChunkIdentifier(0)),
3100 gap: (),
3101 }]
3102 );
3103
3104 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3105 assert_eq!(gap_identifier, ChunkIdentifier(1));
3106
3107 let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3108 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3109 assert_items_eq!(
3110 linked_chunk,
3111 ['x'] ['a', 'b']
3112 );
3113 assert_eq!(
3114 linked_chunk.updates().unwrap().take(),
3115 &[
3116 NewItemsChunk {
3117 previous: Some(ChunkIdentifier(1)),
3118 new: ChunkIdentifier(2),
3119 next: Some(ChunkIdentifier(0)),
3120 },
3121 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3122 RemoveChunk(ChunkIdentifier(1)),
3123 ]
3124 );
3125
3126 assert_eq!(linked_chunk.num_items(), 3);
3127
3128 Ok(())
3129 }
3130
3131 #[test]
3132 fn test_remove_empty_chunk_at() -> Result<(), Error> {
3133 use super::Update::*;
3134
3135 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3136
3137 let _ = linked_chunk.updates().unwrap().take();
3139
3140 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3141 linked_chunk.push_items_back(['a', 'b']);
3142 linked_chunk.push_gap_back(());
3143 linked_chunk.push_items_back(['l', 'm']);
3144 linked_chunk.push_gap_back(());
3145 assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3146 assert_eq!(
3147 linked_chunk.updates().unwrap().take(),
3148 &[
3149 NewGapChunk {
3150 previous: None,
3151 new: ChunkIdentifier(1),
3152 next: Some(ChunkIdentifier(0)),
3153 gap: (),
3154 },
3155 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3156 NewGapChunk {
3157 previous: Some(ChunkIdentifier(0)),
3158 new: ChunkIdentifier(2),
3159 next: None,
3160 gap: (),
3161 },
3162 NewItemsChunk {
3163 previous: Some(ChunkIdentifier(2)),
3164 new: ChunkIdentifier(3),
3165 next: None,
3166 },
3167 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3168 NewGapChunk {
3169 previous: Some(ChunkIdentifier(3)),
3170 new: ChunkIdentifier(4),
3171 next: None,
3172 gap: (),
3173 },
3174 ]
3175 );
3176
3177 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3179 assert_matches!(err, Error::RemovingNonEmptyItemsChunk { .. });
3180
3181 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(42)).unwrap_err();
3183 assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3184
3185 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(2)).unwrap();
3187 let next = maybe_next.unwrap();
3188 assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3190 assert_eq!(next.index(), 0);
3191 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3192 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3193
3194 let next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(4)).unwrap();
3196 assert!(next.is_none());
3198 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3199 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3200
3201 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(1)).unwrap();
3203 let next = maybe_next.unwrap();
3204 assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3205 assert_eq!(next.index(), 0);
3206 assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3207 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3208
3209 Ok(())
3210 }
3211
3212 #[test]
3213 fn test_remove_empty_last_chunk() {
3214 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3215
3216 let _ = linked_chunk.updates().unwrap().take();
3218
3219 assert_items_eq!(linked_chunk, []);
3220 assert!(linked_chunk.updates().unwrap().take().is_empty());
3221
3222 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3224 assert_matches!(err, Error::RemovingLastChunk);
3225 }
3226
3227 #[test]
3228 fn test_chunk_item_positions() {
3229 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3230 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3231 linked_chunk.push_gap_back(());
3232 linked_chunk.push_items_back(['f']);
3233
3234 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3235
3236 let mut iterator = linked_chunk.chunks();
3237
3238 {
3240 let chunk = iterator.next().unwrap();
3241 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3242 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3243 }
3244
3245 {
3247 let chunk = iterator.next().unwrap();
3248 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3249 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3250 }
3251
3252 {
3254 let chunk = iterator.next().unwrap();
3255 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3256 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3257 }
3258
3259 {
3261 let chunk = iterator.next().unwrap();
3262 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3263 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3264 }
3265 }
3266
3267 #[test]
3268 fn test_is_first_and_last_chunk() {
3269 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3270
3271 let mut chunks = linked_chunk.chunks().peekable();
3272 assert!(chunks.peek().unwrap().is_first_chunk());
3273 assert!(chunks.next().unwrap().is_last_chunk());
3274 assert!(chunks.next().is_none());
3275
3276 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3277
3278 let mut chunks = linked_chunk.chunks().peekable();
3279 assert!(chunks.next().unwrap().is_first_chunk());
3280 assert!(chunks.peek().unwrap().is_first_chunk().not());
3281 assert!(chunks.next().unwrap().is_last_chunk().not());
3282 assert!(chunks.next().unwrap().is_last_chunk());
3283 assert!(chunks.next().is_none());
3284 }
3285
3286 #[test]
3291 fn test_clear() {
3292 let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3293
3294 let item = Arc::new('a');
3295 let gap = Arc::new(());
3296
3297 linked_chunk.push_items_back([
3298 item.clone(),
3299 item.clone(),
3300 item.clone(),
3301 item.clone(),
3302 item.clone(),
3303 ]);
3304 linked_chunk.push_gap_back(gap.clone());
3305 linked_chunk.push_items_back([item.clone()]);
3306
3307 assert_eq!(Arc::strong_count(&item), 7);
3308 assert_eq!(Arc::strong_count(&gap), 2);
3309 assert_eq!(linked_chunk.num_items(), 6);
3310 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3311
3312 linked_chunk.clear();
3314
3315 assert_eq!(Arc::strong_count(&item), 1);
3316 assert_eq!(Arc::strong_count(&gap), 1);
3317 assert_eq!(linked_chunk.num_items(), 0);
3318 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3319 }
3320
3321 #[test]
3322 fn test_clear_emit_an_update_clear() {
3323 use super::Update::*;
3324
3325 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3326
3327 assert_eq!(
3328 linked_chunk.updates().unwrap().take(),
3329 &[NewItemsChunk {
3330 previous: None,
3331 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3332 next: None
3333 }]
3334 );
3335
3336 linked_chunk.clear();
3337
3338 assert_eq!(
3339 linked_chunk.updates().unwrap().take(),
3340 &[
3341 Clear,
3342 NewItemsChunk {
3343 previous: None,
3344 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3345 next: None
3346 }
3347 ]
3348 );
3349 }
3350
3351 #[test]
3352 fn test_replace_item() {
3353 use super::Update::*;
3354
3355 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3356
3357 linked_chunk.push_items_back(['a', 'b', 'c']);
3358 linked_chunk.push_gap_back(());
3359 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3361
3362 let _ = linked_chunk.updates().unwrap().take();
3364
3365 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3367 assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3368
3369 assert_eq!(
3370 linked_chunk.updates().unwrap().take(),
3371 &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3372 );
3373
3374 assert_matches!(
3376 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3377 Err(Error::InvalidItemIndex { index: 3 })
3378 );
3379
3380 assert_matches!(
3382 linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3383 Err(Error::ChunkIsAGap { .. })
3384 );
3385 }
3386
3387 #[test]
3388 fn test_lazy_previous() {
3389 use std::marker::PhantomData;
3390
3391 use super::{Ends, ObservableUpdates, Update::*};
3392
3393 let first_chunk_identifier = ChunkIdentifier(0);
3395 let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3396 unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3397
3398 let mut linked_chunk = LinkedChunk::<3, char, ()> {
3399 links: Ends { first: first_loaded_chunk, last: None },
3400 chunk_identifier_generator:
3401 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3402 updates: Some(ObservableUpdates::new()),
3403 marker: PhantomData,
3404 };
3405
3406 {
3409 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3410
3411 assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3412
3413 {
3415 let mut chunks = linked_chunk.chunks();
3416
3417 assert_matches!(chunks.next(), Some(chunk) => {
3418 assert_eq!(chunk.identifier(), 1);
3419 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3420 });
3421 assert_matches!(chunks.next(), Some(chunk) => {
3422 assert_eq!(chunk.identifier(), 2);
3423 assert!(chunk.lazy_previous.is_none());
3424 });
3425 assert!(chunks.next().is_none());
3426 }
3427
3428 assert_eq!(
3430 linked_chunk.updates().unwrap().take(),
3431 &[
3432 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3433 NewItemsChunk {
3434 previous: Some(ChunkIdentifier(1)),
3435 new: ChunkIdentifier(2),
3436 next: None,
3437 },
3438 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3439 ]
3440 );
3441 }
3442
3443 {
3445 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3446
3447 assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3448
3449 {
3451 let mut chunks = linked_chunk.chunks();
3452
3453 assert_matches!(chunks.next(), Some(chunk) => {
3454 assert_eq!(chunk.identifier(), 3);
3455 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3457 });
3458 assert_matches!(chunks.next(), Some(chunk) => {
3459 assert_eq!(chunk.identifier(), 1);
3460 assert!(chunk.lazy_previous.is_none());
3462 });
3463 assert_matches!(chunks.next(), Some(chunk) => {
3464 assert_eq!(chunk.identifier(), 2);
3465 assert!(chunk.lazy_previous.is_none());
3466 });
3467 assert!(chunks.next().is_none());
3468 }
3469
3470 assert_eq!(
3472 linked_chunk.updates().unwrap().take(),
3473 &[NewGapChunk {
3474 previous: Some(ChunkIdentifier(0)),
3476 new: ChunkIdentifier(3),
3477 next: Some(ChunkIdentifier(1)),
3478 gap: ()
3479 }]
3480 );
3481 }
3482
3483 {
3485 linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3486
3487 assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3488
3489 {
3491 let mut chunks = linked_chunk.chunks();
3492
3493 assert_matches!(chunks.next(), Some(chunk) => {
3494 assert_eq!(chunk.identifier(), 4);
3495 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3497 });
3498 assert_matches!(chunks.next(), Some(chunk) => {
3499 assert_eq!(chunk.identifier(), 5);
3500 assert!(chunk.lazy_previous.is_none());
3501 });
3502 assert_matches!(chunks.next(), Some(chunk) => {
3503 assert_eq!(chunk.identifier(), 1);
3504 assert!(chunk.lazy_previous.is_none());
3505 });
3506 assert_matches!(chunks.next(), Some(chunk) => {
3507 assert_eq!(chunk.identifier(), 2);
3508 assert!(chunk.lazy_previous.is_none());
3509 });
3510 assert!(chunks.next().is_none());
3511 }
3512
3513 assert_eq!(
3515 linked_chunk.updates().unwrap().take(),
3516 &[
3517 NewItemsChunk {
3519 previous: Some(ChunkIdentifier(3)),
3520 new: ChunkIdentifier(4),
3521 next: Some(ChunkIdentifier(1)),
3522 },
3523 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3525 NewItemsChunk {
3527 previous: Some(ChunkIdentifier(4)),
3528 new: ChunkIdentifier(5),
3529 next: Some(ChunkIdentifier(1)),
3530 },
3531 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3533 RemoveChunk(ChunkIdentifier(3)),
3535 ]
3536 );
3537 }
3538
3539 {
3543 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3544
3545 assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3546
3547 {
3549 let mut chunks = linked_chunk.chunks();
3550
3551 assert_matches!(chunks.next(), Some(chunk) => {
3552 assert_eq!(chunk.identifier(), 6);
3553 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3555 });
3556 assert_matches!(chunks.next(), Some(chunk) => {
3557 assert_eq!(chunk.identifier(), 4);
3558 assert!(chunk.lazy_previous.is_none());
3560 });
3561 assert_matches!(chunks.next(), Some(chunk) => {
3562 assert_eq!(chunk.identifier(), 5);
3563 assert!(chunk.lazy_previous.is_none());
3564 });
3565 assert_matches!(chunks.next(), Some(chunk) => {
3566 assert_eq!(chunk.identifier(), 1);
3567 assert!(chunk.lazy_previous.is_none());
3568 });
3569 assert_matches!(chunks.next(), Some(chunk) => {
3570 assert_eq!(chunk.identifier(), 2);
3571 assert!(chunk.lazy_previous.is_none());
3572 });
3573 assert!(chunks.next().is_none());
3574 }
3575
3576 assert_eq!(
3578 linked_chunk.updates().unwrap().take(),
3579 &[NewGapChunk {
3580 previous: Some(ChunkIdentifier(0)),
3582 new: ChunkIdentifier(6),
3583 next: Some(ChunkIdentifier(4)),
3584 gap: ()
3585 }]
3586 );
3587 }
3588 }
3589}