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::{self, Display},
101 marker::PhantomData,
102 ptr::NonNull,
103 sync::atomic::{self, AtomicU64},
104};
105
106pub use as_vector::*;
107use ruma::{OwnedRoomId, RoomId};
108pub use updates::*;
109
110#[derive(Debug, Clone, Copy, PartialEq)]
112pub enum LinkedChunkId<'a> {
113 Room(&'a RoomId),
114 }
117
118impl LinkedChunkId<'_> {
119 pub fn storage_key(&self) -> impl '_ + AsRef<[u8]> {
120 match self {
121 LinkedChunkId::Room(room_id) => room_id,
122 }
123 }
124
125 pub fn to_owned(&self) -> OwnedLinkedChunkId {
126 match self {
127 LinkedChunkId::Room(room_id) => OwnedLinkedChunkId::Room((*room_id).to_owned()),
128 }
129 }
130
131 pub fn room_id(&self) -> &RoomId {
132 match self {
133 LinkedChunkId::Room(room_id) => room_id,
134 }
135 }
136}
137
138impl PartialEq<&OwnedLinkedChunkId> for LinkedChunkId<'_> {
139 fn eq(&self, other: &&OwnedLinkedChunkId) -> bool {
140 match (self, other) {
141 (LinkedChunkId::Room(a), OwnedLinkedChunkId::Room(b)) => *a == b,
142 }
143 }
144}
145
146impl PartialEq<LinkedChunkId<'_>> for OwnedLinkedChunkId {
147 fn eq(&self, other: &LinkedChunkId<'_>) -> bool {
148 other.eq(&self)
149 }
150}
151
152#[derive(Clone, Debug, PartialEq, Eq, Hash)]
154pub enum OwnedLinkedChunkId {
155 Room(OwnedRoomId),
156 }
159
160impl Display for OwnedLinkedChunkId {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 match self {
163 OwnedLinkedChunkId::Room(room_id) => write!(f, "{room_id}"),
164 }
165 }
166}
167
168impl OwnedLinkedChunkId {
169 fn as_ref(&self) -> LinkedChunkId<'_> {
170 match self {
171 OwnedLinkedChunkId::Room(room_id) => LinkedChunkId::Room(room_id.as_ref()),
172 }
173 }
174
175 pub fn room_id(&self) -> &RoomId {
176 match self {
177 OwnedLinkedChunkId::Room(room_id) => room_id,
178 }
179 }
180}
181
182#[derive(thiserror::Error, Debug)]
184pub enum Error {
185 #[error("The chunk identifier is invalid: `{identifier:?}`")]
187 InvalidChunkIdentifier {
188 identifier: ChunkIdentifier,
190 },
191
192 #[error("The chunk is a gap: `{identifier:?}`")]
194 ChunkIsAGap {
195 identifier: ChunkIdentifier,
197 },
198
199 #[error("The chunk is an item: `{identifier:?}`")]
201 ChunkIsItems {
202 identifier: ChunkIdentifier,
204 },
205
206 #[error("The chunk is a non-empty item chunk: `{identifier:?}`")]
208 RemovingNonEmptyItemsChunk {
209 identifier: ChunkIdentifier,
211 },
212
213 #[error("Trying to remove the only chunk, but a linked chunk can't be empty")]
216 RemovingLastChunk,
217
218 #[error("The item index is invalid: `{index}`")]
220 InvalidItemIndex {
221 index: usize,
223 },
224}
225
226struct Ends<const CHUNK_CAPACITY: usize, Item, Gap> {
231 first: NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>,
233 last: Option<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
235}
236
237impl<const CAP: usize, Item, Gap> Ends<CAP, Item, Gap> {
238 fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
240 unsafe { self.first.as_ref() }
241 }
242
243 fn first_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
245 unsafe { self.first.as_mut() }
246 }
247
248 fn latest_chunk(&self) -> &Chunk<CAP, Item, Gap> {
250 unsafe { self.last.unwrap_or(self.first).as_ref() }
251 }
252
253 fn latest_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
255 unsafe { self.last.as_mut().unwrap_or(&mut self.first).as_mut() }
256 }
257
258 fn chunk(&self, identifier: ChunkIdentifier) -> Option<&Chunk<CAP, Item, Gap>> {
260 let mut chunk = self.latest_chunk();
261
262 loop {
263 if chunk.identifier() == identifier {
264 return Some(chunk);
265 }
266
267 chunk = chunk.previous()?;
268 }
269 }
270
271 fn chunk_mut(&mut self, identifier: ChunkIdentifier) -> Option<&mut Chunk<CAP, Item, Gap>> {
273 let mut chunk = self.latest_chunk_mut();
274
275 loop {
276 if chunk.identifier() == identifier {
277 return Some(chunk);
278 }
279
280 chunk = chunk.previous_mut()?;
281 }
282 }
283
284 unsafe fn clear(&mut self) {
291 let mut current_chunk_ptr = self.last.or(Some(self.first));
294
295 while let Some(chunk_ptr) = current_chunk_ptr {
297 let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
299
300 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
302
303 current_chunk_ptr = previous_ptr;
305 }
306
307 self.first = NonNull::dangling();
309 self.last = None;
310 }
311
312 fn replace_with(&mut self, first_chunk: NonNull<Chunk<CAP, Item, Gap>>) {
315 unsafe {
317 self.clear();
318 }
319
320 self.first = first_chunk;
322 }
323
324 fn reset(&mut self) {
329 self.replace_with(Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER));
330 }
331}
332
333pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
340 links: Ends<CHUNK_CAPACITY, Item, Gap>,
342
343 chunk_identifier_generator: ChunkIdentifierGenerator,
345
346 updates: Option<ObservableUpdates<Item, Gap>>,
350
351 marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
353}
354
355impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
356 fn default() -> Self {
357 Self::new()
358 }
359}
360
361impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
362 pub fn new() -> Self {
364 Self {
365 links: Ends {
366 first: Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER),
367 last: None,
368 },
369 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
370 updates: None,
371 marker: PhantomData,
372 }
373 }
374
375 pub fn new_with_update_history() -> Self {
381 let first_chunk_identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
382
383 let mut updates = ObservableUpdates::new();
384 updates.push(Update::NewItemsChunk {
385 previous: None,
386 new: first_chunk_identifier,
387 next: None,
388 });
389
390 Self {
391 links: Ends { first: Chunk::new_items_leaked(first_chunk_identifier), last: None },
392 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
393 updates: Some(updates),
394 marker: PhantomData,
395 }
396 }
397
398 pub fn clear(&mut self) {
400 self.links.reset();
402
403 self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
405
406 if let Some(updates) = self.updates.as_mut() {
408 updates.clear_pending();
411 updates.push(Update::Clear);
412 updates.push(Update::NewItemsChunk {
413 previous: None,
414 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
415 next: None,
416 })
417 }
418 }
419
420 pub fn push_items_back<I>(&mut self, items: I)
426 where
427 Item: Clone,
428 Gap: Clone,
429 I: IntoIterator<Item = Item>,
430 I::IntoIter: ExactSizeIterator,
431 {
432 let items = items.into_iter();
433
434 let last_chunk = self.links.latest_chunk_mut();
435
436 let last_chunk =
438 last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
439
440 debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
441
442 if !last_chunk.is_first_chunk() {
446 self.links.last = Some(last_chunk.as_ptr());
449 }
450 }
451
452 pub fn push_gap_back(&mut self, content: Gap)
455 where
456 Item: Clone,
457 Gap: Clone,
458 {
459 let last_chunk = self.links.latest_chunk_mut();
460 last_chunk.insert_next(
461 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
462 &mut self.updates,
463 );
464
465 self.links.last = last_chunk.next;
466 }
467
468 pub fn insert_items_at<I>(&mut self, items: I, position: Position) -> Result<(), Error>
473 where
474 Item: Clone,
475 Gap: Clone,
476 I: IntoIterator<Item = Item>,
477 I::IntoIter: ExactSizeIterator,
478 {
479 let chunk_identifier = position.chunk_identifier();
480 let item_index = position.index();
481
482 let chunk = self
483 .links
484 .chunk_mut(chunk_identifier)
485 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
486
487 let chunk = match &mut chunk.content {
488 ChunkContent::Gap(..) => {
489 return Err(Error::ChunkIsAGap { identifier: chunk_identifier })
490 }
491
492 ChunkContent::Items(current_items) => {
493 let current_items_length = current_items.len();
494
495 if item_index > current_items_length {
496 return Err(Error::InvalidItemIndex { index: item_index });
497 }
498
499 let items = items.into_iter();
501
502 if item_index == current_items_length {
504 chunk
505 .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
507 }
508 else {
510 if let Some(updates) = self.updates.as_mut() {
511 updates.push(Update::DetachLastItems {
512 at: Position(chunk_identifier, item_index),
513 });
514 }
515
516 let detached_items = current_items.split_off(item_index);
518
519 let chunk = chunk
520 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
522
523 if let Some(updates) = self.updates.as_mut() {
524 updates.push(Update::StartReattachItems);
525 }
526
527 let chunk = chunk
528 .push_items(
530 detached_items.into_iter(),
531 &self.chunk_identifier_generator,
532 &mut self.updates,
533 );
534
535 if let Some(updates) = self.updates.as_mut() {
536 updates.push(Update::EndReattachItems);
537 }
538
539 chunk
540 }
541 }
542 };
543
544 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
547 self.links.last = Some(chunk.as_ptr());
550 }
551
552 Ok(())
553 }
554
555 pub fn remove_item_at(&mut self, position: Position) -> Result<Item, Error> {
563 let chunk_identifier = position.chunk_identifier();
564 let item_index = position.index();
565
566 let mut chunk_ptr = None;
567 let removed_item;
568
569 {
570 let chunk = self
571 .links
572 .chunk_mut(chunk_identifier)
573 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
574
575 let can_unlink_chunk = match &mut chunk.content {
576 ChunkContent::Gap(..) => {
577 return Err(Error::ChunkIsAGap { identifier: chunk_identifier })
578 }
579
580 ChunkContent::Items(current_items) => {
581 let current_items_length = current_items.len();
582
583 if item_index > current_items_length {
584 return Err(Error::InvalidItemIndex { index: item_index });
585 }
586
587 removed_item = current_items.remove(item_index);
588
589 if let Some(updates) = self.updates.as_mut() {
590 updates
591 .push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
592 }
593
594 current_items.is_empty()
595 }
596 };
597
598 if can_unlink_chunk && !chunk.is_first_chunk() {
601 chunk.unlink(self.updates.as_mut());
603
604 chunk_ptr = Some(chunk.as_ptr());
605
606 if chunk.is_last_chunk() {
609 self.links.last = chunk.previous;
610 }
611 }
612
613 }
615
616 if let Some(chunk_ptr) = chunk_ptr {
617 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
624 }
625
626 Ok(removed_item)
627 }
628
629 pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
634 where
635 Item: Clone,
636 {
637 let chunk_identifier = position.chunk_identifier();
638 let item_index = position.index();
639
640 let chunk = self
641 .links
642 .chunk_mut(chunk_identifier)
643 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
644
645 match &mut chunk.content {
646 ChunkContent::Gap(..) => {
647 return Err(Error::ChunkIsAGap { identifier: chunk_identifier })
648 }
649
650 ChunkContent::Items(current_items) => {
651 if item_index >= current_items.len() {
652 return Err(Error::InvalidItemIndex { index: item_index });
653 }
654
655 if let Some(updates) = self.updates.as_mut() {
657 updates.push(Update::ReplaceItem {
658 at: Position(chunk_identifier, item_index),
659 item: item.clone(),
660 });
661 }
662
663 current_items[item_index] = item;
664 }
665 }
666
667 Ok(())
668 }
669
670 pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
675 where
676 Item: Clone,
677 Gap: Clone,
678 {
679 let chunk_identifier = position.chunk_identifier();
680 let item_index = position.index();
681
682 let chunk = self
683 .links
684 .chunk_mut(chunk_identifier)
685 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
686
687 let chunk = match &mut chunk.content {
688 ChunkContent::Gap(..) => {
689 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
690 }
691
692 ChunkContent::Items(current_items) => {
693 if item_index == 0 {
697 let chunk_was_first = chunk.is_first_chunk();
698 let chunk_was_last = chunk.is_last_chunk();
699
700 let new_chunk = chunk.insert_before(
701 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
702 self.updates.as_mut(),
703 );
704
705 let new_chunk_ptr = new_chunk.as_ptr();
706 let chunk_ptr = chunk.as_ptr();
707
708 if chunk_was_first {
713 self.links.first = new_chunk_ptr;
714
715 if chunk_was_last {
717 self.links.last = Some(chunk_ptr);
718 }
719 }
720
721 return Ok(());
722 }
723
724 let current_items_length = current_items.len();
725
726 if item_index >= current_items_length {
727 return Err(Error::InvalidItemIndex { index: item_index });
728 }
729
730 if let Some(updates) = self.updates.as_mut() {
731 updates.push(Update::DetachLastItems {
732 at: Position(chunk_identifier, item_index),
733 });
734 }
735
736 let detached_items = current_items.split_off(item_index);
738
739 let chunk = chunk
740 .insert_next(
742 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
743 &mut self.updates,
744 );
745
746 if let Some(updates) = self.updates.as_mut() {
747 updates.push(Update::StartReattachItems);
748 }
749
750 let chunk = chunk
751 .insert_next(
753 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
754 &mut self.updates,
755 )
756 .push_items(
758 detached_items.into_iter(),
759 &self.chunk_identifier_generator,
760 &mut self.updates,
761 );
762
763 if let Some(updates) = self.updates.as_mut() {
764 updates.push(Update::EndReattachItems);
765 }
766
767 chunk
768 }
769 };
770
771 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
774 self.links.last = Some(chunk.as_ptr());
777 }
778
779 Ok(())
780 }
781
782 pub fn remove_empty_chunk_at(
791 &mut self,
792 chunk_identifier: ChunkIdentifier,
793 ) -> Result<Option<Position>, Error> {
794 if self.links.first_chunk().is_last_chunk() {
796 return Err(Error::RemovingLastChunk);
797 }
798
799 let chunk = self
800 .links
801 .chunk_mut(chunk_identifier)
802 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
803
804 if chunk.num_items() > 0 {
805 return Err(Error::RemovingNonEmptyItemsChunk { identifier: chunk_identifier });
806 }
807
808 let chunk_was_first = chunk.is_first_chunk();
809 let chunk_was_last = chunk.is_last_chunk();
810 let next_ptr = chunk.next;
811 let previous_ptr = chunk.previous;
812 let position_of_next = chunk.next().map(|next| next.first_position());
813
814 chunk.unlink(self.updates.as_mut());
815
816 let chunk_ptr = chunk.as_ptr();
817
818 if chunk_was_first {
820 if let Some(next_ptr) = next_ptr {
822 self.links.first = next_ptr;
823 }
824 }
825
826 if chunk_was_last {
827 self.links.last = previous_ptr;
828 }
829
830 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
833
834 Ok(position_of_next)
836 }
837
838 pub fn replace_gap_at<I>(
846 &mut self,
847 items: I,
848 chunk_identifier: ChunkIdentifier,
849 ) -> Result<&Chunk<CAP, Item, Gap>, Error>
850 where
851 Item: Clone,
852 Gap: Clone,
853 I: IntoIterator<Item = Item>,
854 I::IntoIter: ExactSizeIterator,
855 {
856 let chunk_ptr;
857 let new_chunk_ptr;
858
859 {
860 let chunk = self
861 .links
862 .chunk_mut(chunk_identifier)
863 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
864
865 if chunk.is_items() {
866 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
867 };
868
869 let chunk_was_first = chunk.is_first_chunk();
870
871 let maybe_last_chunk_ptr = {
872 let items = items.into_iter();
873
874 let last_inserted_chunk = chunk
875 .insert_next(
877 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
878 &mut self.updates,
879 )
880 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
882
883 last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
884 };
885
886 new_chunk_ptr = chunk
887 .next
888 .unwrap();
890
891 chunk.unlink(self.updates.as_mut());
893
894 chunk_ptr = chunk.as_ptr();
896
897 if chunk_was_first {
899 self.links.first = new_chunk_ptr;
900 }
901
902 if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
905 self.links.last = Some(last_chunk_ptr);
906 }
907
908 }
910
911 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
916
917 Ok(
918 unsafe { new_chunk_ptr.as_ref() },
922 )
923 }
924
925 pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
927 where
928 P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
929 {
930 self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
931 }
932
933 pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
935 where
936 P: FnMut(&'a Item) -> bool,
937 {
938 self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
939 }
940
941 pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
945 IterBackward::new(self.links.latest_chunk())
946 }
947
948 pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
952 Iter::new(self.links.first_chunk())
953 }
954
955 pub fn rchunks_from(
960 &self,
961 identifier: ChunkIdentifier,
962 ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
963 Ok(IterBackward::new(
964 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
965 ))
966 }
967
968 pub fn chunks_from(
973 &self,
974 identifier: ChunkIdentifier,
975 ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
976 Ok(Iter::new(
977 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
978 ))
979 }
980
981 pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
985 self.ritems_from(self.links.latest_chunk().last_position())
986 .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
987 }
988
989 pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
993 let first_chunk = self.links.first_chunk();
994
995 self.items_from(first_chunk.first_position())
996 .expect("`items` cannot fail because at least one empty chunk must exist")
997 }
998
999 pub fn ritems_from(
1003 &self,
1004 position: Position,
1005 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1006 Ok(self
1007 .rchunks_from(position.chunk_identifier())?
1008 .filter_map(|chunk| match &chunk.content {
1009 ChunkContent::Gap(..) => None,
1010 ChunkContent::Items(items) => {
1011 let identifier = chunk.identifier();
1012
1013 Some(
1014 items.iter().enumerate().rev().map(move |(item_index, item)| {
1015 (Position(identifier, item_index), item)
1016 }),
1017 )
1018 }
1019 })
1020 .flatten()
1021 .skip_while({
1022 let expected_index = position.index();
1023
1024 move |(Position(chunk_identifier, item_index), _item)| {
1025 *chunk_identifier == position.chunk_identifier()
1026 && *item_index != expected_index
1027 }
1028 }))
1029 }
1030
1031 pub fn items_from(
1035 &self,
1036 position: Position,
1037 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1038 Ok(self
1039 .chunks_from(position.chunk_identifier())?
1040 .filter_map(|chunk| match &chunk.content {
1041 ChunkContent::Gap(..) => None,
1042 ChunkContent::Items(items) => {
1043 let identifier = chunk.identifier();
1044
1045 Some(
1046 items.iter().enumerate().map(move |(item_index, item)| {
1047 (Position(identifier, item_index), item)
1048 }),
1049 )
1050 }
1051 })
1052 .flatten()
1053 .skip(position.index()))
1054 }
1055
1056 #[must_use]
1068 pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
1069 self.updates.as_mut()
1070 }
1071
1072 pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
1079 let (updates, token) = self
1080 .updates
1081 .as_mut()
1082 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1083 let chunk_iterator = self.chunks();
1084
1085 Some(AsVector::new(updates, token, chunk_iterator))
1086 }
1087
1088 pub fn num_items(&self) -> usize {
1090 self.items().count()
1091 }
1092}
1093
1094impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
1095 fn drop(&mut self) {
1096 unsafe {
1106 self.links.clear();
1107 }
1108 }
1109}
1110
1111unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1115
1116unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1120
1121#[derive(Debug)]
1131pub struct ChunkIdentifierGenerator {
1132 next: AtomicU64,
1133}
1134
1135impl ChunkIdentifierGenerator {
1136 const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1138
1139 pub fn new_from_scratch() -> Self {
1142 Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1143 }
1144
1145 pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1148 Self { next: AtomicU64::new(last_chunk_identifier.0) }
1149 }
1150
1151 fn next(&self) -> ChunkIdentifier {
1156 let previous = self.next.fetch_add(1, atomic::Ordering::Relaxed);
1157
1158 if previous == u64::MAX {
1161 panic!("No more chunk identifiers available. Congrats, you did it. 2^64 identifiers have been consumed.")
1162 }
1163
1164 ChunkIdentifier(previous + 1)
1165 }
1166
1167 #[doc(hidden)]
1171 pub fn current(&self) -> ChunkIdentifier {
1172 ChunkIdentifier(self.next.load(atomic::Ordering::Relaxed))
1173 }
1174}
1175
1176#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1182#[repr(transparent)]
1183pub struct ChunkIdentifier(u64);
1184
1185impl ChunkIdentifier {
1186 pub fn new(identifier: u64) -> Self {
1188 Self(identifier)
1189 }
1190
1191 pub fn index(&self) -> u64 {
1193 self.0
1194 }
1195}
1196
1197impl PartialEq<u64> for ChunkIdentifier {
1198 fn eq(&self, other: &u64) -> bool {
1199 self.0 == *other
1200 }
1201}
1202
1203#[derive(Copy, Clone, Debug, PartialEq)]
1207pub struct Position(ChunkIdentifier, usize);
1208
1209impl Position {
1210 pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1212 Self(chunk_identifier, index)
1213 }
1214
1215 pub fn chunk_identifier(&self) -> ChunkIdentifier {
1217 self.0
1218 }
1219
1220 pub fn index(&self) -> usize {
1222 self.1
1223 }
1224
1225 pub fn decrement_index(&mut self) {
1231 self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1232 }
1233
1234 pub fn increment_index(&mut self) {
1241 self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1242 }
1243}
1244
1245#[derive(Debug)]
1248pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1249 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1250}
1251
1252impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1253 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1255 Self { chunk: Some(from_chunk) }
1256 }
1257}
1258
1259impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1260 type Item = &'a Chunk<CAP, Item, Gap>;
1261
1262 fn next(&mut self) -> Option<Self::Item> {
1263 self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1264 }
1265}
1266
1267#[derive(Debug)]
1270pub struct Iter<'a, const CAP: usize, Item, Gap> {
1271 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1272}
1273
1274impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1275 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1277 Self { chunk: Some(from_chunk) }
1278 }
1279}
1280
1281impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1282 type Item = &'a Chunk<CAP, Item, Gap>;
1283
1284 fn next(&mut self) -> Option<Self::Item> {
1285 self.chunk.inspect(|chunk| self.chunk = chunk.next())
1286 }
1287}
1288
1289#[derive(Clone, Debug)]
1291pub enum ChunkContent<Item, Gap> {
1292 Gap(Gap),
1295
1296 Items(Vec<Item>),
1298}
1299
1300pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1302 previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1304
1305 lazy_previous: Option<ChunkIdentifier>,
1310
1311 next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1313
1314 identifier: ChunkIdentifier,
1316
1317 content: ChunkContent<Item, Gap>,
1319}
1320
1321impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1322 fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1324 Self::new(identifier, ChunkContent::Gap(content))
1325 }
1326
1327 fn new_items(identifier: ChunkIdentifier) -> Self {
1329 Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1330 }
1331
1332 fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1333 Self { previous: None, lazy_previous: None, next: None, identifier, content }
1334 }
1335
1336 fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1338 let chunk = Self::new(identifier, content);
1339 let chunk_box = Box::new(chunk);
1340
1341 NonNull::from(Box::leak(chunk_box))
1342 }
1343
1344 fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1346 let chunk = Self::new_gap(identifier, content);
1347 let chunk_box = Box::new(chunk);
1348
1349 NonNull::from(Box::leak(chunk_box))
1350 }
1351
1352 fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1354 let chunk = Self::new_items(identifier);
1355 let chunk_box = Box::new(chunk);
1356
1357 NonNull::from(Box::leak(chunk_box))
1358 }
1359
1360 pub fn as_ptr(&self) -> NonNull<Self> {
1362 NonNull::from(self)
1363 }
1364
1365 pub fn is_gap(&self) -> bool {
1367 matches!(self.content, ChunkContent::Gap(..))
1368 }
1369
1370 pub fn is_items(&self) -> bool {
1372 !self.is_gap()
1373 }
1374
1375 pub fn is_definitive_head(&self) -> bool {
1378 self.previous.is_none() && self.lazy_previous.is_none()
1379 }
1380
1381 fn is_first_chunk(&self) -> bool {
1383 self.previous.is_none()
1384 }
1385
1386 fn is_last_chunk(&self) -> bool {
1388 self.next.is_none()
1389 }
1390
1391 #[doc(hidden)]
1395 pub fn lazy_previous(&self) -> Option<ChunkIdentifier> {
1396 self.lazy_previous
1397 }
1398
1399 pub fn identifier(&self) -> ChunkIdentifier {
1401 self.identifier
1402 }
1403
1404 pub fn content(&self) -> &ChunkContent<Item, Gap> {
1406 &self.content
1407 }
1408
1409 pub fn first_position(&self) -> Position {
1413 Position(self.identifier(), 0)
1414 }
1415
1416 pub fn last_position(&self) -> Position {
1420 let identifier = self.identifier();
1421
1422 match &self.content {
1423 ChunkContent::Gap(..) => Position(identifier, 0),
1424 ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1425 }
1426 }
1427
1428 pub fn num_items(&self) -> usize {
1432 match &self.content {
1433 ChunkContent::Gap(..) => 0,
1434 ChunkContent::Items(items) => items.len(),
1435 }
1436 }
1437
1438 fn push_items<I>(
1450 &mut self,
1451 mut new_items: I,
1452 chunk_identifier_generator: &ChunkIdentifierGenerator,
1453 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1454 ) -> &mut Self
1455 where
1456 I: Iterator<Item = Item> + ExactSizeIterator,
1457 Item: Clone,
1458 Gap: Clone,
1459 {
1460 if new_items.len() == 0 {
1462 return self;
1463 }
1464
1465 let identifier = self.identifier();
1466 let prev_num_items = self.num_items();
1467
1468 match &mut self.content {
1469 ChunkContent::Gap(..) => {
1472 self
1473 .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1475 .push_items(new_items, chunk_identifier_generator, updates)
1478 }
1479
1480 ChunkContent::Items(items) => {
1481 let free_space = CAPACITY.saturating_sub(prev_num_items);
1483
1484 if new_items.len() <= free_space {
1486 let start = items.len();
1487 items.extend(new_items);
1488
1489 if let Some(updates) = updates.as_mut() {
1490 updates.push(Update::PushItems {
1491 at: Position(identifier, start),
1492 items: items[start..].to_vec(),
1493 });
1494 }
1495
1496 self
1498 } else {
1499 if free_space > 0 {
1500 let start = items.len();
1502 items.extend(new_items.by_ref().take(free_space));
1503
1504 if let Some(updates) = updates.as_mut() {
1505 updates.push(Update::PushItems {
1506 at: Position(identifier, start),
1507 items: items[start..].to_vec(),
1508 });
1509 }
1510 }
1511
1512 self
1513 .insert_next(
1515 Self::new_items_leaked(chunk_identifier_generator.next()),
1516 updates,
1517 )
1518 .push_items(new_items, chunk_identifier_generator, updates)
1521 }
1522 }
1523 }
1524 }
1525
1526 fn insert_next(
1531 &mut self,
1532 mut new_chunk_ptr: NonNull<Self>,
1533 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1534 ) -> &mut Self
1535 where
1536 Gap: Clone,
1537 {
1538 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1539
1540 if let Some(next_chunk) = self.next_mut() {
1542 next_chunk.previous = Some(new_chunk_ptr);
1544
1545 new_chunk.next = self.next;
1547 }
1548
1549 self.next = Some(new_chunk_ptr);
1551 new_chunk.previous = Some(self.as_ptr());
1553
1554 if let Some(updates) = updates.as_mut() {
1555 let previous = new_chunk.previous().map(Chunk::identifier);
1556 let new = new_chunk.identifier();
1557 let next = new_chunk.next().map(Chunk::identifier);
1558
1559 match new_chunk.content() {
1560 ChunkContent::Gap(gap) => {
1561 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1562 }
1563
1564 ChunkContent::Items(..) => {
1565 updates.push(Update::NewItemsChunk { previous, new, next })
1566 }
1567 }
1568 }
1569
1570 new_chunk
1571 }
1572
1573 fn insert_before(
1578 &mut self,
1579 mut new_chunk_ptr: NonNull<Self>,
1580 updates: Option<&mut ObservableUpdates<Item, Gap>>,
1581 ) -> &mut Self
1582 where
1583 Gap: Clone,
1584 {
1585 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1586
1587 if let Some(previous_chunk) = self.previous_mut() {
1589 previous_chunk.next = Some(new_chunk_ptr);
1591
1592 new_chunk.previous = self.previous;
1594 }
1595 else {
1598 new_chunk.lazy_previous = self.lazy_previous.take();
1599 }
1600
1601 self.previous = Some(new_chunk_ptr);
1603 new_chunk.next = Some(self.as_ptr());
1605
1606 if let Some(updates) = updates {
1607 let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1608 let new = new_chunk.identifier();
1609 let next = new_chunk.next().map(Chunk::identifier);
1610
1611 match new_chunk.content() {
1612 ChunkContent::Gap(gap) => {
1613 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1614 }
1615
1616 ChunkContent::Items(..) => {
1617 updates.push(Update::NewItemsChunk { previous, new, next })
1618 }
1619 }
1620 }
1621
1622 new_chunk
1623 }
1624
1625 fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1630 let previous_ptr = self.previous;
1631 let next_ptr = self.next;
1632 let lazy_previous = self.lazy_previous.take();
1636
1637 if let Some(previous) = self.previous_mut() {
1638 previous.next = next_ptr;
1639 }
1640
1641 if let Some(next) = self.next_mut() {
1642 next.previous = previous_ptr;
1643 next.lazy_previous = lazy_previous;
1644 }
1645
1646 if let Some(updates) = updates {
1647 updates.push(Update::RemoveChunk(self.identifier()));
1648 }
1649 }
1650
1651 fn previous(&self) -> Option<&Self> {
1653 self.previous.map(|non_null| unsafe { non_null.as_ref() })
1654 }
1655
1656 fn previous_mut(&mut self) -> Option<&mut Self> {
1658 self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1659 }
1660
1661 fn next(&self) -> Option<&Self> {
1663 self.next.map(|non_null| unsafe { non_null.as_ref() })
1664 }
1665
1666 fn next_mut(&mut self) -> Option<&mut Self> {
1668 self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1669 }
1670}
1671
1672impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<CAP, Item, Gap>
1673where
1674 Item: fmt::Debug,
1675 Gap: fmt::Debug,
1676{
1677 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1678 formatter
1679 .debug_struct("LinkedChunk")
1680 .field("first (deref)", unsafe { self.links.first.as_ref() })
1681 .field("last", &self.links.last)
1682 .finish_non_exhaustive()
1683 }
1684}
1685
1686impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1687where
1688 Item: fmt::Debug,
1689 Gap: fmt::Debug,
1690{
1691 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1692 formatter
1693 .debug_struct("Chunk")
1694 .field("identifier", &self.identifier)
1695 .field("content", &self.content)
1696 .field("previous", &self.previous)
1697 .field("ptr", &std::ptr::from_ref(self))
1698 .field("next", &self.next)
1699 .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1700 .finish()
1701 }
1702}
1703
1704#[derive(Clone, Debug)]
1710pub struct RawChunk<Item, Gap> {
1711 pub content: ChunkContent<Item, Gap>,
1713
1714 pub previous: Option<ChunkIdentifier>,
1716
1717 pub identifier: ChunkIdentifier,
1719
1720 pub next: Option<ChunkIdentifier>,
1722}
1723
1724#[cfg(test)]
1725mod tests {
1726 use std::{
1727 ops::Not,
1728 sync::{atomic::Ordering, Arc},
1729 };
1730
1731 use assert_matches::assert_matches;
1732
1733 use super::{
1734 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Error, LinkedChunk,
1735 Position,
1736 };
1737
1738 #[test]
1739 fn test_chunk_identifier_generator() {
1740 let generator = ChunkIdentifierGenerator::new_from_scratch();
1741
1742 assert_eq!(generator.next(), ChunkIdentifier(1));
1743 assert_eq!(generator.next(), ChunkIdentifier(2));
1744 assert_eq!(generator.next(), ChunkIdentifier(3));
1745 assert_eq!(generator.next(), ChunkIdentifier(4));
1746
1747 let generator =
1748 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1749
1750 assert_eq!(generator.next(), ChunkIdentifier(43));
1751 assert_eq!(generator.next(), ChunkIdentifier(44));
1752 assert_eq!(generator.next(), ChunkIdentifier(45));
1753 assert_eq!(generator.next(), ChunkIdentifier(46));
1754 }
1755
1756 #[test]
1757 fn test_empty() {
1758 let items = LinkedChunk::<3, char, ()>::new();
1759
1760 assert_eq!(items.num_items(), 0);
1761
1762 }
1765
1766 #[test]
1767 fn test_updates() {
1768 assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1769 assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1770 }
1771
1772 #[test]
1773 fn test_new_with_initial_update() {
1774 use super::Update::*;
1775
1776 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1777
1778 assert_eq!(
1779 linked_chunk.updates().unwrap().take(),
1780 &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1781 );
1782 }
1783
1784 #[test]
1785 fn test_push_items() {
1786 use super::Update::*;
1787
1788 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1789
1790 let _ = linked_chunk.updates().unwrap().take();
1792
1793 linked_chunk.push_items_back(['a']);
1794
1795 assert_items_eq!(linked_chunk, ['a']);
1796 assert_eq!(
1797 linked_chunk.updates().unwrap().take(),
1798 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1799 );
1800
1801 linked_chunk.push_items_back(['b', 'c']);
1802 assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1803 assert_eq!(
1804 linked_chunk.updates().unwrap().take(),
1805 &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1806 );
1807
1808 linked_chunk.push_items_back(['d', 'e']);
1809 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
1810 assert_eq!(
1811 linked_chunk.updates().unwrap().take(),
1812 &[
1813 NewItemsChunk {
1814 previous: Some(ChunkIdentifier(0)),
1815 new: ChunkIdentifier(1),
1816 next: None
1817 },
1818 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1819 ]
1820 );
1821
1822 linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1823 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1824 assert_eq!(
1825 linked_chunk.updates().unwrap().take(),
1826 &[
1827 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1828 NewItemsChunk {
1829 previous: Some(ChunkIdentifier(1)),
1830 new: ChunkIdentifier(2),
1831 next: None,
1832 },
1833 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1834 NewItemsChunk {
1835 previous: Some(ChunkIdentifier(2)),
1836 new: ChunkIdentifier(3),
1837 next: None,
1838 },
1839 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1840 ]
1841 );
1842
1843 assert_eq!(linked_chunk.num_items(), 10);
1844 }
1845
1846 #[test]
1847 fn test_push_gap() {
1848 use super::Update::*;
1849
1850 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1851
1852 let _ = linked_chunk.updates().unwrap().take();
1854
1855 linked_chunk.push_items_back(['a']);
1856 assert_items_eq!(linked_chunk, ['a']);
1857 assert_eq!(
1858 linked_chunk.updates().unwrap().take(),
1859 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1860 );
1861
1862 linked_chunk.push_gap_back(());
1863 assert_items_eq!(linked_chunk, ['a'] [-]);
1864 assert_eq!(
1865 linked_chunk.updates().unwrap().take(),
1866 &[NewGapChunk {
1867 previous: Some(ChunkIdentifier(0)),
1868 new: ChunkIdentifier(1),
1869 next: None,
1870 gap: (),
1871 }]
1872 );
1873
1874 linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1875 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1876 assert_eq!(
1877 linked_chunk.updates().unwrap().take(),
1878 &[
1879 NewItemsChunk {
1880 previous: Some(ChunkIdentifier(1)),
1881 new: ChunkIdentifier(2),
1882 next: None,
1883 },
1884 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
1885 NewItemsChunk {
1886 previous: Some(ChunkIdentifier(2)),
1887 new: ChunkIdentifier(3),
1888 next: None,
1889 },
1890 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
1891 ]
1892 );
1893
1894 linked_chunk.push_gap_back(());
1895 linked_chunk.push_gap_back(()); assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
1897 assert_eq!(
1898 linked_chunk.updates().unwrap().take(),
1899 &[
1900 NewGapChunk {
1901 previous: Some(ChunkIdentifier(3)),
1902 new: ChunkIdentifier(4),
1903 next: None,
1904 gap: (),
1905 },
1906 NewGapChunk {
1907 previous: Some(ChunkIdentifier(4)),
1908 new: ChunkIdentifier(5),
1909 next: None,
1910 gap: (),
1911 }
1912 ]
1913 );
1914
1915 linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
1916 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
1917 assert_eq!(
1918 linked_chunk.updates().unwrap().take(),
1919 &[
1920 NewItemsChunk {
1921 previous: Some(ChunkIdentifier(5)),
1922 new: ChunkIdentifier(6),
1923 next: None,
1924 },
1925 PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
1926 NewItemsChunk {
1927 previous: Some(ChunkIdentifier(6)),
1928 new: ChunkIdentifier(7),
1929 next: None,
1930 },
1931 PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
1932 ]
1933 );
1934
1935 assert_eq!(linked_chunk.num_items(), 9);
1936 }
1937
1938 #[test]
1939 fn test_identifiers_and_positions() {
1940 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
1941 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
1942 linked_chunk.push_gap_back(());
1943 linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
1944 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
1945
1946 assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
1947 assert_eq!(
1948 linked_chunk.item_position(|item| *item == 'e'),
1949 Some(Position(ChunkIdentifier(1), 1))
1950 );
1951 }
1952
1953 #[test]
1954 fn test_rchunks() {
1955 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1956 linked_chunk.push_items_back(['a', 'b']);
1957 linked_chunk.push_gap_back(());
1958 linked_chunk.push_items_back(['c', 'd', 'e']);
1959
1960 let mut iterator = linked_chunk.rchunks();
1961
1962 assert_matches!(
1963 iterator.next(),
1964 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
1965 assert_eq!(items, &['e']);
1966 }
1967 );
1968 assert_matches!(
1969 iterator.next(),
1970 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1971 assert_eq!(items, &['c', 'd']);
1972 }
1973 );
1974 assert_matches!(
1975 iterator.next(),
1976 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
1977 );
1978 assert_matches!(
1979 iterator.next(),
1980 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
1981 assert_eq!(items, &['a', 'b']);
1982 }
1983 );
1984 assert_matches!(iterator.next(), None);
1985 }
1986
1987 #[test]
1988 fn test_chunks() {
1989 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1990 linked_chunk.push_items_back(['a', 'b']);
1991 linked_chunk.push_gap_back(());
1992 linked_chunk.push_items_back(['c', 'd', 'e']);
1993
1994 let mut iterator = linked_chunk.chunks();
1995
1996 assert_matches!(
1997 iterator.next(),
1998 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
1999 assert_eq!(items, &['a', 'b']);
2000 }
2001 );
2002 assert_matches!(
2003 iterator.next(),
2004 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2005 );
2006 assert_matches!(
2007 iterator.next(),
2008 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2009 assert_eq!(items, &['c', 'd']);
2010 }
2011 );
2012 assert_matches!(
2013 iterator.next(),
2014 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2015 assert_eq!(items, &['e']);
2016 }
2017 );
2018 assert_matches!(iterator.next(), None);
2019 }
2020
2021 #[test]
2022 fn test_rchunks_from() -> Result<(), Error> {
2023 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2024 linked_chunk.push_items_back(['a', 'b']);
2025 linked_chunk.push_gap_back(());
2026 linked_chunk.push_items_back(['c', 'd', 'e']);
2027
2028 let mut iterator = linked_chunk.rchunks_from(
2029 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2030 )?;
2031
2032 assert_matches!(
2033 iterator.next(),
2034 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2035 assert_eq!(items, &['c', 'd']);
2036 }
2037 );
2038 assert_matches!(
2039 iterator.next(),
2040 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2041 );
2042 assert_matches!(
2043 iterator.next(),
2044 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2045 assert_eq!(items, &['a', 'b']);
2046 }
2047 );
2048 assert_matches!(iterator.next(), None);
2049
2050 Ok(())
2051 }
2052
2053 #[test]
2054 fn test_chunks_from() -> Result<(), Error> {
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.chunks_from(
2061 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2062 )?;
2063
2064 assert_matches!(
2065 iterator.next(),
2066 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2067 assert_eq!(items, &['c', 'd']);
2068 }
2069 );
2070 assert_matches!(
2071 iterator.next(),
2072 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2073 assert_eq!(items, &['e']);
2074 }
2075 );
2076 assert_matches!(iterator.next(), None);
2077
2078 Ok(())
2079 }
2080
2081 #[test]
2082 fn test_ritems() {
2083 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2084 linked_chunk.push_items_back(['a', 'b']);
2085 linked_chunk.push_gap_back(());
2086 linked_chunk.push_items_back(['c', 'd', 'e']);
2087
2088 let mut iterator = linked_chunk.ritems();
2089
2090 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2091 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2092 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2093 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2094 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2095 assert_matches!(iterator.next(), None);
2096 }
2097
2098 #[test]
2099 fn test_ritems_with_final_gap() -> Result<(), Error> {
2100 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2101 linked_chunk.push_items_back(['a', 'b']);
2102 linked_chunk.push_gap_back(());
2103 linked_chunk.push_items_back(['c', 'd', 'e']);
2104 linked_chunk.push_gap_back(());
2105
2106 let mut iterator = linked_chunk.ritems();
2107
2108 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
2109 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2110 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2111 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2112 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2113 assert_matches!(iterator.next(), None);
2114
2115 Ok(())
2116 }
2117
2118 #[test]
2119 fn test_ritems_empty() {
2120 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2121 let mut iterator = linked_chunk.ritems();
2122
2123 assert_matches!(iterator.next(), None);
2124 }
2125
2126 #[test]
2127 fn test_items() {
2128 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2129 linked_chunk.push_items_back(['a', 'b']);
2130 linked_chunk.push_gap_back(());
2131 linked_chunk.push_items_back(['c', 'd', 'e']);
2132
2133 let mut iterator = linked_chunk.items();
2134
2135 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2136 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2137 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2138 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2139 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2140 assert_matches!(iterator.next(), None);
2141 }
2142
2143 #[test]
2144 fn test_items_empty() {
2145 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2146 let mut iterator = linked_chunk.items();
2147
2148 assert_matches!(iterator.next(), None);
2149 }
2150
2151 #[test]
2152 fn test_ritems_from() -> Result<(), Error> {
2153 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2154 linked_chunk.push_items_back(['a', 'b']);
2155 linked_chunk.push_gap_back(());
2156 linked_chunk.push_items_back(['c', 'd', 'e']);
2157
2158 let mut iterator =
2159 linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2160
2161 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2162 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2163 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2164 assert_matches!(iterator.next(), None);
2165
2166 Ok(())
2167 }
2168
2169 #[test]
2170 fn test_items_from() -> Result<(), Error> {
2171 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2172 linked_chunk.push_items_back(['a', 'b']);
2173 linked_chunk.push_gap_back(());
2174 linked_chunk.push_items_back(['c', 'd', 'e']);
2175
2176 let mut iterator =
2177 linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2178
2179 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2180 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2181 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2182 assert_matches!(iterator.next(), None);
2183
2184 Ok(())
2185 }
2186
2187 #[test]
2188 fn test_insert_items_at() -> Result<(), Error> {
2189 use super::Update::*;
2190
2191 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2192
2193 let _ = linked_chunk.updates().unwrap().take();
2195
2196 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2197 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2198 assert_eq!(
2199 linked_chunk.updates().unwrap().take(),
2200 &[
2201 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2202 NewItemsChunk {
2203 previous: Some(ChunkIdentifier(0)),
2204 new: ChunkIdentifier(1),
2205 next: None,
2206 },
2207 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2208 ]
2209 );
2210
2211 {
2213 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2214
2215 linked_chunk.insert_items_at(['w', 'x', 'y', 'z'], position_of_e)?;
2218
2219 assert_items_eq!(
2220 linked_chunk,
2221 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2222 );
2223 assert_eq!(linked_chunk.num_items(), 10);
2224 assert_eq!(
2225 linked_chunk.updates().unwrap().take(),
2226 &[
2227 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2228 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2229 NewItemsChunk {
2230 previous: Some(ChunkIdentifier(1)),
2231 new: ChunkIdentifier(2),
2232 next: None,
2233 },
2234 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2235 StartReattachItems,
2236 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2237 NewItemsChunk {
2238 previous: Some(ChunkIdentifier(2)),
2239 new: ChunkIdentifier(3),
2240 next: None,
2241 },
2242 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2243 EndReattachItems,
2244 ]
2245 );
2246 }
2247
2248 {
2250 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2251 linked_chunk.insert_items_at(['l', 'm', 'n', 'o'], position_of_a)?;
2252
2253 assert_items_eq!(
2254 linked_chunk,
2255 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2256 );
2257 assert_eq!(linked_chunk.num_items(), 14);
2258 assert_eq!(
2259 linked_chunk.updates().unwrap().take(),
2260 &[
2261 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2262 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2263 NewItemsChunk {
2264 previous: Some(ChunkIdentifier(0)),
2265 new: ChunkIdentifier(4),
2266 next: Some(ChunkIdentifier(1)),
2267 },
2268 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2269 StartReattachItems,
2270 PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2271 NewItemsChunk {
2272 previous: Some(ChunkIdentifier(4)),
2273 new: ChunkIdentifier(5),
2274 next: Some(ChunkIdentifier(1)),
2275 },
2276 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2277 EndReattachItems,
2278 ]
2279 );
2280 }
2281
2282 {
2284 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2285 linked_chunk.insert_items_at(['r', 's'], position_of_c)?;
2286
2287 assert_items_eq!(
2288 linked_chunk,
2289 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2290 );
2291 assert_eq!(linked_chunk.num_items(), 16);
2292 assert_eq!(
2293 linked_chunk.updates().unwrap().take(),
2294 &[
2295 DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2296 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2297 StartReattachItems,
2298 PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2299 EndReattachItems,
2300 ]
2301 );
2302 }
2303
2304 {
2306 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2307 let position_after_f =
2308 Position(position_of_f.chunk_identifier(), position_of_f.index() + 1);
2309
2310 linked_chunk.insert_items_at(['p', 'q'], position_after_f)?;
2311 assert_items_eq!(
2312 linked_chunk,
2313 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2314 );
2315 assert_eq!(
2316 linked_chunk.updates().unwrap().take(),
2317 &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2318 );
2319 assert_eq!(linked_chunk.num_items(), 18);
2320 }
2321
2322 {
2324 assert_matches!(
2325 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)),
2326 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2327 );
2328 assert!(linked_chunk.updates().unwrap().take().is_empty());
2329 }
2330
2331 {
2333 assert_matches!(
2334 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)),
2335 Err(Error::InvalidItemIndex { index: 128 })
2336 );
2337 assert!(linked_chunk.updates().unwrap().take().is_empty());
2338 }
2339
2340 {
2342 linked_chunk.push_gap_back(());
2344 assert_items_eq!(
2345 linked_chunk,
2346 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2347 );
2348 assert_eq!(
2349 linked_chunk.updates().unwrap().take(),
2350 &[NewGapChunk {
2351 previous: Some(ChunkIdentifier(3)),
2352 new: ChunkIdentifier(6),
2353 next: None,
2354 gap: ()
2355 }]
2356 );
2357
2358 assert_matches!(
2359 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(6), 0)),
2360 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2361 );
2362 }
2363
2364 assert_eq!(linked_chunk.num_items(), 18);
2365
2366 Ok(())
2367 }
2368
2369 #[test]
2370 fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2371 use super::Update::*;
2372
2373 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2374
2375 let _ = linked_chunk.updates().unwrap().take();
2377
2378 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2379 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2380 assert_eq!(
2381 linked_chunk.updates().unwrap().take(),
2382 &[
2383 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2384 NewItemsChunk {
2385 previous: Some(ChunkIdentifier(0)),
2386 new: ChunkIdentifier(1),
2387 next: None,
2388 },
2389 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2390 ]
2391 );
2392
2393 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2395
2396 linked_chunk.insert_items_at(['w', 'x', 'y', 'z'], position_of_e)?;
2399
2400 assert_items_eq!(
2401 linked_chunk,
2402 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2403 );
2404 assert_eq!(linked_chunk.num_items(), 10);
2405 assert_eq!(
2406 linked_chunk.updates().unwrap().take(),
2407 &[
2408 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2409 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2410 NewItemsChunk {
2411 previous: Some(ChunkIdentifier(1)),
2412 new: ChunkIdentifier(2),
2413 next: None,
2414 },
2415 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2416 StartReattachItems,
2417 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2418 NewItemsChunk {
2419 previous: Some(ChunkIdentifier(2)),
2420 new: ChunkIdentifier(3),
2421 next: None,
2422 },
2423 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2424 EndReattachItems,
2425 ]
2426 );
2427
2428 Ok(())
2429 }
2430
2431 #[test]
2432 fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2433 use super::Update::*;
2434
2435 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2436
2437 let _ = linked_chunk.updates().unwrap().take();
2439
2440 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2441 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2442 assert_eq!(
2443 linked_chunk.updates().unwrap().take(),
2444 &[
2445 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2446 NewItemsChunk {
2447 previous: Some(ChunkIdentifier(0)),
2448 new: ChunkIdentifier(1),
2449 next: None,
2450 },
2451 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2452 ]
2453 );
2454
2455 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2457 linked_chunk.insert_items_at(['l', 'm', 'n', 'o'], position_of_a)?;
2458
2459 assert_items_eq!(
2460 linked_chunk,
2461 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'e', 'f']
2462 );
2463 assert_eq!(linked_chunk.num_items(), 10);
2464 assert_eq!(
2465 linked_chunk.updates().unwrap().take(),
2466 &[
2467 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2468 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2469 NewItemsChunk {
2470 previous: Some(ChunkIdentifier(0)),
2471 new: ChunkIdentifier(2),
2472 next: Some(ChunkIdentifier(1)),
2473 },
2474 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2475 StartReattachItems,
2476 PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2477 NewItemsChunk {
2478 previous: Some(ChunkIdentifier(2)),
2479 new: ChunkIdentifier(3),
2480 next: Some(ChunkIdentifier(1)),
2481 },
2482 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2483 EndReattachItems,
2484 ]
2485 );
2486
2487 Ok(())
2488 }
2489
2490 #[test]
2491 fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2492 use super::Update::*;
2493
2494 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2495
2496 let _ = linked_chunk.updates().unwrap().take();
2498
2499 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2500 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2501 assert_eq!(
2502 linked_chunk.updates().unwrap().take(),
2503 &[
2504 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2505 NewItemsChunk {
2506 previous: Some(ChunkIdentifier(0)),
2507 new: ChunkIdentifier(1),
2508 next: None,
2509 },
2510 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2511 NewItemsChunk {
2512 previous: Some(ChunkIdentifier(1)),
2513 new: ChunkIdentifier(2),
2514 next: None,
2515 },
2516 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2517 ]
2518 );
2519
2520 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2521 linked_chunk.insert_items_at(['r', 's'], position_of_d)?;
2522
2523 assert_items_eq!(
2524 linked_chunk,
2525 ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2526 );
2527 assert_eq!(linked_chunk.num_items(), 10);
2528 assert_eq!(
2529 linked_chunk.updates().unwrap().take(),
2530 &[
2531 DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2532 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2533 StartReattachItems,
2534 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2535 NewItemsChunk {
2536 previous: Some(ChunkIdentifier(1)),
2537 new: ChunkIdentifier(3),
2538 next: Some(ChunkIdentifier(2)),
2539 },
2540 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2541 EndReattachItems,
2542 ]
2543 );
2544
2545 Ok(())
2546 }
2547
2548 #[test]
2549 fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2550 use super::Update::*;
2551
2552 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2553
2554 let _ = linked_chunk.updates().unwrap().take();
2556
2557 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2558 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2559 assert_eq!(
2560 linked_chunk.updates().unwrap().take(),
2561 &[
2562 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2563 NewItemsChunk {
2564 previous: Some(ChunkIdentifier(0)),
2565 new: ChunkIdentifier(1),
2566 next: None,
2567 },
2568 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2569 ]
2570 );
2571
2572 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2574 let position_after_e =
2575 Position(position_of_e.chunk_identifier(), position_of_e.index() + 1);
2576
2577 linked_chunk.insert_items_at(['p', 'q'], position_after_e)?;
2578 assert_items_eq!(
2579 linked_chunk,
2580 ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2581 );
2582 assert_eq!(
2583 linked_chunk.updates().unwrap().take(),
2584 &[
2585 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2586 NewItemsChunk {
2587 previous: Some(ChunkIdentifier(1)),
2588 new: ChunkIdentifier(2),
2589 next: None
2590 },
2591 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2592 ]
2593 );
2594 assert_eq!(linked_chunk.num_items(), 7);
2595
2596 Ok(())
2597 }
2598
2599 #[test]
2600 fn test_insert_items_at_errs() -> Result<(), Error> {
2601 use super::Update::*;
2602
2603 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2604
2605 let _ = linked_chunk.updates().unwrap().take();
2607
2608 linked_chunk.push_items_back(['a', 'b', 'c']);
2609 linked_chunk.push_gap_back(());
2610 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2611 assert_eq!(
2612 linked_chunk.updates().unwrap().take(),
2613 &[
2614 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2615 NewGapChunk {
2616 previous: Some(ChunkIdentifier(0)),
2617 new: ChunkIdentifier(1),
2618 next: None,
2619 gap: (),
2620 },
2621 ]
2622 );
2623
2624 {
2626 assert_matches!(
2627 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)),
2628 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2629 );
2630 assert!(linked_chunk.updates().unwrap().take().is_empty());
2631 }
2632
2633 {
2635 assert_matches!(
2636 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)),
2637 Err(Error::InvalidItemIndex { index: 128 })
2638 );
2639 assert!(linked_chunk.updates().unwrap().take().is_empty());
2640 }
2641
2642 {
2644 assert_matches!(
2645 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(1), 0)),
2646 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2647 );
2648 }
2649
2650 Ok(())
2651 }
2652
2653 #[test]
2654 fn test_remove_item_at() -> Result<(), Error> {
2655 use super::Update::*;
2656
2657 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2658
2659 let _ = linked_chunk.updates().unwrap().take();
2661
2662 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2663 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2664 assert_eq!(linked_chunk.num_items(), 11);
2665
2666 let _ = linked_chunk.updates().unwrap().take();
2668
2669 {
2672 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2673 let removed_item = linked_chunk.remove_item_at(position_of_f)?;
2674
2675 assert_eq!(removed_item, 'f');
2676 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2677 assert_eq!(linked_chunk.num_items(), 10);
2678
2679 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2680 let removed_item = linked_chunk.remove_item_at(position_of_e)?;
2681
2682 assert_eq!(removed_item, 'e');
2683 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2684 assert_eq!(linked_chunk.num_items(), 9);
2685
2686 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2687 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2688
2689 assert_eq!(removed_item, 'd');
2690 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2691 assert_eq!(linked_chunk.num_items(), 8);
2692
2693 assert_eq!(
2694 linked_chunk.updates().unwrap().take(),
2695 &[
2696 RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2697 RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2698 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2699 RemoveChunk(ChunkIdentifier(1)),
2700 ]
2701 );
2702 }
2703
2704 {
2707 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2708 let removed_item = linked_chunk.remove_item_at(first_position)?;
2709
2710 assert_eq!(removed_item, 'a');
2711 assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2712 assert_eq!(linked_chunk.num_items(), 7);
2713
2714 let removed_item = linked_chunk.remove_item_at(first_position)?;
2715
2716 assert_eq!(removed_item, 'b');
2717 assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2718 assert_eq!(linked_chunk.num_items(), 6);
2719
2720 let removed_item = linked_chunk.remove_item_at(first_position)?;
2721
2722 assert_eq!(removed_item, 'c');
2723 assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2724 assert_eq!(linked_chunk.num_items(), 5);
2725
2726 assert_eq!(
2727 linked_chunk.updates().unwrap().take(),
2728 &[
2729 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2730 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2731 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2732 ]
2733 );
2734 }
2735
2736 {
2739 let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2740 let removed_item = linked_chunk.remove_item_at(first_position)?;
2741
2742 assert_eq!(removed_item, 'g');
2743 assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2744 assert_eq!(linked_chunk.num_items(), 4);
2745
2746 let removed_item = linked_chunk.remove_item_at(first_position)?;
2747
2748 assert_eq!(removed_item, 'h');
2749 assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2750 assert_eq!(linked_chunk.num_items(), 3);
2751
2752 let removed_item = linked_chunk.remove_item_at(first_position)?;
2753
2754 assert_eq!(removed_item, 'i');
2755 assert_items_eq!(linked_chunk, [] ['j', 'k']);
2756 assert_eq!(linked_chunk.num_items(), 2);
2757
2758 assert_eq!(
2759 linked_chunk.updates().unwrap().take(),
2760 &[
2761 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2762 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2763 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2764 RemoveChunk(ChunkIdentifier(2)),
2765 ]
2766 );
2767 }
2768
2769 {
2772 let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2773 let removed_item = linked_chunk.remove_item_at(position_of_k)?;
2774
2775 assert_eq!(removed_item, 'k');
2776 #[rustfmt::skip]
2777 assert_items_eq!(linked_chunk, [] ['j']);
2778 assert_eq!(linked_chunk.num_items(), 1);
2779
2780 let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2781 let removed_item = linked_chunk.remove_item_at(position_of_j)?;
2782
2783 assert_eq!(removed_item, 'j');
2784 assert_items_eq!(linked_chunk, []);
2785 assert_eq!(linked_chunk.num_items(), 0);
2786
2787 assert_eq!(
2788 linked_chunk.updates().unwrap().take(),
2789 &[
2790 RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2791 RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2792 RemoveChunk(ChunkIdentifier(3)),
2793 ]
2794 );
2795 }
2796
2797 {
2799 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2800
2801 #[rustfmt::skip]
2802 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2803 assert_eq!(linked_chunk.num_items(), 4);
2804
2805 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2806 linked_chunk.insert_gap_at((), position_of_c)?;
2807
2808 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2809 assert_eq!(linked_chunk.num_items(), 4);
2810
2811 let _ = linked_chunk.updates().unwrap().take();
2813
2814 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2815 let removed_item = linked_chunk.remove_item_at(position_of_c)?;
2816
2817 assert_eq!(removed_item, 'c');
2818 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2819 assert_eq!(linked_chunk.num_items(), 3);
2820
2821 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2822 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2823
2824 assert_eq!(removed_item, 'd');
2825 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2826 assert_eq!(linked_chunk.num_items(), 2);
2827
2828 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2829 let removed_item = linked_chunk.remove_item_at(first_position)?;
2830
2831 assert_eq!(removed_item, 'a');
2832 assert_items_eq!(linked_chunk, ['b'] [-]);
2833 assert_eq!(linked_chunk.num_items(), 1);
2834
2835 let removed_item = linked_chunk.remove_item_at(first_position)?;
2836
2837 assert_eq!(removed_item, 'b');
2838 assert_items_eq!(linked_chunk, [] [-]);
2839 assert_eq!(linked_chunk.num_items(), 0);
2840
2841 assert_eq!(
2842 linked_chunk.updates().unwrap().take(),
2843 &[
2844 RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2845 RemoveChunk(ChunkIdentifier(6)),
2846 RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2847 RemoveChunk(ChunkIdentifier(4)),
2848 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2849 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2850 ]
2851 );
2852 }
2853
2854 Ok(())
2855 }
2856
2857 #[test]
2858 fn test_insert_gap_at() -> Result<(), Error> {
2859 use super::Update::*;
2860
2861 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2862
2863 let _ = linked_chunk.updates().unwrap().take();
2865
2866 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2867 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2868 assert_eq!(
2869 linked_chunk.updates().unwrap().take(),
2870 &[
2871 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2872 NewItemsChunk {
2873 previous: Some(ChunkIdentifier(0)),
2874 new: ChunkIdentifier(1),
2875 next: None
2876 },
2877 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2878 ]
2879 );
2880
2881 {
2883 let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
2884 linked_chunk.insert_gap_at((), position_of_b)?;
2885
2886 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2887 assert_eq!(
2888 linked_chunk.updates().unwrap().take(),
2889 &[
2890 DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
2891 NewGapChunk {
2892 previous: Some(ChunkIdentifier(0)),
2893 new: ChunkIdentifier(2),
2894 next: Some(ChunkIdentifier(1)),
2895 gap: (),
2896 },
2897 StartReattachItems,
2898 NewItemsChunk {
2899 previous: Some(ChunkIdentifier(2)),
2900 new: ChunkIdentifier(3),
2901 next: Some(ChunkIdentifier(1)),
2902 },
2903 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
2904 EndReattachItems,
2905 ]
2906 );
2907 }
2908
2909 {
2912 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2913 linked_chunk.insert_gap_at((), position_of_a)?;
2914
2915 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2918 assert_eq!(
2919 linked_chunk.updates().unwrap().take(),
2920 &[NewGapChunk {
2921 previous: None,
2922 new: ChunkIdentifier(4),
2923 next: Some(ChunkIdentifier(0)),
2924 gap: (),
2925 },]
2926 );
2927 }
2928
2929 {
2932 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2933 linked_chunk.insert_gap_at((), position_of_d)?;
2934
2935 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
2939 assert_eq!(
2940 linked_chunk.updates().unwrap().take(),
2941 &[NewGapChunk {
2942 previous: Some(ChunkIdentifier(3)),
2943 new: ChunkIdentifier(5),
2944 next: Some(ChunkIdentifier(1)),
2945 gap: (),
2946 }]
2947 );
2948 }
2949
2950 {
2952 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
2954 let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
2955
2956 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
2957
2958 assert_eq!(
2959 linked_chunk.updates().unwrap().take(),
2960 &[
2961 NewItemsChunk {
2962 previous: Some(ChunkIdentifier(5)),
2963 new: ChunkIdentifier(6),
2964 next: Some(ChunkIdentifier(1)),
2965 },
2966 RemoveChunk(ChunkIdentifier(5)),
2967 ]
2968 );
2969
2970 linked_chunk.insert_gap_at((), position)?;
2971
2972 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
2973 assert_eq!(
2974 linked_chunk.updates().unwrap().take(),
2975 &[NewGapChunk {
2976 previous: Some(ChunkIdentifier(3)),
2977 new: ChunkIdentifier(7),
2978 next: Some(ChunkIdentifier(6)),
2979 gap: (),
2980 }]
2981 );
2982 }
2983
2984 {
2986 assert_matches!(
2987 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)),
2988 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2989 );
2990 assert!(linked_chunk.updates().unwrap().take().is_empty());
2991 }
2992
2993 {
2995 assert_matches!(
2996 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)),
2997 Err(Error::InvalidItemIndex { index: 128 })
2998 );
2999 assert!(linked_chunk.updates().unwrap().take().is_empty());
3000 }
3001
3002 {
3004 let position_of_a_gap = Position(ChunkIdentifier(2), 0);
3007 assert_matches!(
3008 linked_chunk.insert_gap_at((), position_of_a_gap),
3009 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
3010 );
3011 assert!(linked_chunk.updates().unwrap().take().is_empty());
3012 }
3013
3014 assert_eq!(linked_chunk.num_items(), 6);
3015
3016 Ok(())
3017 }
3018
3019 #[test]
3020 fn test_replace_gap_at_middle() -> Result<(), Error> {
3021 use super::Update::*;
3022
3023 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3024
3025 let _ = linked_chunk.updates().unwrap().take();
3027
3028 linked_chunk.push_items_back(['a', 'b']);
3029 linked_chunk.push_gap_back(());
3030 linked_chunk.push_items_back(['l', 'm']);
3031 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
3032 assert_eq!(
3033 linked_chunk.updates().unwrap().take(),
3034 &[
3035 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3036 NewGapChunk {
3037 previous: Some(ChunkIdentifier(0)),
3038 new: ChunkIdentifier(1),
3039 next: None,
3040 gap: (),
3041 },
3042 NewItemsChunk {
3043 previous: Some(ChunkIdentifier(1)),
3044 new: ChunkIdentifier(2),
3045 next: None,
3046 },
3047 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
3048 ]
3049 );
3050
3051 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3053 assert_eq!(gap_identifier, ChunkIdentifier(1));
3054
3055 let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
3056 assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
3057 assert_items_eq!(
3058 linked_chunk,
3059 ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
3060 );
3061 assert_eq!(
3062 linked_chunk.updates().unwrap().take(),
3063 &[
3064 NewItemsChunk {
3065 previous: Some(ChunkIdentifier(1)),
3066 new: ChunkIdentifier(3),
3067 next: Some(ChunkIdentifier(2)),
3068 },
3069 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
3070 NewItemsChunk {
3071 previous: Some(ChunkIdentifier(3)),
3072 new: ChunkIdentifier(4),
3073 next: Some(ChunkIdentifier(2)),
3074 },
3075 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
3076 RemoveChunk(ChunkIdentifier(1)),
3077 ]
3078 );
3079
3080 assert_eq!(linked_chunk.num_items(), 9);
3081
3082 Ok(())
3083 }
3084
3085 #[test]
3086 fn test_replace_gap_at_end() -> Result<(), Error> {
3087 use super::Update::*;
3088
3089 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3090
3091 let _ = linked_chunk.updates().unwrap().take();
3093
3094 linked_chunk.push_items_back(['a', 'b']);
3095 linked_chunk.push_gap_back(());
3096 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3097 assert_eq!(
3098 linked_chunk.updates().unwrap().take(),
3099 &[
3100 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3101 NewGapChunk {
3102 previous: Some(ChunkIdentifier(0)),
3103 new: ChunkIdentifier(1),
3104 next: None,
3105 gap: (),
3106 },
3107 ]
3108 );
3109
3110 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3112 assert_eq!(gap_identifier, ChunkIdentifier(1));
3113
3114 let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3115 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3116 assert_items_eq!(
3117 linked_chunk,
3118 ['a', 'b'] ['w', 'x', 'y'] ['z']
3119 );
3120 assert_eq!(
3121 linked_chunk.updates().unwrap().take(),
3122 &[
3123 NewItemsChunk {
3124 previous: Some(ChunkIdentifier(1)),
3125 new: ChunkIdentifier(2),
3126 next: None,
3127 },
3128 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3129 NewItemsChunk {
3130 previous: Some(ChunkIdentifier(2)),
3131 new: ChunkIdentifier(3),
3132 next: None,
3133 },
3134 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3135 RemoveChunk(ChunkIdentifier(1)),
3136 ]
3137 );
3138
3139 assert_eq!(linked_chunk.num_items(), 6);
3140
3141 Ok(())
3142 }
3143
3144 #[test]
3145 fn test_replace_gap_at_beginning() -> Result<(), Error> {
3146 use super::Update::*;
3147
3148 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3149
3150 let _ = linked_chunk.updates().unwrap().take();
3152
3153 linked_chunk.push_items_back(['a', 'b']);
3154 assert_items_eq!(linked_chunk, ['a', 'b']);
3155 assert_eq!(
3156 linked_chunk.updates().unwrap().take(),
3157 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },]
3158 );
3159
3160 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3162 linked_chunk.insert_gap_at((), position_of_a).unwrap();
3163 assert_items_eq!(
3164 linked_chunk,
3165 [-] ['a', 'b']
3166 );
3167 assert_eq!(
3168 linked_chunk.updates().unwrap().take(),
3169 &[NewGapChunk {
3170 previous: None,
3171 new: ChunkIdentifier(1),
3172 next: Some(ChunkIdentifier(0)),
3173 gap: (),
3174 }]
3175 );
3176
3177 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3178 assert_eq!(gap_identifier, ChunkIdentifier(1));
3179
3180 let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3181 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3182 assert_items_eq!(
3183 linked_chunk,
3184 ['x'] ['a', 'b']
3185 );
3186 assert_eq!(
3187 linked_chunk.updates().unwrap().take(),
3188 &[
3189 NewItemsChunk {
3190 previous: Some(ChunkIdentifier(1)),
3191 new: ChunkIdentifier(2),
3192 next: Some(ChunkIdentifier(0)),
3193 },
3194 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3195 RemoveChunk(ChunkIdentifier(1)),
3196 ]
3197 );
3198
3199 assert_eq!(linked_chunk.num_items(), 3);
3200
3201 Ok(())
3202 }
3203
3204 #[test]
3205 fn test_remove_empty_chunk_at() -> Result<(), Error> {
3206 use super::Update::*;
3207
3208 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3209
3210 let _ = linked_chunk.updates().unwrap().take();
3212
3213 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3214 linked_chunk.push_items_back(['a', 'b']);
3215 linked_chunk.push_gap_back(());
3216 linked_chunk.push_items_back(['l', 'm']);
3217 linked_chunk.push_gap_back(());
3218 assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3219 assert_eq!(
3220 linked_chunk.updates().unwrap().take(),
3221 &[
3222 NewGapChunk {
3223 previous: None,
3224 new: ChunkIdentifier(1),
3225 next: Some(ChunkIdentifier(0)),
3226 gap: (),
3227 },
3228 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3229 NewGapChunk {
3230 previous: Some(ChunkIdentifier(0)),
3231 new: ChunkIdentifier(2),
3232 next: None,
3233 gap: (),
3234 },
3235 NewItemsChunk {
3236 previous: Some(ChunkIdentifier(2)),
3237 new: ChunkIdentifier(3),
3238 next: None,
3239 },
3240 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3241 NewGapChunk {
3242 previous: Some(ChunkIdentifier(3)),
3243 new: ChunkIdentifier(4),
3244 next: None,
3245 gap: (),
3246 },
3247 ]
3248 );
3249
3250 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3252 assert_matches!(err, Error::RemovingNonEmptyItemsChunk { .. });
3253
3254 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(42)).unwrap_err();
3256 assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3257
3258 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(2)).unwrap();
3260 let next = maybe_next.unwrap();
3261 assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3263 assert_eq!(next.index(), 0);
3264 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3265 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3266
3267 let next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(4)).unwrap();
3269 assert!(next.is_none());
3271 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3272 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3273
3274 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(1)).unwrap();
3276 let next = maybe_next.unwrap();
3277 assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3278 assert_eq!(next.index(), 0);
3279 assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3280 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3281
3282 Ok(())
3283 }
3284
3285 #[test]
3286 fn test_remove_empty_last_chunk() {
3287 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3288
3289 let _ = linked_chunk.updates().unwrap().take();
3291
3292 assert_items_eq!(linked_chunk, []);
3293 assert!(linked_chunk.updates().unwrap().take().is_empty());
3294
3295 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3297 assert_matches!(err, Error::RemovingLastChunk);
3298 }
3299
3300 #[test]
3301 fn test_chunk_item_positions() {
3302 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3303 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3304 linked_chunk.push_gap_back(());
3305 linked_chunk.push_items_back(['f']);
3306
3307 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3308
3309 let mut iterator = linked_chunk.chunks();
3310
3311 {
3313 let chunk = iterator.next().unwrap();
3314 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3315 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3316 }
3317
3318 {
3320 let chunk = iterator.next().unwrap();
3321 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3322 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3323 }
3324
3325 {
3327 let chunk = iterator.next().unwrap();
3328 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3329 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3330 }
3331
3332 {
3334 let chunk = iterator.next().unwrap();
3335 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3336 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3337 }
3338 }
3339
3340 #[test]
3341 fn test_is_first_and_last_chunk() {
3342 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3343
3344 let mut chunks = linked_chunk.chunks().peekable();
3345 assert!(chunks.peek().unwrap().is_first_chunk());
3346 assert!(chunks.next().unwrap().is_last_chunk());
3347 assert!(chunks.next().is_none());
3348
3349 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3350
3351 let mut chunks = linked_chunk.chunks().peekable();
3352 assert!(chunks.next().unwrap().is_first_chunk());
3353 assert!(chunks.peek().unwrap().is_first_chunk().not());
3354 assert!(chunks.next().unwrap().is_last_chunk().not());
3355 assert!(chunks.next().unwrap().is_last_chunk());
3356 assert!(chunks.next().is_none());
3357 }
3358
3359 #[test]
3364 fn test_clear() {
3365 let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3366
3367 let item = Arc::new('a');
3368 let gap = Arc::new(());
3369
3370 linked_chunk.push_items_back([
3371 item.clone(),
3372 item.clone(),
3373 item.clone(),
3374 item.clone(),
3375 item.clone(),
3376 ]);
3377 linked_chunk.push_gap_back(gap.clone());
3378 linked_chunk.push_items_back([item.clone()]);
3379
3380 assert_eq!(Arc::strong_count(&item), 7);
3381 assert_eq!(Arc::strong_count(&gap), 2);
3382 assert_eq!(linked_chunk.num_items(), 6);
3383 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3384
3385 linked_chunk.clear();
3387
3388 assert_eq!(Arc::strong_count(&item), 1);
3389 assert_eq!(Arc::strong_count(&gap), 1);
3390 assert_eq!(linked_chunk.num_items(), 0);
3391 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3392 }
3393
3394 #[test]
3395 fn test_clear_emit_an_update_clear() {
3396 use super::Update::*;
3397
3398 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3399
3400 assert_eq!(
3401 linked_chunk.updates().unwrap().take(),
3402 &[NewItemsChunk {
3403 previous: None,
3404 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3405 next: None
3406 }]
3407 );
3408
3409 linked_chunk.clear();
3410
3411 assert_eq!(
3412 linked_chunk.updates().unwrap().take(),
3413 &[
3414 Clear,
3415 NewItemsChunk {
3416 previous: None,
3417 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3418 next: None
3419 }
3420 ]
3421 );
3422 }
3423
3424 #[test]
3425 fn test_replace_item() {
3426 use super::Update::*;
3427
3428 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3429
3430 linked_chunk.push_items_back(['a', 'b', 'c']);
3431 linked_chunk.push_gap_back(());
3432 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3434
3435 let _ = linked_chunk.updates().unwrap().take();
3437
3438 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3440 assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3441
3442 assert_eq!(
3443 linked_chunk.updates().unwrap().take(),
3444 &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3445 );
3446
3447 assert_matches!(
3449 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3450 Err(Error::InvalidItemIndex { index: 3 })
3451 );
3452
3453 assert_matches!(
3455 linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3456 Err(Error::ChunkIsAGap { .. })
3457 );
3458 }
3459
3460 #[test]
3461 fn test_lazy_previous() {
3462 use std::marker::PhantomData;
3463
3464 use super::{Ends, ObservableUpdates, Update::*};
3465
3466 let first_chunk_identifier = ChunkIdentifier(0);
3468 let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3469 unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3470
3471 let mut linked_chunk = LinkedChunk::<3, char, ()> {
3472 links: Ends { first: first_loaded_chunk, last: None },
3473 chunk_identifier_generator:
3474 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3475 updates: Some(ObservableUpdates::new()),
3476 marker: PhantomData,
3477 };
3478
3479 {
3482 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3483
3484 assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3485
3486 {
3488 let mut chunks = linked_chunk.chunks();
3489
3490 assert_matches!(chunks.next(), Some(chunk) => {
3491 assert_eq!(chunk.identifier(), 1);
3492 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3493 });
3494 assert_matches!(chunks.next(), Some(chunk) => {
3495 assert_eq!(chunk.identifier(), 2);
3496 assert!(chunk.lazy_previous.is_none());
3497 });
3498 assert!(chunks.next().is_none());
3499 }
3500
3501 assert_eq!(
3503 linked_chunk.updates().unwrap().take(),
3504 &[
3505 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3506 NewItemsChunk {
3507 previous: Some(ChunkIdentifier(1)),
3508 new: ChunkIdentifier(2),
3509 next: None,
3510 },
3511 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3512 ]
3513 );
3514 }
3515
3516 {
3518 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3519
3520 assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3521
3522 {
3524 let mut chunks = linked_chunk.chunks();
3525
3526 assert_matches!(chunks.next(), Some(chunk) => {
3527 assert_eq!(chunk.identifier(), 3);
3528 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3530 });
3531 assert_matches!(chunks.next(), Some(chunk) => {
3532 assert_eq!(chunk.identifier(), 1);
3533 assert!(chunk.lazy_previous.is_none());
3535 });
3536 assert_matches!(chunks.next(), Some(chunk) => {
3537 assert_eq!(chunk.identifier(), 2);
3538 assert!(chunk.lazy_previous.is_none());
3539 });
3540 assert!(chunks.next().is_none());
3541 }
3542
3543 assert_eq!(
3545 linked_chunk.updates().unwrap().take(),
3546 &[NewGapChunk {
3547 previous: Some(ChunkIdentifier(0)),
3549 new: ChunkIdentifier(3),
3550 next: Some(ChunkIdentifier(1)),
3551 gap: ()
3552 }]
3553 );
3554 }
3555
3556 {
3558 linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3559
3560 assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3561
3562 {
3564 let mut chunks = linked_chunk.chunks();
3565
3566 assert_matches!(chunks.next(), Some(chunk) => {
3567 assert_eq!(chunk.identifier(), 4);
3568 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3570 });
3571 assert_matches!(chunks.next(), Some(chunk) => {
3572 assert_eq!(chunk.identifier(), 5);
3573 assert!(chunk.lazy_previous.is_none());
3574 });
3575 assert_matches!(chunks.next(), Some(chunk) => {
3576 assert_eq!(chunk.identifier(), 1);
3577 assert!(chunk.lazy_previous.is_none());
3578 });
3579 assert_matches!(chunks.next(), Some(chunk) => {
3580 assert_eq!(chunk.identifier(), 2);
3581 assert!(chunk.lazy_previous.is_none());
3582 });
3583 assert!(chunks.next().is_none());
3584 }
3585
3586 assert_eq!(
3588 linked_chunk.updates().unwrap().take(),
3589 &[
3590 NewItemsChunk {
3592 previous: Some(ChunkIdentifier(3)),
3593 new: ChunkIdentifier(4),
3594 next: Some(ChunkIdentifier(1)),
3595 },
3596 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3598 NewItemsChunk {
3600 previous: Some(ChunkIdentifier(4)),
3601 new: ChunkIdentifier(5),
3602 next: Some(ChunkIdentifier(1)),
3603 },
3604 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3606 RemoveChunk(ChunkIdentifier(3)),
3608 ]
3609 );
3610 }
3611
3612 {
3616 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3617
3618 assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3619
3620 {
3622 let mut chunks = linked_chunk.chunks();
3623
3624 assert_matches!(chunks.next(), Some(chunk) => {
3625 assert_eq!(chunk.identifier(), 6);
3626 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3628 });
3629 assert_matches!(chunks.next(), Some(chunk) => {
3630 assert_eq!(chunk.identifier(), 4);
3631 assert!(chunk.lazy_previous.is_none());
3633 });
3634 assert_matches!(chunks.next(), Some(chunk) => {
3635 assert_eq!(chunk.identifier(), 5);
3636 assert!(chunk.lazy_previous.is_none());
3637 });
3638 assert_matches!(chunks.next(), Some(chunk) => {
3639 assert_eq!(chunk.identifier(), 1);
3640 assert!(chunk.lazy_previous.is_none());
3641 });
3642 assert_matches!(chunks.next(), Some(chunk) => {
3643 assert_eq!(chunk.identifier(), 2);
3644 assert!(chunk.lazy_previous.is_none());
3645 });
3646 assert!(chunks.next().is_none());
3647 }
3648
3649 assert_eq!(
3651 linked_chunk.updates().unwrap().take(),
3652 &[NewGapChunk {
3653 previous: Some(ChunkIdentifier(0)),
3655 new: ChunkIdentifier(6),
3656 next: Some(ChunkIdentifier(4)),
3657 gap: ()
3658 }]
3659 );
3660 }
3661 }
3662}