1#![allow(rustdoc::private_intra_doc_links)]
16
17#[cfg(test)]
32macro_rules! assert_items_eq {
33 ( @_ [ $iterator:ident ] { [-] $( $rest:tt )* } { $( $accumulator:tt )* } ) => {
34 assert_items_eq!(
35 @_
36 [ $iterator ]
37 { $( $rest )* }
38 {
39 $( $accumulator )*
40 {
41 let chunk = $iterator .next().expect("next chunk (expect gap)");
42 assert!(chunk.is_gap(), "chunk should be a gap");
43 }
44 }
45 )
46 };
47
48 ( @_ [ $iterator:ident ] { [ $( $item:expr ),* ] $( $rest:tt )* } { $( $accumulator:tt )* } ) => {
49 assert_items_eq!(
50 @_
51 [ $iterator ]
52 { $( $rest )* }
53 {
54 $( $accumulator )*
55 {
56 let chunk = $iterator .next().expect("next chunk (expect items)");
57 assert!(chunk.is_items(), "chunk should contain items");
58
59 let $crate::linked_chunk::ChunkContent::Items(items) = chunk.content() else {
60 unreachable!()
61 };
62
63 let mut items_iterator = items.iter();
64
65 $(
66 assert_eq!(items_iterator.next(), Some(& $item ));
67 )*
68
69 assert!(items_iterator.next().is_none(), "no more items");
70 }
71 }
72 )
73 };
74
75 ( @_ [ $iterator:ident ] {} { $( $accumulator:tt )* } ) => {
76 {
77 $( $accumulator )*
78 assert!( $iterator .next().is_none(), "no more chunks");
79 }
80 };
81
82 ( $linked_chunk:expr, $( $all:tt )* ) => {
83 assert_items_eq!(
84 @_
85 [ iterator ]
86 { $( $all )* }
87 {
88 let mut iterator = $linked_chunk.chunks();
89 }
90 )
91 }
92}
93
94mod as_vector;
95pub mod lazy_loader;
96mod order_tracker;
97pub mod relational;
98mod updates;
99
100use std::{
101 fmt::{self, Display},
102 marker::PhantomData,
103 ptr::NonNull,
104 sync::atomic::{self, AtomicU64},
105};
106
107pub use as_vector::*;
108pub use order_tracker::OrderTracker;
109use ruma::{OwnedRoomId, RoomId};
110pub use updates::*;
111
112#[derive(Debug, Clone, Copy, PartialEq)]
114pub enum LinkedChunkId<'a> {
115 Room(&'a RoomId),
116 }
119
120impl LinkedChunkId<'_> {
121 pub fn storage_key(&self) -> impl '_ + AsRef<[u8]> {
122 match self {
123 LinkedChunkId::Room(room_id) => room_id,
124 }
125 }
126
127 pub fn to_owned(&self) -> OwnedLinkedChunkId {
128 match self {
129 LinkedChunkId::Room(room_id) => OwnedLinkedChunkId::Room((*room_id).to_owned()),
130 }
131 }
132}
133
134impl PartialEq<&OwnedLinkedChunkId> for LinkedChunkId<'_> {
135 fn eq(&self, other: &&OwnedLinkedChunkId) -> bool {
136 match (self, other) {
137 (LinkedChunkId::Room(a), OwnedLinkedChunkId::Room(b)) => *a == b,
138 }
139 }
140}
141
142impl PartialEq<LinkedChunkId<'_>> for OwnedLinkedChunkId {
143 fn eq(&self, other: &LinkedChunkId<'_>) -> bool {
144 other.eq(&self)
145 }
146}
147
148#[derive(Clone, Debug, PartialEq, Eq, Hash)]
150pub enum OwnedLinkedChunkId {
151 Room(OwnedRoomId),
152 }
155
156impl Display for OwnedLinkedChunkId {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 match self {
159 OwnedLinkedChunkId::Room(room_id) => write!(f, "{room_id}"),
160 }
161 }
162}
163
164impl OwnedLinkedChunkId {
165 #[cfg(test)]
166 fn as_ref(&self) -> LinkedChunkId<'_> {
167 match self {
168 OwnedLinkedChunkId::Room(room_id) => LinkedChunkId::Room(room_id.as_ref()),
169 }
170 }
171
172 pub fn room_id(&self) -> &RoomId {
173 match self {
174 OwnedLinkedChunkId::Room(room_id) => room_id,
175 }
176 }
177}
178
179#[derive(thiserror::Error, Debug)]
181pub enum Error {
182 #[error("The chunk identifier is invalid: `{identifier:?}`")]
184 InvalidChunkIdentifier {
185 identifier: ChunkIdentifier,
187 },
188
189 #[error("The chunk is a gap: `{identifier:?}`")]
191 ChunkIsAGap {
192 identifier: ChunkIdentifier,
194 },
195
196 #[error("The chunk is an item: `{identifier:?}`")]
198 ChunkIsItems {
199 identifier: ChunkIdentifier,
201 },
202
203 #[error("The chunk is a non-empty item chunk: `{identifier:?}`")]
205 RemovingNonEmptyItemsChunk {
206 identifier: ChunkIdentifier,
208 },
209
210 #[error("Trying to remove the only chunk, but a linked chunk can't be empty")]
213 RemovingLastChunk,
214
215 #[error("The item index is invalid: `{index}`")]
217 InvalidItemIndex {
218 index: usize,
220 },
221}
222
223struct Ends<const CHUNK_CAPACITY: usize, Item, Gap> {
228 first: NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>,
230 last: Option<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
232}
233
234impl<const CAP: usize, Item, Gap> Ends<CAP, Item, Gap> {
235 fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
237 unsafe { self.first.as_ref() }
238 }
239
240 fn first_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
242 unsafe { self.first.as_mut() }
243 }
244
245 fn latest_chunk(&self) -> &Chunk<CAP, Item, Gap> {
247 unsafe { self.last.unwrap_or(self.first).as_ref() }
248 }
249
250 fn latest_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
252 unsafe { self.last.as_mut().unwrap_or(&mut self.first).as_mut() }
253 }
254
255 fn chunk(&self, identifier: ChunkIdentifier) -> Option<&Chunk<CAP, Item, Gap>> {
257 let mut chunk = self.latest_chunk();
258
259 loop {
260 if chunk.identifier() == identifier {
261 return Some(chunk);
262 }
263
264 chunk = chunk.previous()?;
265 }
266 }
267
268 fn chunk_mut(&mut self, identifier: ChunkIdentifier) -> Option<&mut Chunk<CAP, Item, Gap>> {
270 let mut chunk = self.latest_chunk_mut();
271
272 loop {
273 if chunk.identifier() == identifier {
274 return Some(chunk);
275 }
276
277 chunk = chunk.previous_mut()?;
278 }
279 }
280
281 unsafe fn clear(&mut self) {
288 let mut current_chunk_ptr = self.last.or(Some(self.first));
291
292 while let Some(chunk_ptr) = current_chunk_ptr {
294 let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
296
297 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
299
300 current_chunk_ptr = previous_ptr;
302 }
303
304 self.first = NonNull::dangling();
306 self.last = None;
307 }
308
309 fn replace_with(&mut self, first_chunk: NonNull<Chunk<CAP, Item, Gap>>) {
312 unsafe {
314 self.clear();
315 }
316
317 self.first = first_chunk;
319 }
320
321 fn reset(&mut self) {
326 self.replace_with(Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER));
327 }
328}
329
330pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
337 links: Ends<CHUNK_CAPACITY, Item, Gap>,
339
340 chunk_identifier_generator: ChunkIdentifierGenerator,
342
343 updates: Option<ObservableUpdates<Item, Gap>>,
347
348 marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
350}
351
352impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
353 fn default() -> Self {
354 Self::new()
355 }
356}
357
358impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
359 pub fn new() -> Self {
361 Self {
362 links: Ends {
363 first: Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER),
364 last: None,
365 },
366 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
367 updates: None,
368 marker: PhantomData,
369 }
370 }
371
372 pub fn new_with_update_history() -> Self {
378 let first_chunk_identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
379
380 let mut updates = ObservableUpdates::new();
381 updates.push(Update::NewItemsChunk {
382 previous: None,
383 new: first_chunk_identifier,
384 next: None,
385 });
386
387 Self {
388 links: Ends { first: Chunk::new_items_leaked(first_chunk_identifier), last: None },
389 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
390 updates: Some(updates),
391 marker: PhantomData,
392 }
393 }
394
395 pub fn clear(&mut self) {
397 self.links.reset();
399
400 self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
402
403 if let Some(updates) = self.updates.as_mut() {
405 updates.clear_pending();
408 updates.push(Update::Clear);
409 updates.push(Update::NewItemsChunk {
410 previous: None,
411 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
412 next: None,
413 })
414 }
415 }
416
417 pub fn push_items_back<I>(&mut self, items: I)
423 where
424 Item: Clone,
425 Gap: Clone,
426 I: IntoIterator<Item = Item>,
427 I::IntoIter: ExactSizeIterator,
428 {
429 let items = items.into_iter();
430
431 let last_chunk = self.links.latest_chunk_mut();
432
433 let last_chunk =
435 last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
436
437 debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
438
439 if !last_chunk.is_first_chunk() {
443 self.links.last = Some(last_chunk.as_ptr());
446 }
447 }
448
449 pub fn push_gap_back(&mut self, content: Gap)
452 where
453 Item: Clone,
454 Gap: Clone,
455 {
456 let last_chunk = self.links.latest_chunk_mut();
457 last_chunk.insert_next(
458 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
459 &mut self.updates,
460 );
461
462 self.links.last = last_chunk.next;
463 }
464
465 pub fn insert_items_at<I>(&mut self, position: Position, items: I) -> Result<(), Error>
470 where
471 Item: Clone,
472 Gap: Clone,
473 I: IntoIterator<Item = Item>,
474 I::IntoIter: ExactSizeIterator,
475 {
476 let chunk_identifier = position.chunk_identifier();
477 let item_index = position.index();
478
479 let chunk = self
480 .links
481 .chunk_mut(chunk_identifier)
482 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
483
484 let chunk = match &mut chunk.content {
485 ChunkContent::Gap(..) => {
486 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
487 }
488
489 ChunkContent::Items(current_items) => {
490 let current_items_length = current_items.len();
491
492 if item_index > current_items_length {
493 return Err(Error::InvalidItemIndex { index: item_index });
494 }
495
496 let items = items.into_iter();
498
499 if item_index == current_items_length {
501 chunk
502 .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
504 }
505 else {
507 if let Some(updates) = self.updates.as_mut() {
508 updates.push(Update::DetachLastItems {
509 at: Position(chunk_identifier, item_index),
510 });
511 }
512
513 let detached_items = current_items.split_off(item_index);
515
516 let chunk = chunk
517 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
519
520 if let Some(updates) = self.updates.as_mut() {
521 updates.push(Update::StartReattachItems);
522 }
523
524 let chunk = chunk
525 .push_items(
527 detached_items.into_iter(),
528 &self.chunk_identifier_generator,
529 &mut self.updates,
530 );
531
532 if let Some(updates) = self.updates.as_mut() {
533 updates.push(Update::EndReattachItems);
534 }
535
536 chunk
537 }
538 }
539 };
540
541 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
544 self.links.last = Some(chunk.as_ptr());
547 }
548
549 Ok(())
550 }
551
552 pub fn remove_item_at(&mut self, position: Position) -> Result<Item, Error> {
560 let chunk_identifier = position.chunk_identifier();
561 let item_index = position.index();
562
563 let mut chunk_ptr = None;
564 let removed_item;
565
566 {
567 let chunk = self
568 .links
569 .chunk_mut(chunk_identifier)
570 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
571
572 let current_items = match &mut chunk.content {
573 ChunkContent::Gap(..) => {
574 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
575 }
576 ChunkContent::Items(current_items) => current_items,
577 };
578
579 if item_index > current_items.len() {
580 return Err(Error::InvalidItemIndex { index: item_index });
581 }
582
583 removed_item = current_items.remove(item_index);
584 if let Some(updates) = self.updates.as_mut() {
585 updates.push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
586 }
587
588 if current_items.is_empty() && !chunk.is_first_chunk() {
590 chunk.unlink(self.updates.as_mut());
592
593 chunk_ptr = Some(chunk.as_ptr());
594
595 if chunk.is_last_chunk() {
598 self.links.last = chunk.previous;
599 }
600 }
601
602 }
604
605 if let Some(chunk_ptr) = chunk_ptr {
606 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
613 }
614
615 Ok(removed_item)
616 }
617
618 pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
623 where
624 Item: Clone,
625 {
626 let chunk_identifier = position.chunk_identifier();
627 let item_index = position.index();
628
629 let chunk = self
630 .links
631 .chunk_mut(chunk_identifier)
632 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
633
634 match &mut chunk.content {
635 ChunkContent::Gap(..) => {
636 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
637 }
638
639 ChunkContent::Items(current_items) => {
640 if item_index >= current_items.len() {
641 return Err(Error::InvalidItemIndex { index: item_index });
642 }
643
644 if let Some(updates) = self.updates.as_mut() {
646 updates.push(Update::ReplaceItem {
647 at: Position(chunk_identifier, item_index),
648 item: item.clone(),
649 });
650 }
651
652 current_items[item_index] = item;
653 }
654 }
655
656 Ok(())
657 }
658
659 pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
664 where
665 Item: Clone,
666 Gap: Clone,
667 {
668 let chunk_identifier = position.chunk_identifier();
669 let item_index = position.index();
670
671 let chunk = self
672 .links
673 .chunk_mut(chunk_identifier)
674 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
675
676 let chunk = match &mut chunk.content {
677 ChunkContent::Gap(..) => {
678 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
679 }
680
681 ChunkContent::Items(current_items) => {
682 if item_index == 0 {
686 let chunk_was_first = chunk.is_first_chunk();
687 let chunk_was_last = chunk.is_last_chunk();
688
689 let new_chunk = chunk.insert_before(
690 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
691 self.updates.as_mut(),
692 );
693
694 let new_chunk_ptr = new_chunk.as_ptr();
695 let chunk_ptr = chunk.as_ptr();
696
697 if chunk_was_first {
702 self.links.first = new_chunk_ptr;
703
704 if chunk_was_last {
706 self.links.last = Some(chunk_ptr);
707 }
708 }
709
710 return Ok(());
711 }
712
713 let current_items_length = current_items.len();
714
715 if item_index >= current_items_length {
716 return Err(Error::InvalidItemIndex { index: item_index });
717 }
718
719 if let Some(updates) = self.updates.as_mut() {
720 updates.push(Update::DetachLastItems {
721 at: Position(chunk_identifier, item_index),
722 });
723 }
724
725 let detached_items = current_items.split_off(item_index);
727
728 let chunk = chunk
729 .insert_next(
731 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
732 &mut self.updates,
733 );
734
735 if let Some(updates) = self.updates.as_mut() {
736 updates.push(Update::StartReattachItems);
737 }
738
739 let chunk = chunk
740 .insert_next(
742 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
743 &mut self.updates,
744 )
745 .push_items(
747 detached_items.into_iter(),
748 &self.chunk_identifier_generator,
749 &mut self.updates,
750 );
751
752 if let Some(updates) = self.updates.as_mut() {
753 updates.push(Update::EndReattachItems);
754 }
755
756 chunk
757 }
758 };
759
760 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
763 self.links.last = Some(chunk.as_ptr());
766 }
767
768 Ok(())
769 }
770
771 pub fn remove_empty_chunk_at(
780 &mut self,
781 chunk_identifier: ChunkIdentifier,
782 ) -> Result<Option<Position>, Error> {
783 if self.links.first_chunk().is_last_chunk() {
785 return Err(Error::RemovingLastChunk);
786 }
787
788 let chunk = self
789 .links
790 .chunk_mut(chunk_identifier)
791 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
792
793 if chunk.num_items() > 0 {
794 return Err(Error::RemovingNonEmptyItemsChunk { identifier: chunk_identifier });
795 }
796
797 let chunk_was_first = chunk.is_first_chunk();
798 let chunk_was_last = chunk.is_last_chunk();
799 let next_ptr = chunk.next;
800 let previous_ptr = chunk.previous;
801 let position_of_next = chunk.next().map(|next| next.first_position());
802
803 chunk.unlink(self.updates.as_mut());
804
805 let chunk_ptr = chunk.as_ptr();
806
807 if chunk_was_first {
809 if let Some(next_ptr) = next_ptr {
811 self.links.first = next_ptr;
812 }
813 }
814
815 if chunk_was_last {
816 self.links.last = previous_ptr;
817 }
818
819 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
822
823 Ok(position_of_next)
825 }
826
827 pub fn replace_gap_at<I>(
835 &mut self,
836 items: I,
837 chunk_identifier: ChunkIdentifier,
838 ) -> Result<&Chunk<CAP, Item, Gap>, Error>
839 where
840 Item: Clone,
841 Gap: Clone,
842 I: IntoIterator<Item = Item>,
843 I::IntoIter: ExactSizeIterator,
844 {
845 let chunk_ptr;
846 let new_chunk_ptr;
847
848 {
849 let chunk = self
850 .links
851 .chunk_mut(chunk_identifier)
852 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
853
854 if chunk.is_items() {
855 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
856 }
857
858 let chunk_was_first = chunk.is_first_chunk();
859
860 let maybe_last_chunk_ptr = {
861 let items = items.into_iter();
862
863 let last_inserted_chunk = chunk
864 .insert_next(
866 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
867 &mut self.updates,
868 )
869 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
871
872 last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
873 };
874
875 new_chunk_ptr = chunk
876 .next
877 .unwrap();
879
880 chunk.unlink(self.updates.as_mut());
882
883 chunk_ptr = chunk.as_ptr();
885
886 if chunk_was_first {
888 self.links.first = new_chunk_ptr;
889 }
890
891 if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
894 self.links.last = Some(last_chunk_ptr);
895 }
896
897 }
899
900 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
905
906 Ok(
907 unsafe { new_chunk_ptr.as_ref() },
911 )
912 }
913
914 pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
916 where
917 P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
918 {
919 self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
920 }
921
922 pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
924 where
925 P: FnMut(&'a Item) -> bool,
926 {
927 self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
928 }
929
930 pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
934 IterBackward::new(self.links.latest_chunk())
935 }
936
937 pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
941 Iter::new(self.links.first_chunk())
942 }
943
944 pub fn rchunks_from(
949 &self,
950 identifier: ChunkIdentifier,
951 ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
952 Ok(IterBackward::new(
953 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
954 ))
955 }
956
957 pub fn chunks_from(
962 &self,
963 identifier: ChunkIdentifier,
964 ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
965 Ok(Iter::new(
966 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
967 ))
968 }
969
970 pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
974 self.ritems_from(self.links.latest_chunk().last_position())
975 .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
976 }
977
978 pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
982 let first_chunk = self.links.first_chunk();
983
984 self.items_from(first_chunk.first_position())
985 .expect("`items` cannot fail because at least one empty chunk must exist")
986 }
987
988 pub fn ritems_from(
992 &self,
993 position: Position,
994 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
995 Ok(self
996 .rchunks_from(position.chunk_identifier())?
997 .filter_map(|chunk| match &chunk.content {
998 ChunkContent::Gap(..) => None,
999 ChunkContent::Items(items) => {
1000 let identifier = chunk.identifier();
1001
1002 Some(
1003 items.iter().enumerate().rev().map(move |(item_index, item)| {
1004 (Position(identifier, item_index), item)
1005 }),
1006 )
1007 }
1008 })
1009 .flatten()
1010 .skip_while({
1011 let expected_index = position.index();
1012
1013 move |(Position(chunk_identifier, item_index), _item)| {
1014 *chunk_identifier == position.chunk_identifier()
1015 && *item_index != expected_index
1016 }
1017 }))
1018 }
1019
1020 pub fn items_from(
1024 &self,
1025 position: Position,
1026 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1027 Ok(self
1028 .chunks_from(position.chunk_identifier())?
1029 .filter_map(|chunk| match &chunk.content {
1030 ChunkContent::Gap(..) => None,
1031 ChunkContent::Items(items) => {
1032 let identifier = chunk.identifier();
1033
1034 Some(
1035 items.iter().enumerate().map(move |(item_index, item)| {
1036 (Position(identifier, item_index), item)
1037 }),
1038 )
1039 }
1040 })
1041 .flatten()
1042 .skip(position.index()))
1043 }
1044
1045 #[must_use]
1057 pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
1058 self.updates.as_mut()
1059 }
1060
1061 pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
1068 let (updates, token) = self
1069 .updates
1070 .as_mut()
1071 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1072 let chunk_iterator = self.chunks();
1073
1074 Some(AsVector::new(updates, token, chunk_iterator))
1075 }
1076
1077 pub fn order_tracker(
1085 &mut self,
1086 all_chunks: Option<Vec<ChunkMetadata>>,
1087 ) -> Option<OrderTracker<Item, Gap>>
1088 where
1089 Item: Clone,
1090 {
1091 let (updates, token) = self
1092 .updates
1093 .as_mut()
1094 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1095
1096 Some(OrderTracker::new(
1097 updates,
1098 token,
1099 all_chunks.unwrap_or_else(|| {
1100 self.chunks()
1102 .map(|chunk| ChunkMetadata {
1103 identifier: chunk.identifier(),
1104 num_items: chunk.num_items(),
1105 previous: chunk.previous().map(|prev| prev.identifier()),
1106 next: chunk.next().map(|next| next.identifier()),
1107 })
1108 .collect()
1109 }),
1110 ))
1111 }
1112
1113 pub fn num_items(&self) -> usize {
1115 self.items().count()
1116 }
1117}
1118
1119impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
1120 fn drop(&mut self) {
1121 unsafe {
1131 self.links.clear();
1132 }
1133 }
1134}
1135
1136unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1140
1141unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1145
1146#[derive(Debug)]
1156pub struct ChunkIdentifierGenerator {
1157 next: AtomicU64,
1158}
1159
1160impl ChunkIdentifierGenerator {
1161 const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1163
1164 pub fn new_from_scratch() -> Self {
1167 Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1168 }
1169
1170 pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1173 Self { next: AtomicU64::new(last_chunk_identifier.0) }
1174 }
1175
1176 fn next(&self) -> ChunkIdentifier {
1181 let previous = self.next.fetch_add(1, atomic::Ordering::Relaxed);
1182
1183 if previous == u64::MAX {
1186 panic!(
1187 "No more chunk identifiers available. Congrats, you did it. \
1188 2^64 identifiers have been consumed."
1189 )
1190 }
1191
1192 ChunkIdentifier(previous + 1)
1193 }
1194
1195 #[doc(hidden)]
1199 pub fn current(&self) -> ChunkIdentifier {
1200 ChunkIdentifier(self.next.load(atomic::Ordering::Relaxed))
1201 }
1202}
1203
1204#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1210#[repr(transparent)]
1211pub struct ChunkIdentifier(u64);
1212
1213impl ChunkIdentifier {
1214 pub fn new(identifier: u64) -> Self {
1216 Self(identifier)
1217 }
1218
1219 pub fn index(&self) -> u64 {
1221 self.0
1222 }
1223}
1224
1225impl PartialEq<u64> for ChunkIdentifier {
1226 fn eq(&self, other: &u64) -> bool {
1227 self.0 == *other
1228 }
1229}
1230
1231#[derive(Copy, Clone, Debug, PartialEq)]
1235pub struct Position(ChunkIdentifier, usize);
1236
1237impl Position {
1238 pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1240 Self(chunk_identifier, index)
1241 }
1242
1243 pub fn chunk_identifier(&self) -> ChunkIdentifier {
1245 self.0
1246 }
1247
1248 pub fn index(&self) -> usize {
1250 self.1
1251 }
1252
1253 pub fn decrement_index(&mut self) {
1259 self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1260 }
1261
1262 pub fn increment_index(&mut self) {
1269 self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1270 }
1271}
1272
1273#[derive(Debug)]
1276pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1277 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1278}
1279
1280impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1281 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1283 Self { chunk: Some(from_chunk) }
1284 }
1285}
1286
1287impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1288 type Item = &'a Chunk<CAP, Item, Gap>;
1289
1290 fn next(&mut self) -> Option<Self::Item> {
1291 self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1292 }
1293}
1294
1295#[derive(Debug)]
1298pub struct Iter<'a, const CAP: usize, Item, Gap> {
1299 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1300}
1301
1302impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1303 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1305 Self { chunk: Some(from_chunk) }
1306 }
1307}
1308
1309impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1310 type Item = &'a Chunk<CAP, Item, Gap>;
1311
1312 fn next(&mut self) -> Option<Self::Item> {
1313 self.chunk.inspect(|chunk| self.chunk = chunk.next())
1314 }
1315}
1316
1317#[derive(Clone, Debug)]
1319pub enum ChunkContent<Item, Gap> {
1320 Gap(Gap),
1323
1324 Items(Vec<Item>),
1326}
1327
1328pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1330 previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1332
1333 lazy_previous: Option<ChunkIdentifier>,
1338
1339 next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1341
1342 identifier: ChunkIdentifier,
1344
1345 content: ChunkContent<Item, Gap>,
1347}
1348
1349impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1350 fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1352 Self::new(identifier, ChunkContent::Gap(content))
1353 }
1354
1355 fn new_items(identifier: ChunkIdentifier) -> Self {
1357 Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1358 }
1359
1360 fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1361 Self { previous: None, lazy_previous: None, next: None, identifier, content }
1362 }
1363
1364 fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1366 let chunk = Self::new(identifier, content);
1367 let chunk_box = Box::new(chunk);
1368
1369 NonNull::from(Box::leak(chunk_box))
1370 }
1371
1372 fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1374 let chunk = Self::new_gap(identifier, content);
1375 let chunk_box = Box::new(chunk);
1376
1377 NonNull::from(Box::leak(chunk_box))
1378 }
1379
1380 fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1382 let chunk = Self::new_items(identifier);
1383 let chunk_box = Box::new(chunk);
1384
1385 NonNull::from(Box::leak(chunk_box))
1386 }
1387
1388 pub fn as_ptr(&self) -> NonNull<Self> {
1390 NonNull::from(self)
1391 }
1392
1393 pub fn is_gap(&self) -> bool {
1395 matches!(self.content, ChunkContent::Gap(..))
1396 }
1397
1398 pub fn is_items(&self) -> bool {
1400 !self.is_gap()
1401 }
1402
1403 pub fn is_definitive_head(&self) -> bool {
1406 self.previous.is_none() && self.lazy_previous.is_none()
1407 }
1408
1409 fn is_first_chunk(&self) -> bool {
1411 self.previous.is_none()
1412 }
1413
1414 fn is_last_chunk(&self) -> bool {
1416 self.next.is_none()
1417 }
1418
1419 #[doc(hidden)]
1423 pub fn lazy_previous(&self) -> Option<ChunkIdentifier> {
1424 self.lazy_previous
1425 }
1426
1427 pub fn identifier(&self) -> ChunkIdentifier {
1429 self.identifier
1430 }
1431
1432 pub fn content(&self) -> &ChunkContent<Item, Gap> {
1434 &self.content
1435 }
1436
1437 pub fn first_position(&self) -> Position {
1441 Position(self.identifier(), 0)
1442 }
1443
1444 pub fn last_position(&self) -> Position {
1448 let identifier = self.identifier();
1449
1450 match &self.content {
1451 ChunkContent::Gap(..) => Position(identifier, 0),
1452 ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1453 }
1454 }
1455
1456 pub fn num_items(&self) -> usize {
1460 match &self.content {
1461 ChunkContent::Gap(..) => 0,
1462 ChunkContent::Items(items) => items.len(),
1463 }
1464 }
1465
1466 fn push_items<I>(
1478 &mut self,
1479 mut new_items: I,
1480 chunk_identifier_generator: &ChunkIdentifierGenerator,
1481 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1482 ) -> &mut Self
1483 where
1484 I: Iterator<Item = Item> + ExactSizeIterator,
1485 Item: Clone,
1486 Gap: Clone,
1487 {
1488 if new_items.len() == 0 {
1490 return self;
1491 }
1492
1493 let identifier = self.identifier();
1494 let prev_num_items = self.num_items();
1495
1496 match &mut self.content {
1497 ChunkContent::Gap(..) => {
1500 self
1501 .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1503 .push_items(new_items, chunk_identifier_generator, updates)
1506 }
1507
1508 ChunkContent::Items(items) => {
1509 let free_space = CAPACITY.saturating_sub(prev_num_items);
1511
1512 if new_items.len() <= free_space {
1514 let start = items.len();
1515 items.extend(new_items);
1516
1517 if let Some(updates) = updates.as_mut() {
1518 updates.push(Update::PushItems {
1519 at: Position(identifier, start),
1520 items: items[start..].to_vec(),
1521 });
1522 }
1523
1524 self
1526 } else {
1527 if free_space > 0 {
1528 let start = items.len();
1530 items.extend(new_items.by_ref().take(free_space));
1531
1532 if let Some(updates) = updates.as_mut() {
1533 updates.push(Update::PushItems {
1534 at: Position(identifier, start),
1535 items: items[start..].to_vec(),
1536 });
1537 }
1538 }
1539
1540 self
1541 .insert_next(
1543 Self::new_items_leaked(chunk_identifier_generator.next()),
1544 updates,
1545 )
1546 .push_items(new_items, chunk_identifier_generator, updates)
1549 }
1550 }
1551 }
1552 }
1553
1554 fn insert_next(
1559 &mut self,
1560 mut new_chunk_ptr: NonNull<Self>,
1561 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1562 ) -> &mut Self
1563 where
1564 Gap: Clone,
1565 {
1566 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1567
1568 if let Some(next_chunk) = self.next_mut() {
1570 next_chunk.previous = Some(new_chunk_ptr);
1572
1573 new_chunk.next = self.next;
1575 }
1576
1577 self.next = Some(new_chunk_ptr);
1579 new_chunk.previous = Some(self.as_ptr());
1581
1582 if let Some(updates) = updates.as_mut() {
1583 let previous = new_chunk.previous().map(Chunk::identifier);
1584 let new = new_chunk.identifier();
1585 let next = new_chunk.next().map(Chunk::identifier);
1586
1587 match new_chunk.content() {
1588 ChunkContent::Gap(gap) => {
1589 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1590 }
1591
1592 ChunkContent::Items(..) => {
1593 updates.push(Update::NewItemsChunk { previous, new, next })
1594 }
1595 }
1596 }
1597
1598 new_chunk
1599 }
1600
1601 fn insert_before(
1606 &mut self,
1607 mut new_chunk_ptr: NonNull<Self>,
1608 updates: Option<&mut ObservableUpdates<Item, Gap>>,
1609 ) -> &mut Self
1610 where
1611 Gap: Clone,
1612 {
1613 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1614
1615 if let Some(previous_chunk) = self.previous_mut() {
1617 previous_chunk.next = Some(new_chunk_ptr);
1619
1620 new_chunk.previous = self.previous;
1622 }
1623 else {
1626 new_chunk.lazy_previous = self.lazy_previous.take();
1627 }
1628
1629 self.previous = Some(new_chunk_ptr);
1631 new_chunk.next = Some(self.as_ptr());
1633
1634 if let Some(updates) = updates {
1635 let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1636 let new = new_chunk.identifier();
1637 let next = new_chunk.next().map(Chunk::identifier);
1638
1639 match new_chunk.content() {
1640 ChunkContent::Gap(gap) => {
1641 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1642 }
1643
1644 ChunkContent::Items(..) => {
1645 updates.push(Update::NewItemsChunk { previous, new, next })
1646 }
1647 }
1648 }
1649
1650 new_chunk
1651 }
1652
1653 fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1658 let previous_ptr = self.previous;
1659 let next_ptr = self.next;
1660 let lazy_previous = self.lazy_previous.take();
1664
1665 if let Some(previous) = self.previous_mut() {
1666 previous.next = next_ptr;
1667 }
1668
1669 if let Some(next) = self.next_mut() {
1670 next.previous = previous_ptr;
1671 next.lazy_previous = lazy_previous;
1672 }
1673
1674 if let Some(updates) = updates {
1675 updates.push(Update::RemoveChunk(self.identifier()));
1676 }
1677 }
1678
1679 fn previous(&self) -> Option<&Self> {
1681 self.previous.map(|non_null| unsafe { non_null.as_ref() })
1682 }
1683
1684 fn previous_mut(&mut self) -> Option<&mut Self> {
1686 self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1687 }
1688
1689 fn next(&self) -> Option<&Self> {
1691 self.next.map(|non_null| unsafe { non_null.as_ref() })
1692 }
1693
1694 fn next_mut(&mut self) -> Option<&mut Self> {
1696 self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1697 }
1698}
1699
1700impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<CAP, Item, Gap>
1701where
1702 Item: fmt::Debug,
1703 Gap: fmt::Debug,
1704{
1705 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1706 formatter
1707 .debug_struct("LinkedChunk")
1708 .field("first (deref)", unsafe { self.links.first.as_ref() })
1709 .field("last", &self.links.last)
1710 .finish_non_exhaustive()
1711 }
1712}
1713
1714impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1715where
1716 Item: fmt::Debug,
1717 Gap: fmt::Debug,
1718{
1719 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1720 formatter
1721 .debug_struct("Chunk")
1722 .field("identifier", &self.identifier)
1723 .field("content", &self.content)
1724 .field("previous", &self.previous)
1725 .field("ptr", &std::ptr::from_ref(self))
1726 .field("next", &self.next)
1727 .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1728 .finish()
1729 }
1730}
1731
1732#[derive(Clone, Debug)]
1738pub struct RawChunk<Item, Gap> {
1739 pub content: ChunkContent<Item, Gap>,
1741
1742 pub previous: Option<ChunkIdentifier>,
1744
1745 pub identifier: ChunkIdentifier,
1747
1748 pub next: Option<ChunkIdentifier>,
1750}
1751
1752#[derive(Clone, Debug)]
1755pub struct ChunkMetadata {
1756 pub num_items: usize,
1760
1761 pub previous: Option<ChunkIdentifier>,
1763
1764 pub identifier: ChunkIdentifier,
1766
1767 pub next: Option<ChunkIdentifier>,
1769}
1770
1771#[cfg(test)]
1772mod tests {
1773 use std::{
1774 ops::Not,
1775 sync::{atomic::Ordering, Arc},
1776 };
1777
1778 use assert_matches::assert_matches;
1779
1780 use super::{
1781 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Error, LinkedChunk,
1782 Position,
1783 };
1784
1785 #[test]
1786 fn test_chunk_identifier_generator() {
1787 let generator = ChunkIdentifierGenerator::new_from_scratch();
1788
1789 assert_eq!(generator.next(), ChunkIdentifier(1));
1790 assert_eq!(generator.next(), ChunkIdentifier(2));
1791 assert_eq!(generator.next(), ChunkIdentifier(3));
1792 assert_eq!(generator.next(), ChunkIdentifier(4));
1793
1794 let generator =
1795 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1796
1797 assert_eq!(generator.next(), ChunkIdentifier(43));
1798 assert_eq!(generator.next(), ChunkIdentifier(44));
1799 assert_eq!(generator.next(), ChunkIdentifier(45));
1800 assert_eq!(generator.next(), ChunkIdentifier(46));
1801 }
1802
1803 #[test]
1804 fn test_empty() {
1805 let items = LinkedChunk::<3, char, ()>::new();
1806
1807 assert_eq!(items.num_items(), 0);
1808
1809 }
1812
1813 #[test]
1814 fn test_updates() {
1815 assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1816 assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1817 }
1818
1819 #[test]
1820 fn test_new_with_initial_update() {
1821 use super::Update::*;
1822
1823 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1824
1825 assert_eq!(
1826 linked_chunk.updates().unwrap().take(),
1827 &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1828 );
1829 }
1830
1831 #[test]
1832 fn test_push_items() {
1833 use super::Update::*;
1834
1835 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1836
1837 let _ = linked_chunk.updates().unwrap().take();
1839
1840 linked_chunk.push_items_back(['a']);
1841
1842 assert_items_eq!(linked_chunk, ['a']);
1843 assert_eq!(
1844 linked_chunk.updates().unwrap().take(),
1845 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1846 );
1847
1848 linked_chunk.push_items_back(['b', 'c']);
1849 assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1850 assert_eq!(
1851 linked_chunk.updates().unwrap().take(),
1852 &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1853 );
1854
1855 linked_chunk.push_items_back(['d', 'e']);
1856 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
1857 assert_eq!(
1858 linked_chunk.updates().unwrap().take(),
1859 &[
1860 NewItemsChunk {
1861 previous: Some(ChunkIdentifier(0)),
1862 new: ChunkIdentifier(1),
1863 next: None
1864 },
1865 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1866 ]
1867 );
1868
1869 linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1870 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1871 assert_eq!(
1872 linked_chunk.updates().unwrap().take(),
1873 &[
1874 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1875 NewItemsChunk {
1876 previous: Some(ChunkIdentifier(1)),
1877 new: ChunkIdentifier(2),
1878 next: None,
1879 },
1880 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1881 NewItemsChunk {
1882 previous: Some(ChunkIdentifier(2)),
1883 new: ChunkIdentifier(3),
1884 next: None,
1885 },
1886 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1887 ]
1888 );
1889
1890 assert_eq!(linked_chunk.num_items(), 10);
1891 }
1892
1893 #[test]
1894 fn test_push_gap() {
1895 use super::Update::*;
1896
1897 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1898
1899 let _ = linked_chunk.updates().unwrap().take();
1901
1902 linked_chunk.push_items_back(['a']);
1903 assert_items_eq!(linked_chunk, ['a']);
1904 assert_eq!(
1905 linked_chunk.updates().unwrap().take(),
1906 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1907 );
1908
1909 linked_chunk.push_gap_back(());
1910 assert_items_eq!(linked_chunk, ['a'] [-]);
1911 assert_eq!(
1912 linked_chunk.updates().unwrap().take(),
1913 &[NewGapChunk {
1914 previous: Some(ChunkIdentifier(0)),
1915 new: ChunkIdentifier(1),
1916 next: None,
1917 gap: (),
1918 }]
1919 );
1920
1921 linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1922 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1923 assert_eq!(
1924 linked_chunk.updates().unwrap().take(),
1925 &[
1926 NewItemsChunk {
1927 previous: Some(ChunkIdentifier(1)),
1928 new: ChunkIdentifier(2),
1929 next: None,
1930 },
1931 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
1932 NewItemsChunk {
1933 previous: Some(ChunkIdentifier(2)),
1934 new: ChunkIdentifier(3),
1935 next: None,
1936 },
1937 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
1938 ]
1939 );
1940
1941 linked_chunk.push_gap_back(());
1942 linked_chunk.push_gap_back(()); assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
1944 assert_eq!(
1945 linked_chunk.updates().unwrap().take(),
1946 &[
1947 NewGapChunk {
1948 previous: Some(ChunkIdentifier(3)),
1949 new: ChunkIdentifier(4),
1950 next: None,
1951 gap: (),
1952 },
1953 NewGapChunk {
1954 previous: Some(ChunkIdentifier(4)),
1955 new: ChunkIdentifier(5),
1956 next: None,
1957 gap: (),
1958 }
1959 ]
1960 );
1961
1962 linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
1963 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
1964 assert_eq!(
1965 linked_chunk.updates().unwrap().take(),
1966 &[
1967 NewItemsChunk {
1968 previous: Some(ChunkIdentifier(5)),
1969 new: ChunkIdentifier(6),
1970 next: None,
1971 },
1972 PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
1973 NewItemsChunk {
1974 previous: Some(ChunkIdentifier(6)),
1975 new: ChunkIdentifier(7),
1976 next: None,
1977 },
1978 PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
1979 ]
1980 );
1981
1982 assert_eq!(linked_chunk.num_items(), 9);
1983 }
1984
1985 #[test]
1986 fn test_identifiers_and_positions() {
1987 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
1988 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
1989 linked_chunk.push_gap_back(());
1990 linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
1991 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
1992
1993 assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
1994 assert_eq!(
1995 linked_chunk.item_position(|item| *item == 'e'),
1996 Some(Position(ChunkIdentifier(1), 1))
1997 );
1998 }
1999
2000 #[test]
2001 fn test_rchunks() {
2002 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2003 linked_chunk.push_items_back(['a', 'b']);
2004 linked_chunk.push_gap_back(());
2005 linked_chunk.push_items_back(['c', 'd', 'e']);
2006
2007 let mut iterator = linked_chunk.rchunks();
2008
2009 assert_matches!(
2010 iterator.next(),
2011 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2012 assert_eq!(items, &['e']);
2013 }
2014 );
2015 assert_matches!(
2016 iterator.next(),
2017 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2018 assert_eq!(items, &['c', 'd']);
2019 }
2020 );
2021 assert_matches!(
2022 iterator.next(),
2023 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2024 );
2025 assert_matches!(
2026 iterator.next(),
2027 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2028 assert_eq!(items, &['a', 'b']);
2029 }
2030 );
2031 assert_matches!(iterator.next(), None);
2032 }
2033
2034 #[test]
2035 fn test_chunks() {
2036 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2037 linked_chunk.push_items_back(['a', 'b']);
2038 linked_chunk.push_gap_back(());
2039 linked_chunk.push_items_back(['c', 'd', 'e']);
2040
2041 let mut iterator = linked_chunk.chunks();
2042
2043 assert_matches!(
2044 iterator.next(),
2045 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2046 assert_eq!(items, &['a', 'b']);
2047 }
2048 );
2049 assert_matches!(
2050 iterator.next(),
2051 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2052 );
2053 assert_matches!(
2054 iterator.next(),
2055 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2056 assert_eq!(items, &['c', 'd']);
2057 }
2058 );
2059 assert_matches!(
2060 iterator.next(),
2061 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2062 assert_eq!(items, &['e']);
2063 }
2064 );
2065 assert_matches!(iterator.next(), None);
2066 }
2067
2068 #[test]
2069 fn test_rchunks_from() -> Result<(), Error> {
2070 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2071 linked_chunk.push_items_back(['a', 'b']);
2072 linked_chunk.push_gap_back(());
2073 linked_chunk.push_items_back(['c', 'd', 'e']);
2074
2075 let mut iterator = linked_chunk.rchunks_from(
2076 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2077 )?;
2078
2079 assert_matches!(
2080 iterator.next(),
2081 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2082 assert_eq!(items, &['c', 'd']);
2083 }
2084 );
2085 assert_matches!(
2086 iterator.next(),
2087 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2088 );
2089 assert_matches!(
2090 iterator.next(),
2091 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2092 assert_eq!(items, &['a', 'b']);
2093 }
2094 );
2095 assert_matches!(iterator.next(), None);
2096
2097 Ok(())
2098 }
2099
2100 #[test]
2101 fn test_chunks_from() -> Result<(), Error> {
2102 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2103 linked_chunk.push_items_back(['a', 'b']);
2104 linked_chunk.push_gap_back(());
2105 linked_chunk.push_items_back(['c', 'd', 'e']);
2106
2107 let mut iterator = linked_chunk.chunks_from(
2108 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2109 )?;
2110
2111 assert_matches!(
2112 iterator.next(),
2113 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2114 assert_eq!(items, &['c', 'd']);
2115 }
2116 );
2117 assert_matches!(
2118 iterator.next(),
2119 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2120 assert_eq!(items, &['e']);
2121 }
2122 );
2123 assert_matches!(iterator.next(), None);
2124
2125 Ok(())
2126 }
2127
2128 #[test]
2129 fn test_ritems() {
2130 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2131 linked_chunk.push_items_back(['a', 'b']);
2132 linked_chunk.push_gap_back(());
2133 linked_chunk.push_items_back(['c', 'd', 'e']);
2134
2135 let mut iterator = linked_chunk.ritems();
2136
2137 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2138 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2139 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2140 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2141 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2142 assert_matches!(iterator.next(), None);
2143 }
2144
2145 #[test]
2146 fn test_ritems_with_final_gap() -> Result<(), Error> {
2147 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2148 linked_chunk.push_items_back(['a', 'b']);
2149 linked_chunk.push_gap_back(());
2150 linked_chunk.push_items_back(['c', 'd', 'e']);
2151 linked_chunk.push_gap_back(());
2152
2153 let mut iterator = linked_chunk.ritems();
2154
2155 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
2156 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2157 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2158 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2159 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2160 assert_matches!(iterator.next(), None);
2161
2162 Ok(())
2163 }
2164
2165 #[test]
2166 fn test_ritems_empty() {
2167 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2168 let mut iterator = linked_chunk.ritems();
2169
2170 assert_matches!(iterator.next(), None);
2171 }
2172
2173 #[test]
2174 fn test_items() {
2175 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2176 linked_chunk.push_items_back(['a', 'b']);
2177 linked_chunk.push_gap_back(());
2178 linked_chunk.push_items_back(['c', 'd', 'e']);
2179
2180 let mut iterator = linked_chunk.items();
2181
2182 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2183 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2184 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2185 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2186 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2187 assert_matches!(iterator.next(), None);
2188 }
2189
2190 #[test]
2191 fn test_items_empty() {
2192 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2193 let mut iterator = linked_chunk.items();
2194
2195 assert_matches!(iterator.next(), None);
2196 }
2197
2198 #[test]
2199 fn test_ritems_from() -> Result<(), Error> {
2200 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2201 linked_chunk.push_items_back(['a', 'b']);
2202 linked_chunk.push_gap_back(());
2203 linked_chunk.push_items_back(['c', 'd', 'e']);
2204
2205 let mut iterator =
2206 linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2207
2208 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2209 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2210 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2211 assert_matches!(iterator.next(), None);
2212
2213 Ok(())
2214 }
2215
2216 #[test]
2217 fn test_items_from() -> Result<(), Error> {
2218 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2219 linked_chunk.push_items_back(['a', 'b']);
2220 linked_chunk.push_gap_back(());
2221 linked_chunk.push_items_back(['c', 'd', 'e']);
2222
2223 let mut iterator =
2224 linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2225
2226 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2227 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2228 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2229 assert_matches!(iterator.next(), None);
2230
2231 Ok(())
2232 }
2233
2234 #[test]
2235 fn test_insert_items_at() -> Result<(), Error> {
2236 use super::Update::*;
2237
2238 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2239
2240 let _ = linked_chunk.updates().unwrap().take();
2242
2243 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2244 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2245 assert_eq!(
2246 linked_chunk.updates().unwrap().take(),
2247 &[
2248 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2249 NewItemsChunk {
2250 previous: Some(ChunkIdentifier(0)),
2251 new: ChunkIdentifier(1),
2252 next: None,
2253 },
2254 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2255 ]
2256 );
2257
2258 {
2260 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2261
2262 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2265
2266 assert_items_eq!(
2267 linked_chunk,
2268 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2269 );
2270 assert_eq!(linked_chunk.num_items(), 10);
2271 assert_eq!(
2272 linked_chunk.updates().unwrap().take(),
2273 &[
2274 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2275 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2276 NewItemsChunk {
2277 previous: Some(ChunkIdentifier(1)),
2278 new: ChunkIdentifier(2),
2279 next: None,
2280 },
2281 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2282 StartReattachItems,
2283 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2284 NewItemsChunk {
2285 previous: Some(ChunkIdentifier(2)),
2286 new: ChunkIdentifier(3),
2287 next: None,
2288 },
2289 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2290 EndReattachItems,
2291 ]
2292 );
2293 }
2294
2295 {
2297 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2298 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2299
2300 assert_items_eq!(
2301 linked_chunk,
2302 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2303 );
2304 assert_eq!(linked_chunk.num_items(), 14);
2305 assert_eq!(
2306 linked_chunk.updates().unwrap().take(),
2307 &[
2308 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2309 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2310 NewItemsChunk {
2311 previous: Some(ChunkIdentifier(0)),
2312 new: ChunkIdentifier(4),
2313 next: Some(ChunkIdentifier(1)),
2314 },
2315 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2316 StartReattachItems,
2317 PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2318 NewItemsChunk {
2319 previous: Some(ChunkIdentifier(4)),
2320 new: ChunkIdentifier(5),
2321 next: Some(ChunkIdentifier(1)),
2322 },
2323 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2324 EndReattachItems,
2325 ]
2326 );
2327 }
2328
2329 {
2331 let pos_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2332 linked_chunk.insert_items_at(pos_c, ['r', 's'])?;
2333
2334 assert_items_eq!(
2335 linked_chunk,
2336 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2337 );
2338 assert_eq!(linked_chunk.num_items(), 16);
2339 assert_eq!(
2340 linked_chunk.updates().unwrap().take(),
2341 &[
2342 DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2343 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2344 StartReattachItems,
2345 PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2346 EndReattachItems,
2347 ]
2348 );
2349 }
2350
2351 {
2353 let pos_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2354 let pos_f = Position(pos_f.chunk_identifier(), pos_f.index() + 1);
2355
2356 linked_chunk.insert_items_at(pos_f, ['p', 'q'])?;
2357 assert_items_eq!(
2358 linked_chunk,
2359 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2360 );
2361 assert_eq!(
2362 linked_chunk.updates().unwrap().take(),
2363 &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2364 );
2365 assert_eq!(linked_chunk.num_items(), 18);
2366 }
2367
2368 {
2370 assert_matches!(
2371 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2372 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2373 );
2374 assert!(linked_chunk.updates().unwrap().take().is_empty());
2375 }
2376
2377 {
2379 assert_matches!(
2380 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2381 Err(Error::InvalidItemIndex { index: 128 })
2382 );
2383 assert!(linked_chunk.updates().unwrap().take().is_empty());
2384 }
2385
2386 {
2388 linked_chunk.push_gap_back(());
2390 assert_items_eq!(
2391 linked_chunk,
2392 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2393 );
2394 assert_eq!(
2395 linked_chunk.updates().unwrap().take(),
2396 &[NewGapChunk {
2397 previous: Some(ChunkIdentifier(3)),
2398 new: ChunkIdentifier(6),
2399 next: None,
2400 gap: ()
2401 }]
2402 );
2403
2404 assert_matches!(
2405 linked_chunk.insert_items_at(Position(ChunkIdentifier(6), 0), ['u', 'v'],),
2406 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2407 );
2408 }
2409
2410 assert_eq!(linked_chunk.num_items(), 18);
2411
2412 Ok(())
2413 }
2414
2415 #[test]
2416 fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2417 use super::Update::*;
2418
2419 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2420
2421 let _ = linked_chunk.updates().unwrap().take();
2423
2424 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2425 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2426 assert_eq!(
2427 linked_chunk.updates().unwrap().take(),
2428 &[
2429 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2430 NewItemsChunk {
2431 previous: Some(ChunkIdentifier(0)),
2432 new: ChunkIdentifier(1),
2433 next: None,
2434 },
2435 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2436 ]
2437 );
2438
2439 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2441
2442 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2445
2446 assert_items_eq!(
2447 linked_chunk,
2448 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2449 );
2450 assert_eq!(linked_chunk.num_items(), 10);
2451 assert_eq!(
2452 linked_chunk.updates().unwrap().take(),
2453 &[
2454 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2455 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2456 NewItemsChunk {
2457 previous: Some(ChunkIdentifier(1)),
2458 new: ChunkIdentifier(2),
2459 next: None,
2460 },
2461 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2462 StartReattachItems,
2463 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2464 NewItemsChunk {
2465 previous: Some(ChunkIdentifier(2)),
2466 new: ChunkIdentifier(3),
2467 next: None,
2468 },
2469 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2470 EndReattachItems,
2471 ]
2472 );
2473
2474 Ok(())
2475 }
2476
2477 #[test]
2478 fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2479 use super::Update::*;
2480
2481 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2482
2483 let _ = linked_chunk.updates().unwrap().take();
2485
2486 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2487 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2488 assert_eq!(
2489 linked_chunk.updates().unwrap().take(),
2490 &[
2491 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2492 NewItemsChunk {
2493 previous: Some(ChunkIdentifier(0)),
2494 new: ChunkIdentifier(1),
2495 next: None,
2496 },
2497 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2498 ]
2499 );
2500
2501 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2503 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2504
2505 assert_items_eq!(
2506 linked_chunk,
2507 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'e', 'f']
2508 );
2509 assert_eq!(linked_chunk.num_items(), 10);
2510 assert_eq!(
2511 linked_chunk.updates().unwrap().take(),
2512 &[
2513 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2514 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2515 NewItemsChunk {
2516 previous: Some(ChunkIdentifier(0)),
2517 new: ChunkIdentifier(2),
2518 next: Some(ChunkIdentifier(1)),
2519 },
2520 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2521 StartReattachItems,
2522 PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2523 NewItemsChunk {
2524 previous: Some(ChunkIdentifier(2)),
2525 new: ChunkIdentifier(3),
2526 next: Some(ChunkIdentifier(1)),
2527 },
2528 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2529 EndReattachItems,
2530 ]
2531 );
2532
2533 Ok(())
2534 }
2535
2536 #[test]
2537 fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2538 use super::Update::*;
2539
2540 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2541
2542 let _ = linked_chunk.updates().unwrap().take();
2544
2545 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2546 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2547 assert_eq!(
2548 linked_chunk.updates().unwrap().take(),
2549 &[
2550 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2551 NewItemsChunk {
2552 previous: Some(ChunkIdentifier(0)),
2553 new: ChunkIdentifier(1),
2554 next: None,
2555 },
2556 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2557 NewItemsChunk {
2558 previous: Some(ChunkIdentifier(1)),
2559 new: ChunkIdentifier(2),
2560 next: None,
2561 },
2562 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2563 ]
2564 );
2565
2566 let pos_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2567 linked_chunk.insert_items_at(pos_d, ['r', 's'])?;
2568
2569 assert_items_eq!(
2570 linked_chunk,
2571 ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2572 );
2573 assert_eq!(linked_chunk.num_items(), 10);
2574 assert_eq!(
2575 linked_chunk.updates().unwrap().take(),
2576 &[
2577 DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2578 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2579 StartReattachItems,
2580 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2581 NewItemsChunk {
2582 previous: Some(ChunkIdentifier(1)),
2583 new: ChunkIdentifier(3),
2584 next: Some(ChunkIdentifier(2)),
2585 },
2586 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2587 EndReattachItems,
2588 ]
2589 );
2590
2591 Ok(())
2592 }
2593
2594 #[test]
2595 fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2596 use super::Update::*;
2597
2598 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2599
2600 let _ = linked_chunk.updates().unwrap().take();
2602
2603 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2604 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2605 assert_eq!(
2606 linked_chunk.updates().unwrap().take(),
2607 &[
2608 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2609 NewItemsChunk {
2610 previous: Some(ChunkIdentifier(0)),
2611 new: ChunkIdentifier(1),
2612 next: None,
2613 },
2614 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2615 ]
2616 );
2617
2618 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2620 let pos_after_e = Position(pos_e.chunk_identifier(), pos_e.index() + 1);
2621
2622 linked_chunk.insert_items_at(pos_after_e, ['p', 'q'])?;
2623 assert_items_eq!(
2624 linked_chunk,
2625 ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2626 );
2627 assert_eq!(
2628 linked_chunk.updates().unwrap().take(),
2629 &[
2630 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2631 NewItemsChunk {
2632 previous: Some(ChunkIdentifier(1)),
2633 new: ChunkIdentifier(2),
2634 next: None
2635 },
2636 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2637 ]
2638 );
2639 assert_eq!(linked_chunk.num_items(), 7);
2640
2641 Ok(())
2642 }
2643
2644 #[test]
2645 fn test_insert_items_at_errs() -> Result<(), Error> {
2646 use super::Update::*;
2647
2648 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2649
2650 let _ = linked_chunk.updates().unwrap().take();
2652
2653 linked_chunk.push_items_back(['a', 'b', 'c']);
2654 linked_chunk.push_gap_back(());
2655 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2656 assert_eq!(
2657 linked_chunk.updates().unwrap().take(),
2658 &[
2659 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2660 NewGapChunk {
2661 previous: Some(ChunkIdentifier(0)),
2662 new: ChunkIdentifier(1),
2663 next: None,
2664 gap: (),
2665 },
2666 ]
2667 );
2668
2669 {
2671 assert_matches!(
2672 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2673 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2674 );
2675 assert!(linked_chunk.updates().unwrap().take().is_empty());
2676 }
2677
2678 {
2680 assert_matches!(
2681 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2682 Err(Error::InvalidItemIndex { index: 128 })
2683 );
2684 assert!(linked_chunk.updates().unwrap().take().is_empty());
2685 }
2686
2687 {
2689 assert_matches!(
2690 linked_chunk.insert_items_at(Position(ChunkIdentifier(1), 0), ['u', 'v'],),
2691 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2692 );
2693 }
2694
2695 Ok(())
2696 }
2697
2698 #[test]
2699 fn test_remove_item_at() -> Result<(), Error> {
2700 use super::Update::*;
2701
2702 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2703
2704 let _ = linked_chunk.updates().unwrap().take();
2706
2707 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2708 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2709 assert_eq!(linked_chunk.num_items(), 11);
2710
2711 let _ = linked_chunk.updates().unwrap().take();
2713
2714 {
2717 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2718 let removed_item = linked_chunk.remove_item_at(position_of_f)?;
2719
2720 assert_eq!(removed_item, 'f');
2721 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2722 assert_eq!(linked_chunk.num_items(), 10);
2723
2724 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2725 let removed_item = linked_chunk.remove_item_at(position_of_e)?;
2726
2727 assert_eq!(removed_item, 'e');
2728 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2729 assert_eq!(linked_chunk.num_items(), 9);
2730
2731 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2732 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2733
2734 assert_eq!(removed_item, 'd');
2735 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2736 assert_eq!(linked_chunk.num_items(), 8);
2737
2738 assert_eq!(
2739 linked_chunk.updates().unwrap().take(),
2740 &[
2741 RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2742 RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2743 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2744 RemoveChunk(ChunkIdentifier(1)),
2745 ]
2746 );
2747 }
2748
2749 {
2752 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2753 let removed_item = linked_chunk.remove_item_at(first_position)?;
2754
2755 assert_eq!(removed_item, 'a');
2756 assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2757 assert_eq!(linked_chunk.num_items(), 7);
2758
2759 let removed_item = linked_chunk.remove_item_at(first_position)?;
2760
2761 assert_eq!(removed_item, 'b');
2762 assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2763 assert_eq!(linked_chunk.num_items(), 6);
2764
2765 let removed_item = linked_chunk.remove_item_at(first_position)?;
2766
2767 assert_eq!(removed_item, 'c');
2768 assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2769 assert_eq!(linked_chunk.num_items(), 5);
2770
2771 assert_eq!(
2772 linked_chunk.updates().unwrap().take(),
2773 &[
2774 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2775 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2776 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2777 ]
2778 );
2779 }
2780
2781 {
2784 let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2785 let removed_item = linked_chunk.remove_item_at(first_position)?;
2786
2787 assert_eq!(removed_item, 'g');
2788 assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2789 assert_eq!(linked_chunk.num_items(), 4);
2790
2791 let removed_item = linked_chunk.remove_item_at(first_position)?;
2792
2793 assert_eq!(removed_item, 'h');
2794 assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2795 assert_eq!(linked_chunk.num_items(), 3);
2796
2797 let removed_item = linked_chunk.remove_item_at(first_position)?;
2798
2799 assert_eq!(removed_item, 'i');
2800 assert_items_eq!(linked_chunk, [] ['j', 'k']);
2801 assert_eq!(linked_chunk.num_items(), 2);
2802
2803 assert_eq!(
2804 linked_chunk.updates().unwrap().take(),
2805 &[
2806 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2807 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2808 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2809 RemoveChunk(ChunkIdentifier(2)),
2810 ]
2811 );
2812 }
2813
2814 {
2817 let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2818 let removed_item = linked_chunk.remove_item_at(position_of_k)?;
2819
2820 assert_eq!(removed_item, 'k');
2821 #[rustfmt::skip]
2822 assert_items_eq!(linked_chunk, [] ['j']);
2823 assert_eq!(linked_chunk.num_items(), 1);
2824
2825 let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2826 let removed_item = linked_chunk.remove_item_at(position_of_j)?;
2827
2828 assert_eq!(removed_item, 'j');
2829 assert_items_eq!(linked_chunk, []);
2830 assert_eq!(linked_chunk.num_items(), 0);
2831
2832 assert_eq!(
2833 linked_chunk.updates().unwrap().take(),
2834 &[
2835 RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2836 RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2837 RemoveChunk(ChunkIdentifier(3)),
2838 ]
2839 );
2840 }
2841
2842 {
2844 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2845
2846 #[rustfmt::skip]
2847 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2848 assert_eq!(linked_chunk.num_items(), 4);
2849
2850 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2851 linked_chunk.insert_gap_at((), position_of_c)?;
2852
2853 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2854 assert_eq!(linked_chunk.num_items(), 4);
2855
2856 let _ = linked_chunk.updates().unwrap().take();
2858
2859 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2860 let removed_item = linked_chunk.remove_item_at(position_of_c)?;
2861
2862 assert_eq!(removed_item, 'c');
2863 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2864 assert_eq!(linked_chunk.num_items(), 3);
2865
2866 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2867 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2868
2869 assert_eq!(removed_item, 'd');
2870 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2871 assert_eq!(linked_chunk.num_items(), 2);
2872
2873 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2874 let removed_item = linked_chunk.remove_item_at(first_position)?;
2875
2876 assert_eq!(removed_item, 'a');
2877 assert_items_eq!(linked_chunk, ['b'] [-]);
2878 assert_eq!(linked_chunk.num_items(), 1);
2879
2880 let removed_item = linked_chunk.remove_item_at(first_position)?;
2881
2882 assert_eq!(removed_item, 'b');
2883 assert_items_eq!(linked_chunk, [] [-]);
2884 assert_eq!(linked_chunk.num_items(), 0);
2885
2886 assert_eq!(
2887 linked_chunk.updates().unwrap().take(),
2888 &[
2889 RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2890 RemoveChunk(ChunkIdentifier(6)),
2891 RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2892 RemoveChunk(ChunkIdentifier(4)),
2893 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2894 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2895 ]
2896 );
2897 }
2898
2899 Ok(())
2900 }
2901
2902 #[test]
2903 fn test_insert_gap_at() -> Result<(), Error> {
2904 use super::Update::*;
2905
2906 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2907
2908 let _ = linked_chunk.updates().unwrap().take();
2910
2911 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2912 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2913 assert_eq!(
2914 linked_chunk.updates().unwrap().take(),
2915 &[
2916 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2917 NewItemsChunk {
2918 previous: Some(ChunkIdentifier(0)),
2919 new: ChunkIdentifier(1),
2920 next: None
2921 },
2922 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2923 ]
2924 );
2925
2926 {
2928 let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
2929 linked_chunk.insert_gap_at((), position_of_b)?;
2930
2931 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2932 assert_eq!(
2933 linked_chunk.updates().unwrap().take(),
2934 &[
2935 DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
2936 NewGapChunk {
2937 previous: Some(ChunkIdentifier(0)),
2938 new: ChunkIdentifier(2),
2939 next: Some(ChunkIdentifier(1)),
2940 gap: (),
2941 },
2942 StartReattachItems,
2943 NewItemsChunk {
2944 previous: Some(ChunkIdentifier(2)),
2945 new: ChunkIdentifier(3),
2946 next: Some(ChunkIdentifier(1)),
2947 },
2948 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
2949 EndReattachItems,
2950 ]
2951 );
2952 }
2953
2954 {
2957 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2958 linked_chunk.insert_gap_at((), position_of_a)?;
2959
2960 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2963 assert_eq!(
2964 linked_chunk.updates().unwrap().take(),
2965 &[NewGapChunk {
2966 previous: None,
2967 new: ChunkIdentifier(4),
2968 next: Some(ChunkIdentifier(0)),
2969 gap: (),
2970 },]
2971 );
2972 }
2973
2974 {
2977 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2978 linked_chunk.insert_gap_at((), position_of_d)?;
2979
2980 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
2984 assert_eq!(
2985 linked_chunk.updates().unwrap().take(),
2986 &[NewGapChunk {
2987 previous: Some(ChunkIdentifier(3)),
2988 new: ChunkIdentifier(5),
2989 next: Some(ChunkIdentifier(1)),
2990 gap: (),
2991 }]
2992 );
2993 }
2994
2995 {
2997 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
2999 let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
3000
3001 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
3002
3003 assert_eq!(
3004 linked_chunk.updates().unwrap().take(),
3005 &[
3006 NewItemsChunk {
3007 previous: Some(ChunkIdentifier(5)),
3008 new: ChunkIdentifier(6),
3009 next: Some(ChunkIdentifier(1)),
3010 },
3011 RemoveChunk(ChunkIdentifier(5)),
3012 ]
3013 );
3014
3015 linked_chunk.insert_gap_at((), position)?;
3016
3017 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
3018 assert_eq!(
3019 linked_chunk.updates().unwrap().take(),
3020 &[NewGapChunk {
3021 previous: Some(ChunkIdentifier(3)),
3022 new: ChunkIdentifier(7),
3023 next: Some(ChunkIdentifier(6)),
3024 gap: (),
3025 }]
3026 );
3027 }
3028
3029 {
3031 assert_matches!(
3032 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
3033 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
3034 );
3035 assert!(linked_chunk.updates().unwrap().take().is_empty());
3036 }
3037
3038 {
3040 assert_matches!(
3041 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
3042 Err(Error::InvalidItemIndex { index: 128 })
3043 );
3044 assert!(linked_chunk.updates().unwrap().take().is_empty());
3045 }
3046
3047 {
3049 let position_of_a_gap = Position(ChunkIdentifier(2), 0);
3052 assert_matches!(
3053 linked_chunk.insert_gap_at((), position_of_a_gap),
3054 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
3055 );
3056 assert!(linked_chunk.updates().unwrap().take().is_empty());
3057 }
3058
3059 assert_eq!(linked_chunk.num_items(), 6);
3060
3061 Ok(())
3062 }
3063
3064 #[test]
3065 fn test_replace_gap_at_middle() -> Result<(), Error> {
3066 use super::Update::*;
3067
3068 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3069
3070 let _ = linked_chunk.updates().unwrap().take();
3072
3073 linked_chunk.push_items_back(['a', 'b']);
3074 linked_chunk.push_gap_back(());
3075 linked_chunk.push_items_back(['l', 'm']);
3076 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
3077 assert_eq!(
3078 linked_chunk.updates().unwrap().take(),
3079 &[
3080 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3081 NewGapChunk {
3082 previous: Some(ChunkIdentifier(0)),
3083 new: ChunkIdentifier(1),
3084 next: None,
3085 gap: (),
3086 },
3087 NewItemsChunk {
3088 previous: Some(ChunkIdentifier(1)),
3089 new: ChunkIdentifier(2),
3090 next: None,
3091 },
3092 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
3093 ]
3094 );
3095
3096 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3098 assert_eq!(gap_identifier, ChunkIdentifier(1));
3099
3100 let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
3101 assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
3102 assert_items_eq!(
3103 linked_chunk,
3104 ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
3105 );
3106 assert_eq!(
3107 linked_chunk.updates().unwrap().take(),
3108 &[
3109 NewItemsChunk {
3110 previous: Some(ChunkIdentifier(1)),
3111 new: ChunkIdentifier(3),
3112 next: Some(ChunkIdentifier(2)),
3113 },
3114 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
3115 NewItemsChunk {
3116 previous: Some(ChunkIdentifier(3)),
3117 new: ChunkIdentifier(4),
3118 next: Some(ChunkIdentifier(2)),
3119 },
3120 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
3121 RemoveChunk(ChunkIdentifier(1)),
3122 ]
3123 );
3124
3125 assert_eq!(linked_chunk.num_items(), 9);
3126
3127 Ok(())
3128 }
3129
3130 #[test]
3131 fn test_replace_gap_at_end() -> Result<(), Error> {
3132 use super::Update::*;
3133
3134 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3135
3136 let _ = linked_chunk.updates().unwrap().take();
3138
3139 linked_chunk.push_items_back(['a', 'b']);
3140 linked_chunk.push_gap_back(());
3141 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3142 assert_eq!(
3143 linked_chunk.updates().unwrap().take(),
3144 &[
3145 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3146 NewGapChunk {
3147 previous: Some(ChunkIdentifier(0)),
3148 new: ChunkIdentifier(1),
3149 next: None,
3150 gap: (),
3151 },
3152 ]
3153 );
3154
3155 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3157 assert_eq!(gap_identifier, ChunkIdentifier(1));
3158
3159 let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3160 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3161 assert_items_eq!(
3162 linked_chunk,
3163 ['a', 'b'] ['w', 'x', 'y'] ['z']
3164 );
3165 assert_eq!(
3166 linked_chunk.updates().unwrap().take(),
3167 &[
3168 NewItemsChunk {
3169 previous: Some(ChunkIdentifier(1)),
3170 new: ChunkIdentifier(2),
3171 next: None,
3172 },
3173 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3174 NewItemsChunk {
3175 previous: Some(ChunkIdentifier(2)),
3176 new: ChunkIdentifier(3),
3177 next: None,
3178 },
3179 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3180 RemoveChunk(ChunkIdentifier(1)),
3181 ]
3182 );
3183
3184 assert_eq!(linked_chunk.num_items(), 6);
3185
3186 Ok(())
3187 }
3188
3189 #[test]
3190 fn test_replace_gap_at_beginning() -> Result<(), Error> {
3191 use super::Update::*;
3192
3193 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3194
3195 let _ = linked_chunk.updates().unwrap().take();
3197
3198 linked_chunk.push_items_back(['a', 'b']);
3199 assert_items_eq!(linked_chunk, ['a', 'b']);
3200 assert_eq!(
3201 linked_chunk.updates().unwrap().take(),
3202 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },]
3203 );
3204
3205 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3207 linked_chunk.insert_gap_at((), position_of_a).unwrap();
3208 assert_items_eq!(
3209 linked_chunk,
3210 [-] ['a', 'b']
3211 );
3212 assert_eq!(
3213 linked_chunk.updates().unwrap().take(),
3214 &[NewGapChunk {
3215 previous: None,
3216 new: ChunkIdentifier(1),
3217 next: Some(ChunkIdentifier(0)),
3218 gap: (),
3219 }]
3220 );
3221
3222 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3223 assert_eq!(gap_identifier, ChunkIdentifier(1));
3224
3225 let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3226 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3227 assert_items_eq!(
3228 linked_chunk,
3229 ['x'] ['a', 'b']
3230 );
3231 assert_eq!(
3232 linked_chunk.updates().unwrap().take(),
3233 &[
3234 NewItemsChunk {
3235 previous: Some(ChunkIdentifier(1)),
3236 new: ChunkIdentifier(2),
3237 next: Some(ChunkIdentifier(0)),
3238 },
3239 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3240 RemoveChunk(ChunkIdentifier(1)),
3241 ]
3242 );
3243
3244 assert_eq!(linked_chunk.num_items(), 3);
3245
3246 Ok(())
3247 }
3248
3249 #[test]
3250 fn test_remove_empty_chunk_at() -> Result<(), Error> {
3251 use super::Update::*;
3252
3253 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3254
3255 let _ = linked_chunk.updates().unwrap().take();
3257
3258 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3259 linked_chunk.push_items_back(['a', 'b']);
3260 linked_chunk.push_gap_back(());
3261 linked_chunk.push_items_back(['l', 'm']);
3262 linked_chunk.push_gap_back(());
3263 assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3264 assert_eq!(
3265 linked_chunk.updates().unwrap().take(),
3266 &[
3267 NewGapChunk {
3268 previous: None,
3269 new: ChunkIdentifier(1),
3270 next: Some(ChunkIdentifier(0)),
3271 gap: (),
3272 },
3273 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3274 NewGapChunk {
3275 previous: Some(ChunkIdentifier(0)),
3276 new: ChunkIdentifier(2),
3277 next: None,
3278 gap: (),
3279 },
3280 NewItemsChunk {
3281 previous: Some(ChunkIdentifier(2)),
3282 new: ChunkIdentifier(3),
3283 next: None,
3284 },
3285 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3286 NewGapChunk {
3287 previous: Some(ChunkIdentifier(3)),
3288 new: ChunkIdentifier(4),
3289 next: None,
3290 gap: (),
3291 },
3292 ]
3293 );
3294
3295 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3297 assert_matches!(err, Error::RemovingNonEmptyItemsChunk { .. });
3298
3299 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(42)).unwrap_err();
3301 assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3302
3303 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(2)).unwrap();
3305 let next = maybe_next.unwrap();
3306 assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3308 assert_eq!(next.index(), 0);
3309 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3310 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3311
3312 let next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(4)).unwrap();
3314 assert!(next.is_none());
3316 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3317 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3318
3319 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(1)).unwrap();
3321 let next = maybe_next.unwrap();
3322 assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3323 assert_eq!(next.index(), 0);
3324 assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3325 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3326
3327 Ok(())
3328 }
3329
3330 #[test]
3331 fn test_remove_empty_last_chunk() {
3332 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3333
3334 let _ = linked_chunk.updates().unwrap().take();
3336
3337 assert_items_eq!(linked_chunk, []);
3338 assert!(linked_chunk.updates().unwrap().take().is_empty());
3339
3340 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3342 assert_matches!(err, Error::RemovingLastChunk);
3343 }
3344
3345 #[test]
3346 fn test_chunk_item_positions() {
3347 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3348 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3349 linked_chunk.push_gap_back(());
3350 linked_chunk.push_items_back(['f']);
3351
3352 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3353
3354 let mut iterator = linked_chunk.chunks();
3355
3356 {
3358 let chunk = iterator.next().unwrap();
3359 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3360 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3361 }
3362
3363 {
3365 let chunk = iterator.next().unwrap();
3366 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3367 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3368 }
3369
3370 {
3372 let chunk = iterator.next().unwrap();
3373 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3374 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3375 }
3376
3377 {
3379 let chunk = iterator.next().unwrap();
3380 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3381 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3382 }
3383 }
3384
3385 #[test]
3386 fn test_is_first_and_last_chunk() {
3387 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3388
3389 let mut chunks = linked_chunk.chunks().peekable();
3390 assert!(chunks.peek().unwrap().is_first_chunk());
3391 assert!(chunks.next().unwrap().is_last_chunk());
3392 assert!(chunks.next().is_none());
3393
3394 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3395
3396 let mut chunks = linked_chunk.chunks().peekable();
3397 assert!(chunks.next().unwrap().is_first_chunk());
3398 assert!(chunks.peek().unwrap().is_first_chunk().not());
3399 assert!(chunks.next().unwrap().is_last_chunk().not());
3400 assert!(chunks.next().unwrap().is_last_chunk());
3401 assert!(chunks.next().is_none());
3402 }
3403
3404 #[test]
3409 fn test_clear() {
3410 let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3411
3412 let item = Arc::new('a');
3413 let gap = Arc::new(());
3414
3415 linked_chunk.push_items_back([
3416 item.clone(),
3417 item.clone(),
3418 item.clone(),
3419 item.clone(),
3420 item.clone(),
3421 ]);
3422 linked_chunk.push_gap_back(gap.clone());
3423 linked_chunk.push_items_back([item.clone()]);
3424
3425 assert_eq!(Arc::strong_count(&item), 7);
3426 assert_eq!(Arc::strong_count(&gap), 2);
3427 assert_eq!(linked_chunk.num_items(), 6);
3428 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3429
3430 linked_chunk.clear();
3432
3433 assert_eq!(Arc::strong_count(&item), 1);
3434 assert_eq!(Arc::strong_count(&gap), 1);
3435 assert_eq!(linked_chunk.num_items(), 0);
3436 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3437 }
3438
3439 #[test]
3440 fn test_clear_emit_an_update_clear() {
3441 use super::Update::*;
3442
3443 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3444
3445 assert_eq!(
3446 linked_chunk.updates().unwrap().take(),
3447 &[NewItemsChunk {
3448 previous: None,
3449 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3450 next: None
3451 }]
3452 );
3453
3454 linked_chunk.clear();
3455
3456 assert_eq!(
3457 linked_chunk.updates().unwrap().take(),
3458 &[
3459 Clear,
3460 NewItemsChunk {
3461 previous: None,
3462 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3463 next: None
3464 }
3465 ]
3466 );
3467 }
3468
3469 #[test]
3470 fn test_replace_item() {
3471 use super::Update::*;
3472
3473 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3474
3475 linked_chunk.push_items_back(['a', 'b', 'c']);
3476 linked_chunk.push_gap_back(());
3477 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3479
3480 let _ = linked_chunk.updates().unwrap().take();
3482
3483 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3485 assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3486
3487 assert_eq!(
3488 linked_chunk.updates().unwrap().take(),
3489 &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3490 );
3491
3492 assert_matches!(
3494 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3495 Err(Error::InvalidItemIndex { index: 3 })
3496 );
3497
3498 assert_matches!(
3500 linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3501 Err(Error::ChunkIsAGap { .. })
3502 );
3503 }
3504
3505 #[test]
3506 fn test_lazy_previous() {
3507 use std::marker::PhantomData;
3508
3509 use super::{Ends, ObservableUpdates, Update::*};
3510
3511 let first_chunk_identifier = ChunkIdentifier(0);
3513 let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3514 unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3515
3516 let mut linked_chunk = LinkedChunk::<3, char, ()> {
3517 links: Ends { first: first_loaded_chunk, last: None },
3518 chunk_identifier_generator:
3519 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3520 updates: Some(ObservableUpdates::new()),
3521 marker: PhantomData,
3522 };
3523
3524 {
3527 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3528
3529 assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3530
3531 {
3533 let mut chunks = linked_chunk.chunks();
3534
3535 assert_matches!(chunks.next(), Some(chunk) => {
3536 assert_eq!(chunk.identifier(), 1);
3537 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3538 });
3539 assert_matches!(chunks.next(), Some(chunk) => {
3540 assert_eq!(chunk.identifier(), 2);
3541 assert!(chunk.lazy_previous.is_none());
3542 });
3543 assert!(chunks.next().is_none());
3544 }
3545
3546 assert_eq!(
3548 linked_chunk.updates().unwrap().take(),
3549 &[
3550 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3551 NewItemsChunk {
3552 previous: Some(ChunkIdentifier(1)),
3553 new: ChunkIdentifier(2),
3554 next: None,
3555 },
3556 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3557 ]
3558 );
3559 }
3560
3561 {
3563 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3564
3565 assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3566
3567 {
3569 let mut chunks = linked_chunk.chunks();
3570
3571 assert_matches!(chunks.next(), Some(chunk) => {
3572 assert_eq!(chunk.identifier(), 3);
3573 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3575 });
3576 assert_matches!(chunks.next(), Some(chunk) => {
3577 assert_eq!(chunk.identifier(), 1);
3578 assert!(chunk.lazy_previous.is_none());
3580 });
3581 assert_matches!(chunks.next(), Some(chunk) => {
3582 assert_eq!(chunk.identifier(), 2);
3583 assert!(chunk.lazy_previous.is_none());
3584 });
3585 assert!(chunks.next().is_none());
3586 }
3587
3588 assert_eq!(
3590 linked_chunk.updates().unwrap().take(),
3591 &[NewGapChunk {
3592 previous: Some(ChunkIdentifier(0)),
3594 new: ChunkIdentifier(3),
3595 next: Some(ChunkIdentifier(1)),
3596 gap: ()
3597 }]
3598 );
3599 }
3600
3601 {
3603 linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3604
3605 assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3606
3607 {
3609 let mut chunks = linked_chunk.chunks();
3610
3611 assert_matches!(chunks.next(), Some(chunk) => {
3612 assert_eq!(chunk.identifier(), 4);
3613 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3615 });
3616 assert_matches!(chunks.next(), Some(chunk) => {
3617 assert_eq!(chunk.identifier(), 5);
3618 assert!(chunk.lazy_previous.is_none());
3619 });
3620 assert_matches!(chunks.next(), Some(chunk) => {
3621 assert_eq!(chunk.identifier(), 1);
3622 assert!(chunk.lazy_previous.is_none());
3623 });
3624 assert_matches!(chunks.next(), Some(chunk) => {
3625 assert_eq!(chunk.identifier(), 2);
3626 assert!(chunk.lazy_previous.is_none());
3627 });
3628 assert!(chunks.next().is_none());
3629 }
3630
3631 assert_eq!(
3633 linked_chunk.updates().unwrap().take(),
3634 &[
3635 NewItemsChunk {
3637 previous: Some(ChunkIdentifier(3)),
3638 new: ChunkIdentifier(4),
3639 next: Some(ChunkIdentifier(1)),
3640 },
3641 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3643 NewItemsChunk {
3645 previous: Some(ChunkIdentifier(4)),
3646 new: ChunkIdentifier(5),
3647 next: Some(ChunkIdentifier(1)),
3648 },
3649 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3651 RemoveChunk(ChunkIdentifier(3)),
3653 ]
3654 );
3655 }
3656
3657 {
3661 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3662
3663 assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3664
3665 {
3667 let mut chunks = linked_chunk.chunks();
3668
3669 assert_matches!(chunks.next(), Some(chunk) => {
3670 assert_eq!(chunk.identifier(), 6);
3671 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3673 });
3674 assert_matches!(chunks.next(), Some(chunk) => {
3675 assert_eq!(chunk.identifier(), 4);
3676 assert!(chunk.lazy_previous.is_none());
3678 });
3679 assert_matches!(chunks.next(), Some(chunk) => {
3680 assert_eq!(chunk.identifier(), 5);
3681 assert!(chunk.lazy_previous.is_none());
3682 });
3683 assert_matches!(chunks.next(), Some(chunk) => {
3684 assert_eq!(chunk.identifier(), 1);
3685 assert!(chunk.lazy_previous.is_none());
3686 });
3687 assert_matches!(chunks.next(), Some(chunk) => {
3688 assert_eq!(chunk.identifier(), 2);
3689 assert!(chunk.lazy_previous.is_none());
3690 });
3691 assert!(chunks.next().is_none());
3692 }
3693
3694 assert_eq!(
3696 linked_chunk.updates().unwrap().take(),
3697 &[NewGapChunk {
3698 previous: Some(ChunkIdentifier(0)),
3700 new: ChunkIdentifier(6),
3701 next: Some(ChunkIdentifier(4)),
3702 gap: ()
3703 }]
3704 );
3705 }
3706 }
3707}