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::{EventId, OwnedEventId, OwnedRoomId, RoomId};
110pub use updates::*;
111
112#[derive(Debug, Clone, Copy, PartialEq)]
114pub enum LinkedChunkId<'a> {
115 Room(&'a RoomId),
116 Thread(&'a RoomId, &'a EventId),
117}
118
119impl LinkedChunkId<'_> {
120 pub fn storage_key(&self) -> impl '_ + AsRef<[u8]> {
121 match self {
122 LinkedChunkId::Room(room_id) => room_id.to_string(),
123 LinkedChunkId::Thread(room_id, event_id) => format!("t:{room_id}:{event_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 LinkedChunkId::Thread(room_id, event_id) => {
131 OwnedLinkedChunkId::Thread((*room_id).to_owned(), (*event_id).to_owned())
132 }
133 }
134 }
135}
136
137impl PartialEq<&OwnedLinkedChunkId> for LinkedChunkId<'_> {
138 fn eq(&self, other: &&OwnedLinkedChunkId) -> bool {
139 match (self, other) {
140 (LinkedChunkId::Room(a), OwnedLinkedChunkId::Room(b)) => *a == b,
141 (LinkedChunkId::Thread(r, ev), OwnedLinkedChunkId::Thread(r2, ev2)) => {
142 r == r2 && ev == ev2
143 }
144 (LinkedChunkId::Room(..), OwnedLinkedChunkId::Thread(..))
145 | (LinkedChunkId::Thread(..), OwnedLinkedChunkId::Room(..)) => false,
146 }
147 }
148}
149
150impl PartialEq<LinkedChunkId<'_>> for OwnedLinkedChunkId {
151 fn eq(&self, other: &LinkedChunkId<'_>) -> bool {
152 other.eq(&self)
153 }
154}
155
156#[derive(Clone, Debug, PartialEq, Eq, Hash)]
158pub enum OwnedLinkedChunkId {
159 Room(OwnedRoomId),
160 Thread(OwnedRoomId, OwnedEventId),
161}
162
163impl Display for OwnedLinkedChunkId {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 match self {
166 OwnedLinkedChunkId::Room(room_id) => write!(f, "{room_id}"),
167 OwnedLinkedChunkId::Thread(room_id, thread_root) => {
168 write!(f, "{room_id}:thread:{thread_root}")
169 }
170 }
171 }
172}
173
174impl OwnedLinkedChunkId {
175 #[cfg(test)]
176 fn as_ref(&self) -> LinkedChunkId<'_> {
177 match self {
178 OwnedLinkedChunkId::Room(room_id) => LinkedChunkId::Room(room_id.as_ref()),
179 OwnedLinkedChunkId::Thread(room_id, event_id) => {
180 LinkedChunkId::Thread(room_id.as_ref(), event_id.as_ref())
181 }
182 }
183 }
184
185 pub fn room_id(&self) -> &RoomId {
186 match self {
187 OwnedLinkedChunkId::Room(room_id) => room_id,
188 OwnedLinkedChunkId::Thread(room_id, ..) => room_id,
189 }
190 }
191}
192
193#[derive(thiserror::Error, Debug)]
195pub enum Error {
196 #[error("The chunk identifier is invalid: `{identifier:?}`")]
198 InvalidChunkIdentifier {
199 identifier: ChunkIdentifier,
201 },
202
203 #[error("The chunk is a gap: `{identifier:?}`")]
205 ChunkIsAGap {
206 identifier: ChunkIdentifier,
208 },
209
210 #[error("The chunk is an item: `{identifier:?}`")]
212 ChunkIsItems {
213 identifier: ChunkIdentifier,
215 },
216
217 #[error("The chunk is a non-empty item chunk: `{identifier:?}`")]
219 RemovingNonEmptyItemsChunk {
220 identifier: ChunkIdentifier,
222 },
223
224 #[error("Trying to remove the only chunk, but a linked chunk can't be empty")]
227 RemovingLastChunk,
228
229 #[error("The item index is invalid: `{index}`")]
231 InvalidItemIndex {
232 index: usize,
234 },
235}
236
237struct Ends<const CHUNK_CAPACITY: usize, Item, Gap> {
242 first: NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>,
244 last: Option<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
246}
247
248impl<const CAP: usize, Item, Gap> Ends<CAP, Item, Gap> {
249 fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
251 unsafe { self.first.as_ref() }
252 }
253
254 fn first_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
256 unsafe { self.first.as_mut() }
257 }
258
259 fn latest_chunk(&self) -> &Chunk<CAP, Item, Gap> {
261 unsafe { self.last.unwrap_or(self.first).as_ref() }
262 }
263
264 fn latest_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
266 unsafe { self.last.as_mut().unwrap_or(&mut self.first).as_mut() }
267 }
268
269 fn chunk(&self, identifier: ChunkIdentifier) -> Option<&Chunk<CAP, Item, Gap>> {
271 let mut chunk = self.latest_chunk();
272
273 loop {
274 if chunk.identifier() == identifier {
275 return Some(chunk);
276 }
277
278 chunk = chunk.previous()?;
279 }
280 }
281
282 fn chunk_mut(&mut self, identifier: ChunkIdentifier) -> Option<&mut Chunk<CAP, Item, Gap>> {
284 let mut chunk = self.latest_chunk_mut();
285
286 loop {
287 if chunk.identifier() == identifier {
288 return Some(chunk);
289 }
290
291 chunk = chunk.previous_mut()?;
292 }
293 }
294
295 unsafe fn clear(&mut self) {
302 let mut current_chunk_ptr = self.last.or(Some(self.first));
305
306 while let Some(chunk_ptr) = current_chunk_ptr {
308 let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
310
311 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
313
314 current_chunk_ptr = previous_ptr;
316 }
317
318 self.first = NonNull::dangling();
320 self.last = None;
321 }
322
323 fn replace_with(&mut self, first_chunk: NonNull<Chunk<CAP, Item, Gap>>) {
326 unsafe {
328 self.clear();
329 }
330
331 self.first = first_chunk;
333 }
334
335 fn reset(&mut self) {
340 self.replace_with(Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER));
341 }
342}
343
344pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
351 links: Ends<CHUNK_CAPACITY, Item, Gap>,
353
354 chunk_identifier_generator: ChunkIdentifierGenerator,
356
357 updates: Option<ObservableUpdates<Item, Gap>>,
361
362 marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
364}
365
366impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
367 fn default() -> Self {
368 Self::new()
369 }
370}
371
372impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
373 pub fn new() -> Self {
375 Self {
376 links: Ends {
377 first: Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER),
378 last: None,
379 },
380 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
381 updates: None,
382 marker: PhantomData,
383 }
384 }
385
386 pub fn new_with_update_history() -> Self {
392 let first_chunk_identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
393
394 let mut updates = ObservableUpdates::new();
395 updates.push(Update::NewItemsChunk {
396 previous: None,
397 new: first_chunk_identifier,
398 next: None,
399 });
400
401 Self {
402 links: Ends { first: Chunk::new_items_leaked(first_chunk_identifier), last: None },
403 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
404 updates: Some(updates),
405 marker: PhantomData,
406 }
407 }
408
409 pub fn clear(&mut self) {
411 self.links.reset();
413
414 self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
416
417 if let Some(updates) = self.updates.as_mut() {
419 updates.clear_pending();
422 updates.push(Update::Clear);
423 updates.push(Update::NewItemsChunk {
424 previous: None,
425 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
426 next: None,
427 })
428 }
429 }
430
431 pub fn push_items_back<I>(&mut self, items: I)
437 where
438 Item: Clone,
439 Gap: Clone,
440 I: IntoIterator<Item = Item>,
441 I::IntoIter: ExactSizeIterator,
442 {
443 let items = items.into_iter();
444
445 let last_chunk = self.links.latest_chunk_mut();
446
447 let last_chunk =
449 last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
450
451 debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
452
453 if !last_chunk.is_first_chunk() {
457 self.links.last = Some(last_chunk.as_ptr());
460 }
461 }
462
463 pub fn push_gap_back(&mut self, content: Gap)
466 where
467 Item: Clone,
468 Gap: Clone,
469 {
470 let last_chunk = self.links.latest_chunk_mut();
471 last_chunk.insert_next(
472 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
473 &mut self.updates,
474 );
475
476 self.links.last = last_chunk.next;
477 }
478
479 pub fn insert_items_at<I>(&mut self, position: Position, items: I) -> Result<(), Error>
484 where
485 Item: Clone,
486 Gap: Clone,
487 I: IntoIterator<Item = Item>,
488 I::IntoIter: ExactSizeIterator,
489 {
490 let chunk_identifier = position.chunk_identifier();
491 let item_index = position.index();
492
493 let chunk = self
494 .links
495 .chunk_mut(chunk_identifier)
496 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
497
498 let chunk = match &mut chunk.content {
499 ChunkContent::Gap(..) => {
500 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
501 }
502
503 ChunkContent::Items(current_items) => {
504 let current_items_length = current_items.len();
505
506 if item_index > current_items_length {
507 return Err(Error::InvalidItemIndex { index: item_index });
508 }
509
510 let items = items.into_iter();
512
513 if item_index == current_items_length {
515 chunk
516 .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
518 }
519 else {
521 if let Some(updates) = self.updates.as_mut() {
522 updates.push(Update::DetachLastItems {
523 at: Position(chunk_identifier, item_index),
524 });
525 }
526
527 let detached_items = current_items.split_off(item_index);
529
530 let chunk = chunk
531 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
533
534 if let Some(updates) = self.updates.as_mut() {
535 updates.push(Update::StartReattachItems);
536 }
537
538 let chunk = chunk
539 .push_items(
541 detached_items.into_iter(),
542 &self.chunk_identifier_generator,
543 &mut self.updates,
544 );
545
546 if let Some(updates) = self.updates.as_mut() {
547 updates.push(Update::EndReattachItems);
548 }
549
550 chunk
551 }
552 }
553 };
554
555 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
558 self.links.last = Some(chunk.as_ptr());
561 }
562
563 Ok(())
564 }
565
566 pub fn remove_item_at(&mut self, position: Position) -> Result<Item, Error> {
574 let chunk_identifier = position.chunk_identifier();
575 let item_index = position.index();
576
577 let mut chunk_ptr = None;
578 let removed_item;
579
580 {
581 let chunk = self
582 .links
583 .chunk_mut(chunk_identifier)
584 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
585
586 let current_items = match &mut chunk.content {
587 ChunkContent::Gap(..) => {
588 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
589 }
590 ChunkContent::Items(current_items) => current_items,
591 };
592
593 if item_index > current_items.len() {
594 return Err(Error::InvalidItemIndex { index: item_index });
595 }
596
597 removed_item = current_items.remove(item_index);
598 if let Some(updates) = self.updates.as_mut() {
599 updates.push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
600 }
601
602 if current_items.is_empty() && !chunk.is_first_chunk() {
604 chunk.unlink(self.updates.as_mut());
606
607 chunk_ptr = Some(chunk.as_ptr());
608
609 if chunk.is_last_chunk() {
612 self.links.last = chunk.previous;
613 }
614 }
615
616 }
618
619 if let Some(chunk_ptr) = chunk_ptr {
620 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
627 }
628
629 Ok(removed_item)
630 }
631
632 pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
637 where
638 Item: Clone,
639 {
640 let chunk_identifier = position.chunk_identifier();
641 let item_index = position.index();
642
643 let chunk = self
644 .links
645 .chunk_mut(chunk_identifier)
646 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
647
648 match &mut chunk.content {
649 ChunkContent::Gap(..) => {
650 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
651 }
652
653 ChunkContent::Items(current_items) => {
654 if item_index >= current_items.len() {
655 return Err(Error::InvalidItemIndex { index: item_index });
656 }
657
658 if let Some(updates) = self.updates.as_mut() {
660 updates.push(Update::ReplaceItem {
661 at: Position(chunk_identifier, item_index),
662 item: item.clone(),
663 });
664 }
665
666 current_items[item_index] = item;
667 }
668 }
669
670 Ok(())
671 }
672
673 pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
678 where
679 Item: Clone,
680 Gap: Clone,
681 {
682 let chunk_identifier = position.chunk_identifier();
683 let item_index = position.index();
684
685 let chunk = self
686 .links
687 .chunk_mut(chunk_identifier)
688 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
689
690 let chunk = match &mut chunk.content {
691 ChunkContent::Gap(..) => {
692 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
693 }
694
695 ChunkContent::Items(current_items) => {
696 if item_index == 0 {
700 let chunk_was_first = chunk.is_first_chunk();
701 let chunk_was_last = chunk.is_last_chunk();
702
703 let new_chunk = chunk.insert_before(
704 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
705 self.updates.as_mut(),
706 );
707
708 let new_chunk_ptr = new_chunk.as_ptr();
709 let chunk_ptr = chunk.as_ptr();
710
711 if chunk_was_first {
716 self.links.first = new_chunk_ptr;
717
718 if chunk_was_last {
720 self.links.last = Some(chunk_ptr);
721 }
722 }
723
724 return Ok(());
725 }
726
727 let current_items_length = current_items.len();
728
729 if item_index >= current_items_length {
730 return Err(Error::InvalidItemIndex { index: item_index });
731 }
732
733 if let Some(updates) = self.updates.as_mut() {
734 updates.push(Update::DetachLastItems {
735 at: Position(chunk_identifier, item_index),
736 });
737 }
738
739 let detached_items = current_items.split_off(item_index);
741
742 let chunk = chunk
743 .insert_next(
745 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
746 &mut self.updates,
747 );
748
749 if let Some(updates) = self.updates.as_mut() {
750 updates.push(Update::StartReattachItems);
751 }
752
753 let chunk = chunk
754 .insert_next(
756 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
757 &mut self.updates,
758 )
759 .push_items(
761 detached_items.into_iter(),
762 &self.chunk_identifier_generator,
763 &mut self.updates,
764 );
765
766 if let Some(updates) = self.updates.as_mut() {
767 updates.push(Update::EndReattachItems);
768 }
769
770 chunk
771 }
772 };
773
774 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
777 self.links.last = Some(chunk.as_ptr());
780 }
781
782 Ok(())
783 }
784
785 pub fn remove_empty_chunk_at(
794 &mut self,
795 chunk_identifier: ChunkIdentifier,
796 ) -> Result<Option<Position>, Error> {
797 if self.links.first_chunk().is_last_chunk() {
799 return Err(Error::RemovingLastChunk);
800 }
801
802 let chunk = self
803 .links
804 .chunk_mut(chunk_identifier)
805 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
806
807 if chunk.num_items() > 0 {
808 return Err(Error::RemovingNonEmptyItemsChunk { identifier: chunk_identifier });
809 }
810
811 let chunk_was_first = chunk.is_first_chunk();
812 let chunk_was_last = chunk.is_last_chunk();
813 let next_ptr = chunk.next;
814 let previous_ptr = chunk.previous;
815 let position_of_next = chunk.next().map(|next| next.first_position());
816
817 chunk.unlink(self.updates.as_mut());
818
819 let chunk_ptr = chunk.as_ptr();
820
821 if chunk_was_first {
823 if let Some(next_ptr) = next_ptr {
825 self.links.first = next_ptr;
826 }
827 }
828
829 if chunk_was_last {
830 self.links.last = previous_ptr;
831 }
832
833 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
836
837 Ok(position_of_next)
839 }
840
841 pub fn replace_gap_at<I>(
849 &mut self,
850 items: I,
851 chunk_identifier: ChunkIdentifier,
852 ) -> Result<&Chunk<CAP, Item, Gap>, Error>
853 where
854 Item: Clone,
855 Gap: Clone,
856 I: IntoIterator<Item = Item>,
857 I::IntoIter: ExactSizeIterator,
858 {
859 let chunk_ptr;
860 let new_chunk_ptr;
861
862 {
863 let chunk = self
864 .links
865 .chunk_mut(chunk_identifier)
866 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
867
868 if chunk.is_items() {
869 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
870 }
871
872 let chunk_was_first = chunk.is_first_chunk();
873
874 let maybe_last_chunk_ptr = {
875 let items = items.into_iter();
876
877 let last_inserted_chunk = chunk
878 .insert_next(
880 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
881 &mut self.updates,
882 )
883 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
885
886 last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
887 };
888
889 new_chunk_ptr = chunk
890 .next
891 .unwrap();
893
894 chunk.unlink(self.updates.as_mut());
896
897 chunk_ptr = chunk.as_ptr();
899
900 if chunk_was_first {
902 self.links.first = new_chunk_ptr;
903 }
904
905 if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
908 self.links.last = Some(last_chunk_ptr);
909 }
910
911 }
913
914 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
919
920 Ok(
921 unsafe { new_chunk_ptr.as_ref() },
925 )
926 }
927
928 pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
930 where
931 P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
932 {
933 self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
934 }
935
936 pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
938 where
939 P: FnMut(&'a Item) -> bool,
940 {
941 self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
942 }
943
944 pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
948 IterBackward::new(self.links.latest_chunk())
949 }
950
951 pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
955 Iter::new(self.links.first_chunk())
956 }
957
958 pub fn rchunks_from(
963 &self,
964 identifier: ChunkIdentifier,
965 ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
966 Ok(IterBackward::new(
967 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
968 ))
969 }
970
971 pub fn chunks_from(
976 &self,
977 identifier: ChunkIdentifier,
978 ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
979 Ok(Iter::new(
980 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
981 ))
982 }
983
984 pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
988 self.ritems_from(self.links.latest_chunk().last_position())
989 .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
990 }
991
992 pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
996 let first_chunk = self.links.first_chunk();
997
998 self.items_from(first_chunk.first_position())
999 .expect("`items` cannot fail because at least one empty chunk must exist")
1000 }
1001
1002 pub fn ritems_from(
1006 &self,
1007 position: Position,
1008 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1009 Ok(self
1010 .rchunks_from(position.chunk_identifier())?
1011 .filter_map(|chunk| match &chunk.content {
1012 ChunkContent::Gap(..) => None,
1013 ChunkContent::Items(items) => {
1014 let identifier = chunk.identifier();
1015
1016 Some(
1017 items.iter().enumerate().rev().map(move |(item_index, item)| {
1018 (Position(identifier, item_index), item)
1019 }),
1020 )
1021 }
1022 })
1023 .flatten()
1024 .skip_while({
1025 let expected_index = position.index();
1026
1027 move |(Position(chunk_identifier, item_index), _item)| {
1028 *chunk_identifier == position.chunk_identifier()
1029 && *item_index != expected_index
1030 }
1031 }))
1032 }
1033
1034 pub fn items_from(
1038 &self,
1039 position: Position,
1040 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1041 Ok(self
1042 .chunks_from(position.chunk_identifier())?
1043 .filter_map(|chunk| match &chunk.content {
1044 ChunkContent::Gap(..) => None,
1045 ChunkContent::Items(items) => {
1046 let identifier = chunk.identifier();
1047
1048 Some(
1049 items.iter().enumerate().map(move |(item_index, item)| {
1050 (Position(identifier, item_index), item)
1051 }),
1052 )
1053 }
1054 })
1055 .flatten()
1056 .skip(position.index()))
1057 }
1058
1059 #[must_use]
1071 pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
1072 self.updates.as_mut()
1073 }
1074
1075 pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
1082 let (updates, token) = self
1083 .updates
1084 .as_mut()
1085 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1086 let chunk_iterator = self.chunks();
1087
1088 Some(AsVector::new(updates, token, chunk_iterator))
1089 }
1090
1091 pub fn order_tracker(
1099 &mut self,
1100 all_chunks: Option<Vec<ChunkMetadata>>,
1101 ) -> Option<OrderTracker<Item, Gap>>
1102 where
1103 Item: Clone,
1104 {
1105 let (updates, token) = self
1106 .updates
1107 .as_mut()
1108 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1109
1110 Some(OrderTracker::new(
1111 updates,
1112 token,
1113 all_chunks.unwrap_or_else(|| {
1114 self.chunks()
1116 .map(|chunk| ChunkMetadata {
1117 identifier: chunk.identifier(),
1118 num_items: chunk.num_items(),
1119 previous: chunk.previous().map(|prev| prev.identifier()),
1120 next: chunk.next().map(|next| next.identifier()),
1121 })
1122 .collect()
1123 }),
1124 ))
1125 }
1126
1127 pub fn num_items(&self) -> usize {
1129 self.items().count()
1130 }
1131}
1132
1133impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
1134 fn drop(&mut self) {
1135 unsafe {
1145 self.links.clear();
1146 }
1147 }
1148}
1149
1150unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1154
1155unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1159
1160#[derive(Debug)]
1170pub struct ChunkIdentifierGenerator {
1171 next: AtomicU64,
1172}
1173
1174impl ChunkIdentifierGenerator {
1175 const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1177
1178 pub fn new_from_scratch() -> Self {
1181 Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1182 }
1183
1184 pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1187 Self { next: AtomicU64::new(last_chunk_identifier.0) }
1188 }
1189
1190 fn next(&self) -> ChunkIdentifier {
1195 let previous = self.next.fetch_add(1, atomic::Ordering::Relaxed);
1196
1197 if previous == u64::MAX {
1200 panic!(
1201 "No more chunk identifiers available. Congrats, you did it. \
1202 2^64 identifiers have been consumed."
1203 )
1204 }
1205
1206 ChunkIdentifier(previous + 1)
1207 }
1208
1209 #[doc(hidden)]
1213 pub fn current(&self) -> ChunkIdentifier {
1214 ChunkIdentifier(self.next.load(atomic::Ordering::Relaxed))
1215 }
1216}
1217
1218#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1224#[repr(transparent)]
1225pub struct ChunkIdentifier(u64);
1226
1227impl ChunkIdentifier {
1228 pub fn new(identifier: u64) -> Self {
1230 Self(identifier)
1231 }
1232
1233 pub fn index(&self) -> u64 {
1235 self.0
1236 }
1237}
1238
1239impl PartialEq<u64> for ChunkIdentifier {
1240 fn eq(&self, other: &u64) -> bool {
1241 self.0 == *other
1242 }
1243}
1244
1245#[derive(Copy, Clone, Debug, PartialEq)]
1249pub struct Position(ChunkIdentifier, usize);
1250
1251impl Position {
1252 pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1254 Self(chunk_identifier, index)
1255 }
1256
1257 pub fn chunk_identifier(&self) -> ChunkIdentifier {
1259 self.0
1260 }
1261
1262 pub fn index(&self) -> usize {
1264 self.1
1265 }
1266
1267 pub fn decrement_index(&mut self) {
1273 self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1274 }
1275
1276 pub fn increment_index(&mut self) {
1283 self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1284 }
1285}
1286
1287#[derive(Debug)]
1290pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1291 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1292}
1293
1294impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1295 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1297 Self { chunk: Some(from_chunk) }
1298 }
1299}
1300
1301impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1302 type Item = &'a Chunk<CAP, Item, Gap>;
1303
1304 fn next(&mut self) -> Option<Self::Item> {
1305 self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1306 }
1307}
1308
1309#[derive(Debug)]
1312pub struct Iter<'a, const CAP: usize, Item, Gap> {
1313 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1314}
1315
1316impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1317 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1319 Self { chunk: Some(from_chunk) }
1320 }
1321}
1322
1323impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1324 type Item = &'a Chunk<CAP, Item, Gap>;
1325
1326 fn next(&mut self) -> Option<Self::Item> {
1327 self.chunk.inspect(|chunk| self.chunk = chunk.next())
1328 }
1329}
1330
1331#[derive(Clone, Debug)]
1333pub enum ChunkContent<Item, Gap> {
1334 Gap(Gap),
1337
1338 Items(Vec<Item>),
1340}
1341
1342pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1344 previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1346
1347 lazy_previous: Option<ChunkIdentifier>,
1352
1353 next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1355
1356 identifier: ChunkIdentifier,
1358
1359 content: ChunkContent<Item, Gap>,
1361}
1362
1363impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1364 fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1366 Self::new(identifier, ChunkContent::Gap(content))
1367 }
1368
1369 fn new_items(identifier: ChunkIdentifier) -> Self {
1371 Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1372 }
1373
1374 fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1375 Self { previous: None, lazy_previous: None, next: None, identifier, content }
1376 }
1377
1378 fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1380 let chunk = Self::new(identifier, content);
1381 let chunk_box = Box::new(chunk);
1382
1383 NonNull::from(Box::leak(chunk_box))
1384 }
1385
1386 fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1388 let chunk = Self::new_gap(identifier, content);
1389 let chunk_box = Box::new(chunk);
1390
1391 NonNull::from(Box::leak(chunk_box))
1392 }
1393
1394 fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1396 let chunk = Self::new_items(identifier);
1397 let chunk_box = Box::new(chunk);
1398
1399 NonNull::from(Box::leak(chunk_box))
1400 }
1401
1402 pub fn as_ptr(&self) -> NonNull<Self> {
1404 NonNull::from(self)
1405 }
1406
1407 pub fn is_gap(&self) -> bool {
1409 matches!(self.content, ChunkContent::Gap(..))
1410 }
1411
1412 pub fn is_items(&self) -> bool {
1414 !self.is_gap()
1415 }
1416
1417 pub fn is_definitive_head(&self) -> bool {
1420 self.previous.is_none() && self.lazy_previous.is_none()
1421 }
1422
1423 fn is_first_chunk(&self) -> bool {
1425 self.previous.is_none()
1426 }
1427
1428 fn is_last_chunk(&self) -> bool {
1430 self.next.is_none()
1431 }
1432
1433 #[doc(hidden)]
1437 pub fn lazy_previous(&self) -> Option<ChunkIdentifier> {
1438 self.lazy_previous
1439 }
1440
1441 pub fn identifier(&self) -> ChunkIdentifier {
1443 self.identifier
1444 }
1445
1446 pub fn content(&self) -> &ChunkContent<Item, Gap> {
1448 &self.content
1449 }
1450
1451 pub fn first_position(&self) -> Position {
1455 Position(self.identifier(), 0)
1456 }
1457
1458 pub fn last_position(&self) -> Position {
1462 let identifier = self.identifier();
1463
1464 match &self.content {
1465 ChunkContent::Gap(..) => Position(identifier, 0),
1466 ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1467 }
1468 }
1469
1470 pub fn num_items(&self) -> usize {
1474 match &self.content {
1475 ChunkContent::Gap(..) => 0,
1476 ChunkContent::Items(items) => items.len(),
1477 }
1478 }
1479
1480 fn push_items<I>(
1492 &mut self,
1493 mut new_items: I,
1494 chunk_identifier_generator: &ChunkIdentifierGenerator,
1495 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1496 ) -> &mut Self
1497 where
1498 I: Iterator<Item = Item> + ExactSizeIterator,
1499 Item: Clone,
1500 Gap: Clone,
1501 {
1502 if new_items.len() == 0 {
1504 return self;
1505 }
1506
1507 let identifier = self.identifier();
1508 let prev_num_items = self.num_items();
1509
1510 match &mut self.content {
1511 ChunkContent::Gap(..) => {
1514 self
1515 .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1517 .push_items(new_items, chunk_identifier_generator, updates)
1520 }
1521
1522 ChunkContent::Items(items) => {
1523 let free_space = CAPACITY.saturating_sub(prev_num_items);
1525
1526 if new_items.len() <= free_space {
1528 let start = items.len();
1529 items.extend(new_items);
1530
1531 if let Some(updates) = updates.as_mut() {
1532 updates.push(Update::PushItems {
1533 at: Position(identifier, start),
1534 items: items[start..].to_vec(),
1535 });
1536 }
1537
1538 self
1540 } else {
1541 if free_space > 0 {
1542 let start = items.len();
1544 items.extend(new_items.by_ref().take(free_space));
1545
1546 if let Some(updates) = updates.as_mut() {
1547 updates.push(Update::PushItems {
1548 at: Position(identifier, start),
1549 items: items[start..].to_vec(),
1550 });
1551 }
1552 }
1553
1554 self
1555 .insert_next(
1557 Self::new_items_leaked(chunk_identifier_generator.next()),
1558 updates,
1559 )
1560 .push_items(new_items, chunk_identifier_generator, updates)
1563 }
1564 }
1565 }
1566 }
1567
1568 fn insert_next(
1573 &mut self,
1574 mut new_chunk_ptr: NonNull<Self>,
1575 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1576 ) -> &mut Self
1577 where
1578 Gap: Clone,
1579 {
1580 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1581
1582 if let Some(next_chunk) = self.next_mut() {
1584 next_chunk.previous = Some(new_chunk_ptr);
1586
1587 new_chunk.next = self.next;
1589 }
1590
1591 self.next = Some(new_chunk_ptr);
1593 new_chunk.previous = Some(self.as_ptr());
1595
1596 if let Some(updates) = updates.as_mut() {
1597 let previous = new_chunk.previous().map(Chunk::identifier);
1598 let new = new_chunk.identifier();
1599 let next = new_chunk.next().map(Chunk::identifier);
1600
1601 match new_chunk.content() {
1602 ChunkContent::Gap(gap) => {
1603 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1604 }
1605
1606 ChunkContent::Items(..) => {
1607 updates.push(Update::NewItemsChunk { previous, new, next })
1608 }
1609 }
1610 }
1611
1612 new_chunk
1613 }
1614
1615 fn insert_before(
1620 &mut self,
1621 mut new_chunk_ptr: NonNull<Self>,
1622 updates: Option<&mut ObservableUpdates<Item, Gap>>,
1623 ) -> &mut Self
1624 where
1625 Gap: Clone,
1626 {
1627 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1628
1629 if let Some(previous_chunk) = self.previous_mut() {
1631 previous_chunk.next = Some(new_chunk_ptr);
1633
1634 new_chunk.previous = self.previous;
1636 }
1637 else {
1640 new_chunk.lazy_previous = self.lazy_previous.take();
1641 }
1642
1643 self.previous = Some(new_chunk_ptr);
1645 new_chunk.next = Some(self.as_ptr());
1647
1648 if let Some(updates) = updates {
1649 let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1650 let new = new_chunk.identifier();
1651 let next = new_chunk.next().map(Chunk::identifier);
1652
1653 match new_chunk.content() {
1654 ChunkContent::Gap(gap) => {
1655 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1656 }
1657
1658 ChunkContent::Items(..) => {
1659 updates.push(Update::NewItemsChunk { previous, new, next })
1660 }
1661 }
1662 }
1663
1664 new_chunk
1665 }
1666
1667 fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1672 let previous_ptr = self.previous;
1673 let next_ptr = self.next;
1674 let lazy_previous = self.lazy_previous.take();
1678
1679 if let Some(previous) = self.previous_mut() {
1680 previous.next = next_ptr;
1681 }
1682
1683 if let Some(next) = self.next_mut() {
1684 next.previous = previous_ptr;
1685 next.lazy_previous = lazy_previous;
1686 }
1687
1688 if let Some(updates) = updates {
1689 updates.push(Update::RemoveChunk(self.identifier()));
1690 }
1691 }
1692
1693 fn previous(&self) -> Option<&Self> {
1695 self.previous.map(|non_null| unsafe { non_null.as_ref() })
1696 }
1697
1698 fn previous_mut(&mut self) -> Option<&mut Self> {
1700 self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1701 }
1702
1703 fn next(&self) -> Option<&Self> {
1705 self.next.map(|non_null| unsafe { non_null.as_ref() })
1706 }
1707
1708 fn next_mut(&mut self) -> Option<&mut Self> {
1710 self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1711 }
1712}
1713
1714impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<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("LinkedChunk")
1722 .field("first (deref)", unsafe { self.links.first.as_ref() })
1723 .field("last", &self.links.last)
1724 .finish_non_exhaustive()
1725 }
1726}
1727
1728impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1729where
1730 Item: fmt::Debug,
1731 Gap: fmt::Debug,
1732{
1733 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1734 formatter
1735 .debug_struct("Chunk")
1736 .field("identifier", &self.identifier)
1737 .field("content", &self.content)
1738 .field("previous", &self.previous)
1739 .field("ptr", &std::ptr::from_ref(self))
1740 .field("next", &self.next)
1741 .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1742 .finish()
1743 }
1744}
1745
1746#[derive(Clone, Debug)]
1752pub struct RawChunk<Item, Gap> {
1753 pub content: ChunkContent<Item, Gap>,
1755
1756 pub previous: Option<ChunkIdentifier>,
1758
1759 pub identifier: ChunkIdentifier,
1761
1762 pub next: Option<ChunkIdentifier>,
1764}
1765
1766#[derive(Clone, Debug)]
1769pub struct ChunkMetadata {
1770 pub num_items: usize,
1774
1775 pub previous: Option<ChunkIdentifier>,
1777
1778 pub identifier: ChunkIdentifier,
1780
1781 pub next: Option<ChunkIdentifier>,
1783}
1784
1785#[cfg(test)]
1786mod tests {
1787 use std::{
1788 ops::Not,
1789 sync::{Arc, atomic::Ordering},
1790 };
1791
1792 use assert_matches::assert_matches;
1793
1794 use super::{
1795 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Error, LinkedChunk,
1796 Position,
1797 };
1798
1799 #[test]
1800 fn test_chunk_identifier_generator() {
1801 let generator = ChunkIdentifierGenerator::new_from_scratch();
1802
1803 assert_eq!(generator.next(), ChunkIdentifier(1));
1804 assert_eq!(generator.next(), ChunkIdentifier(2));
1805 assert_eq!(generator.next(), ChunkIdentifier(3));
1806 assert_eq!(generator.next(), ChunkIdentifier(4));
1807
1808 let generator =
1809 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1810
1811 assert_eq!(generator.next(), ChunkIdentifier(43));
1812 assert_eq!(generator.next(), ChunkIdentifier(44));
1813 assert_eq!(generator.next(), ChunkIdentifier(45));
1814 assert_eq!(generator.next(), ChunkIdentifier(46));
1815 }
1816
1817 #[test]
1818 fn test_empty() {
1819 let items = LinkedChunk::<3, char, ()>::new();
1820
1821 assert_eq!(items.num_items(), 0);
1822
1823 }
1826
1827 #[test]
1828 fn test_updates() {
1829 assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1830 assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1831 }
1832
1833 #[test]
1834 fn test_new_with_initial_update() {
1835 use super::Update::*;
1836
1837 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1838
1839 assert_eq!(
1840 linked_chunk.updates().unwrap().take(),
1841 &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1842 );
1843 }
1844
1845 #[test]
1846 fn test_push_items() {
1847 use super::Update::*;
1848
1849 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1850
1851 let _ = linked_chunk.updates().unwrap().take();
1853
1854 linked_chunk.push_items_back(['a']);
1855
1856 assert_items_eq!(linked_chunk, ['a']);
1857 assert_eq!(
1858 linked_chunk.updates().unwrap().take(),
1859 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1860 );
1861
1862 linked_chunk.push_items_back(['b', 'c']);
1863 assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1864 assert_eq!(
1865 linked_chunk.updates().unwrap().take(),
1866 &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1867 );
1868
1869 linked_chunk.push_items_back(['d', 'e']);
1870 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
1871 assert_eq!(
1872 linked_chunk.updates().unwrap().take(),
1873 &[
1874 NewItemsChunk {
1875 previous: Some(ChunkIdentifier(0)),
1876 new: ChunkIdentifier(1),
1877 next: None
1878 },
1879 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1880 ]
1881 );
1882
1883 linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1884 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1885 assert_eq!(
1886 linked_chunk.updates().unwrap().take(),
1887 &[
1888 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1889 NewItemsChunk {
1890 previous: Some(ChunkIdentifier(1)),
1891 new: ChunkIdentifier(2),
1892 next: None,
1893 },
1894 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1895 NewItemsChunk {
1896 previous: Some(ChunkIdentifier(2)),
1897 new: ChunkIdentifier(3),
1898 next: None,
1899 },
1900 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1901 ]
1902 );
1903
1904 assert_eq!(linked_chunk.num_items(), 10);
1905 }
1906
1907 #[test]
1908 fn test_push_gap() {
1909 use super::Update::*;
1910
1911 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1912
1913 let _ = linked_chunk.updates().unwrap().take();
1915
1916 linked_chunk.push_items_back(['a']);
1917 assert_items_eq!(linked_chunk, ['a']);
1918 assert_eq!(
1919 linked_chunk.updates().unwrap().take(),
1920 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1921 );
1922
1923 linked_chunk.push_gap_back(());
1924 assert_items_eq!(linked_chunk, ['a'] [-]);
1925 assert_eq!(
1926 linked_chunk.updates().unwrap().take(),
1927 &[NewGapChunk {
1928 previous: Some(ChunkIdentifier(0)),
1929 new: ChunkIdentifier(1),
1930 next: None,
1931 gap: (),
1932 }]
1933 );
1934
1935 linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1936 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1937 assert_eq!(
1938 linked_chunk.updates().unwrap().take(),
1939 &[
1940 NewItemsChunk {
1941 previous: Some(ChunkIdentifier(1)),
1942 new: ChunkIdentifier(2),
1943 next: None,
1944 },
1945 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
1946 NewItemsChunk {
1947 previous: Some(ChunkIdentifier(2)),
1948 new: ChunkIdentifier(3),
1949 next: None,
1950 },
1951 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
1952 ]
1953 );
1954
1955 linked_chunk.push_gap_back(());
1956 linked_chunk.push_gap_back(()); assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
1958 assert_eq!(
1959 linked_chunk.updates().unwrap().take(),
1960 &[
1961 NewGapChunk {
1962 previous: Some(ChunkIdentifier(3)),
1963 new: ChunkIdentifier(4),
1964 next: None,
1965 gap: (),
1966 },
1967 NewGapChunk {
1968 previous: Some(ChunkIdentifier(4)),
1969 new: ChunkIdentifier(5),
1970 next: None,
1971 gap: (),
1972 }
1973 ]
1974 );
1975
1976 linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
1977 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
1978 assert_eq!(
1979 linked_chunk.updates().unwrap().take(),
1980 &[
1981 NewItemsChunk {
1982 previous: Some(ChunkIdentifier(5)),
1983 new: ChunkIdentifier(6),
1984 next: None,
1985 },
1986 PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
1987 NewItemsChunk {
1988 previous: Some(ChunkIdentifier(6)),
1989 new: ChunkIdentifier(7),
1990 next: None,
1991 },
1992 PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
1993 ]
1994 );
1995
1996 assert_eq!(linked_chunk.num_items(), 9);
1997 }
1998
1999 #[test]
2000 fn test_identifiers_and_positions() {
2001 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2002 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2003 linked_chunk.push_gap_back(());
2004 linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
2005 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
2006
2007 assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
2008 assert_eq!(
2009 linked_chunk.item_position(|item| *item == 'e'),
2010 Some(Position(ChunkIdentifier(1), 1))
2011 );
2012 }
2013
2014 #[test]
2015 fn test_rchunks() {
2016 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2017 linked_chunk.push_items_back(['a', 'b']);
2018 linked_chunk.push_gap_back(());
2019 linked_chunk.push_items_back(['c', 'd', 'e']);
2020
2021 let mut iterator = linked_chunk.rchunks();
2022
2023 assert_matches!(
2024 iterator.next(),
2025 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2026 assert_eq!(items, &['e']);
2027 }
2028 );
2029 assert_matches!(
2030 iterator.next(),
2031 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2032 assert_eq!(items, &['c', 'd']);
2033 }
2034 );
2035 assert_matches!(
2036 iterator.next(),
2037 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2038 );
2039 assert_matches!(
2040 iterator.next(),
2041 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2042 assert_eq!(items, &['a', 'b']);
2043 }
2044 );
2045 assert_matches!(iterator.next(), None);
2046 }
2047
2048 #[test]
2049 fn test_chunks() {
2050 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2051 linked_chunk.push_items_back(['a', 'b']);
2052 linked_chunk.push_gap_back(());
2053 linked_chunk.push_items_back(['c', 'd', 'e']);
2054
2055 let mut iterator = linked_chunk.chunks();
2056
2057 assert_matches!(
2058 iterator.next(),
2059 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2060 assert_eq!(items, &['a', 'b']);
2061 }
2062 );
2063 assert_matches!(
2064 iterator.next(),
2065 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2066 );
2067 assert_matches!(
2068 iterator.next(),
2069 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2070 assert_eq!(items, &['c', 'd']);
2071 }
2072 );
2073 assert_matches!(
2074 iterator.next(),
2075 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2076 assert_eq!(items, &['e']);
2077 }
2078 );
2079 assert_matches!(iterator.next(), None);
2080 }
2081
2082 #[test]
2083 fn test_rchunks_from() -> Result<(), Error> {
2084 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2085 linked_chunk.push_items_back(['a', 'b']);
2086 linked_chunk.push_gap_back(());
2087 linked_chunk.push_items_back(['c', 'd', 'e']);
2088
2089 let mut iterator = linked_chunk.rchunks_from(
2090 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2091 )?;
2092
2093 assert_matches!(
2094 iterator.next(),
2095 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2096 assert_eq!(items, &['c', 'd']);
2097 }
2098 );
2099 assert_matches!(
2100 iterator.next(),
2101 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2102 );
2103 assert_matches!(
2104 iterator.next(),
2105 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2106 assert_eq!(items, &['a', 'b']);
2107 }
2108 );
2109 assert_matches!(iterator.next(), None);
2110
2111 Ok(())
2112 }
2113
2114 #[test]
2115 fn test_chunks_from() -> Result<(), Error> {
2116 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2117 linked_chunk.push_items_back(['a', 'b']);
2118 linked_chunk.push_gap_back(());
2119 linked_chunk.push_items_back(['c', 'd', 'e']);
2120
2121 let mut iterator = linked_chunk.chunks_from(
2122 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2123 )?;
2124
2125 assert_matches!(
2126 iterator.next(),
2127 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2128 assert_eq!(items, &['c', 'd']);
2129 }
2130 );
2131 assert_matches!(
2132 iterator.next(),
2133 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2134 assert_eq!(items, &['e']);
2135 }
2136 );
2137 assert_matches!(iterator.next(), None);
2138
2139 Ok(())
2140 }
2141
2142 #[test]
2143 fn test_ritems() {
2144 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2145 linked_chunk.push_items_back(['a', 'b']);
2146 linked_chunk.push_gap_back(());
2147 linked_chunk.push_items_back(['c', 'd', 'e']);
2148
2149 let mut iterator = linked_chunk.ritems();
2150
2151 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2152 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2153 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2154 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2155 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2156 assert_matches!(iterator.next(), None);
2157 }
2158
2159 #[test]
2160 fn test_ritems_with_final_gap() -> Result<(), Error> {
2161 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2162 linked_chunk.push_items_back(['a', 'b']);
2163 linked_chunk.push_gap_back(());
2164 linked_chunk.push_items_back(['c', 'd', 'e']);
2165 linked_chunk.push_gap_back(());
2166
2167 let mut iterator = linked_chunk.ritems();
2168
2169 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
2170 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2171 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2172 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2173 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2174 assert_matches!(iterator.next(), None);
2175
2176 Ok(())
2177 }
2178
2179 #[test]
2180 fn test_ritems_empty() {
2181 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2182 let mut iterator = linked_chunk.ritems();
2183
2184 assert_matches!(iterator.next(), None);
2185 }
2186
2187 #[test]
2188 fn test_items() {
2189 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2190 linked_chunk.push_items_back(['a', 'b']);
2191 linked_chunk.push_gap_back(());
2192 linked_chunk.push_items_back(['c', 'd', 'e']);
2193
2194 let mut iterator = linked_chunk.items();
2195
2196 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2197 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2198 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2199 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2200 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2201 assert_matches!(iterator.next(), None);
2202 }
2203
2204 #[test]
2205 fn test_items_empty() {
2206 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2207 let mut iterator = linked_chunk.items();
2208
2209 assert_matches!(iterator.next(), None);
2210 }
2211
2212 #[test]
2213 fn test_ritems_from() -> Result<(), Error> {
2214 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2215 linked_chunk.push_items_back(['a', 'b']);
2216 linked_chunk.push_gap_back(());
2217 linked_chunk.push_items_back(['c', 'd', 'e']);
2218
2219 let mut iterator =
2220 linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2221
2222 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2223 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2224 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2225 assert_matches!(iterator.next(), None);
2226
2227 Ok(())
2228 }
2229
2230 #[test]
2231 fn test_items_from() -> Result<(), Error> {
2232 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2233 linked_chunk.push_items_back(['a', 'b']);
2234 linked_chunk.push_gap_back(());
2235 linked_chunk.push_items_back(['c', 'd', 'e']);
2236
2237 let mut iterator =
2238 linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2239
2240 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2241 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2242 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2243 assert_matches!(iterator.next(), None);
2244
2245 Ok(())
2246 }
2247
2248 #[test]
2249 fn test_insert_items_at() -> Result<(), Error> {
2250 use super::Update::*;
2251
2252 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2253
2254 let _ = linked_chunk.updates().unwrap().take();
2256
2257 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2258 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2259 assert_eq!(
2260 linked_chunk.updates().unwrap().take(),
2261 &[
2262 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2263 NewItemsChunk {
2264 previous: Some(ChunkIdentifier(0)),
2265 new: ChunkIdentifier(1),
2266 next: None,
2267 },
2268 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2269 ]
2270 );
2271
2272 {
2274 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2275
2276 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2279
2280 assert_items_eq!(
2281 linked_chunk,
2282 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2283 );
2284 assert_eq!(linked_chunk.num_items(), 10);
2285 assert_eq!(
2286 linked_chunk.updates().unwrap().take(),
2287 &[
2288 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2289 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2290 NewItemsChunk {
2291 previous: Some(ChunkIdentifier(1)),
2292 new: ChunkIdentifier(2),
2293 next: None,
2294 },
2295 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2296 StartReattachItems,
2297 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2298 NewItemsChunk {
2299 previous: Some(ChunkIdentifier(2)),
2300 new: ChunkIdentifier(3),
2301 next: None,
2302 },
2303 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2304 EndReattachItems,
2305 ]
2306 );
2307 }
2308
2309 {
2311 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2312 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2313
2314 assert_items_eq!(
2315 linked_chunk,
2316 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2317 );
2318 assert_eq!(linked_chunk.num_items(), 14);
2319 assert_eq!(
2320 linked_chunk.updates().unwrap().take(),
2321 &[
2322 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2323 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2324 NewItemsChunk {
2325 previous: Some(ChunkIdentifier(0)),
2326 new: ChunkIdentifier(4),
2327 next: Some(ChunkIdentifier(1)),
2328 },
2329 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2330 StartReattachItems,
2331 PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2332 NewItemsChunk {
2333 previous: Some(ChunkIdentifier(4)),
2334 new: ChunkIdentifier(5),
2335 next: Some(ChunkIdentifier(1)),
2336 },
2337 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2338 EndReattachItems,
2339 ]
2340 );
2341 }
2342
2343 {
2345 let pos_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2346 linked_chunk.insert_items_at(pos_c, ['r', 's'])?;
2347
2348 assert_items_eq!(
2349 linked_chunk,
2350 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2351 );
2352 assert_eq!(linked_chunk.num_items(), 16);
2353 assert_eq!(
2354 linked_chunk.updates().unwrap().take(),
2355 &[
2356 DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2357 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2358 StartReattachItems,
2359 PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2360 EndReattachItems,
2361 ]
2362 );
2363 }
2364
2365 {
2367 let pos_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2368 let pos_f = Position(pos_f.chunk_identifier(), pos_f.index() + 1);
2369
2370 linked_chunk.insert_items_at(pos_f, ['p', 'q'])?;
2371 assert_items_eq!(
2372 linked_chunk,
2373 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2374 );
2375 assert_eq!(
2376 linked_chunk.updates().unwrap().take(),
2377 &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2378 );
2379 assert_eq!(linked_chunk.num_items(), 18);
2380 }
2381
2382 {
2384 assert_matches!(
2385 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2386 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2387 );
2388 assert!(linked_chunk.updates().unwrap().take().is_empty());
2389 }
2390
2391 {
2393 assert_matches!(
2394 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2395 Err(Error::InvalidItemIndex { index: 128 })
2396 );
2397 assert!(linked_chunk.updates().unwrap().take().is_empty());
2398 }
2399
2400 {
2402 linked_chunk.push_gap_back(());
2404 assert_items_eq!(
2405 linked_chunk,
2406 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2407 );
2408 assert_eq!(
2409 linked_chunk.updates().unwrap().take(),
2410 &[NewGapChunk {
2411 previous: Some(ChunkIdentifier(3)),
2412 new: ChunkIdentifier(6),
2413 next: None,
2414 gap: ()
2415 }]
2416 );
2417
2418 assert_matches!(
2419 linked_chunk.insert_items_at(Position(ChunkIdentifier(6), 0), ['u', 'v'],),
2420 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2421 );
2422 }
2423
2424 assert_eq!(linked_chunk.num_items(), 18);
2425
2426 Ok(())
2427 }
2428
2429 #[test]
2430 fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2431 use super::Update::*;
2432
2433 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2434
2435 let _ = linked_chunk.updates().unwrap().take();
2437
2438 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2439 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2440 assert_eq!(
2441 linked_chunk.updates().unwrap().take(),
2442 &[
2443 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2444 NewItemsChunk {
2445 previous: Some(ChunkIdentifier(0)),
2446 new: ChunkIdentifier(1),
2447 next: None,
2448 },
2449 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2450 ]
2451 );
2452
2453 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2455
2456 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2459
2460 assert_items_eq!(
2461 linked_chunk,
2462 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2463 );
2464 assert_eq!(linked_chunk.num_items(), 10);
2465 assert_eq!(
2466 linked_chunk.updates().unwrap().take(),
2467 &[
2468 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2469 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2470 NewItemsChunk {
2471 previous: Some(ChunkIdentifier(1)),
2472 new: ChunkIdentifier(2),
2473 next: None,
2474 },
2475 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2476 StartReattachItems,
2477 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2478 NewItemsChunk {
2479 previous: Some(ChunkIdentifier(2)),
2480 new: ChunkIdentifier(3),
2481 next: None,
2482 },
2483 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2484 EndReattachItems,
2485 ]
2486 );
2487
2488 Ok(())
2489 }
2490
2491 #[test]
2492 fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2493 use super::Update::*;
2494
2495 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2496
2497 let _ = linked_chunk.updates().unwrap().take();
2499
2500 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2501 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2502 assert_eq!(
2503 linked_chunk.updates().unwrap().take(),
2504 &[
2505 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2506 NewItemsChunk {
2507 previous: Some(ChunkIdentifier(0)),
2508 new: ChunkIdentifier(1),
2509 next: None,
2510 },
2511 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2512 ]
2513 );
2514
2515 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2517 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2518
2519 assert_items_eq!(
2520 linked_chunk,
2521 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'e', 'f']
2522 );
2523 assert_eq!(linked_chunk.num_items(), 10);
2524 assert_eq!(
2525 linked_chunk.updates().unwrap().take(),
2526 &[
2527 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2528 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2529 NewItemsChunk {
2530 previous: Some(ChunkIdentifier(0)),
2531 new: ChunkIdentifier(2),
2532 next: Some(ChunkIdentifier(1)),
2533 },
2534 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2535 StartReattachItems,
2536 PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2537 NewItemsChunk {
2538 previous: Some(ChunkIdentifier(2)),
2539 new: ChunkIdentifier(3),
2540 next: Some(ChunkIdentifier(1)),
2541 },
2542 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2543 EndReattachItems,
2544 ]
2545 );
2546
2547 Ok(())
2548 }
2549
2550 #[test]
2551 fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2552 use super::Update::*;
2553
2554 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2555
2556 let _ = linked_chunk.updates().unwrap().take();
2558
2559 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2560 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2561 assert_eq!(
2562 linked_chunk.updates().unwrap().take(),
2563 &[
2564 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2565 NewItemsChunk {
2566 previous: Some(ChunkIdentifier(0)),
2567 new: ChunkIdentifier(1),
2568 next: None,
2569 },
2570 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2571 NewItemsChunk {
2572 previous: Some(ChunkIdentifier(1)),
2573 new: ChunkIdentifier(2),
2574 next: None,
2575 },
2576 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2577 ]
2578 );
2579
2580 let pos_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2581 linked_chunk.insert_items_at(pos_d, ['r', 's'])?;
2582
2583 assert_items_eq!(
2584 linked_chunk,
2585 ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2586 );
2587 assert_eq!(linked_chunk.num_items(), 10);
2588 assert_eq!(
2589 linked_chunk.updates().unwrap().take(),
2590 &[
2591 DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2592 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2593 StartReattachItems,
2594 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2595 NewItemsChunk {
2596 previous: Some(ChunkIdentifier(1)),
2597 new: ChunkIdentifier(3),
2598 next: Some(ChunkIdentifier(2)),
2599 },
2600 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2601 EndReattachItems,
2602 ]
2603 );
2604
2605 Ok(())
2606 }
2607
2608 #[test]
2609 fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2610 use super::Update::*;
2611
2612 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2613
2614 let _ = linked_chunk.updates().unwrap().take();
2616
2617 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2618 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2619 assert_eq!(
2620 linked_chunk.updates().unwrap().take(),
2621 &[
2622 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2623 NewItemsChunk {
2624 previous: Some(ChunkIdentifier(0)),
2625 new: ChunkIdentifier(1),
2626 next: None,
2627 },
2628 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2629 ]
2630 );
2631
2632 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2634 let pos_after_e = Position(pos_e.chunk_identifier(), pos_e.index() + 1);
2635
2636 linked_chunk.insert_items_at(pos_after_e, ['p', 'q'])?;
2637 assert_items_eq!(
2638 linked_chunk,
2639 ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2640 );
2641 assert_eq!(
2642 linked_chunk.updates().unwrap().take(),
2643 &[
2644 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2645 NewItemsChunk {
2646 previous: Some(ChunkIdentifier(1)),
2647 new: ChunkIdentifier(2),
2648 next: None
2649 },
2650 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2651 ]
2652 );
2653 assert_eq!(linked_chunk.num_items(), 7);
2654
2655 Ok(())
2656 }
2657
2658 #[test]
2659 fn test_insert_items_at_errs() -> Result<(), Error> {
2660 use super::Update::*;
2661
2662 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2663
2664 let _ = linked_chunk.updates().unwrap().take();
2666
2667 linked_chunk.push_items_back(['a', 'b', 'c']);
2668 linked_chunk.push_gap_back(());
2669 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2670 assert_eq!(
2671 linked_chunk.updates().unwrap().take(),
2672 &[
2673 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2674 NewGapChunk {
2675 previous: Some(ChunkIdentifier(0)),
2676 new: ChunkIdentifier(1),
2677 next: None,
2678 gap: (),
2679 },
2680 ]
2681 );
2682
2683 {
2685 assert_matches!(
2686 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2687 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2688 );
2689 assert!(linked_chunk.updates().unwrap().take().is_empty());
2690 }
2691
2692 {
2694 assert_matches!(
2695 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2696 Err(Error::InvalidItemIndex { index: 128 })
2697 );
2698 assert!(linked_chunk.updates().unwrap().take().is_empty());
2699 }
2700
2701 {
2703 assert_matches!(
2704 linked_chunk.insert_items_at(Position(ChunkIdentifier(1), 0), ['u', 'v'],),
2705 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2706 );
2707 }
2708
2709 Ok(())
2710 }
2711
2712 #[test]
2713 fn test_remove_item_at() -> Result<(), Error> {
2714 use super::Update::*;
2715
2716 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2717
2718 let _ = linked_chunk.updates().unwrap().take();
2720
2721 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2722 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2723 assert_eq!(linked_chunk.num_items(), 11);
2724
2725 let _ = linked_chunk.updates().unwrap().take();
2727
2728 {
2731 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2732 let removed_item = linked_chunk.remove_item_at(position_of_f)?;
2733
2734 assert_eq!(removed_item, 'f');
2735 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2736 assert_eq!(linked_chunk.num_items(), 10);
2737
2738 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2739 let removed_item = linked_chunk.remove_item_at(position_of_e)?;
2740
2741 assert_eq!(removed_item, 'e');
2742 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2743 assert_eq!(linked_chunk.num_items(), 9);
2744
2745 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2746 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2747
2748 assert_eq!(removed_item, 'd');
2749 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2750 assert_eq!(linked_chunk.num_items(), 8);
2751
2752 assert_eq!(
2753 linked_chunk.updates().unwrap().take(),
2754 &[
2755 RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2756 RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2757 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2758 RemoveChunk(ChunkIdentifier(1)),
2759 ]
2760 );
2761 }
2762
2763 {
2766 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2767 let removed_item = linked_chunk.remove_item_at(first_position)?;
2768
2769 assert_eq!(removed_item, 'a');
2770 assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2771 assert_eq!(linked_chunk.num_items(), 7);
2772
2773 let removed_item = linked_chunk.remove_item_at(first_position)?;
2774
2775 assert_eq!(removed_item, 'b');
2776 assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2777 assert_eq!(linked_chunk.num_items(), 6);
2778
2779 let removed_item = linked_chunk.remove_item_at(first_position)?;
2780
2781 assert_eq!(removed_item, 'c');
2782 assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2783 assert_eq!(linked_chunk.num_items(), 5);
2784
2785 assert_eq!(
2786 linked_chunk.updates().unwrap().take(),
2787 &[
2788 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2789 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2790 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2791 ]
2792 );
2793 }
2794
2795 {
2798 let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2799 let removed_item = linked_chunk.remove_item_at(first_position)?;
2800
2801 assert_eq!(removed_item, 'g');
2802 assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2803 assert_eq!(linked_chunk.num_items(), 4);
2804
2805 let removed_item = linked_chunk.remove_item_at(first_position)?;
2806
2807 assert_eq!(removed_item, 'h');
2808 assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2809 assert_eq!(linked_chunk.num_items(), 3);
2810
2811 let removed_item = linked_chunk.remove_item_at(first_position)?;
2812
2813 assert_eq!(removed_item, 'i');
2814 assert_items_eq!(linked_chunk, [] ['j', 'k']);
2815 assert_eq!(linked_chunk.num_items(), 2);
2816
2817 assert_eq!(
2818 linked_chunk.updates().unwrap().take(),
2819 &[
2820 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2821 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2822 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2823 RemoveChunk(ChunkIdentifier(2)),
2824 ]
2825 );
2826 }
2827
2828 {
2831 let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2832 let removed_item = linked_chunk.remove_item_at(position_of_k)?;
2833
2834 assert_eq!(removed_item, 'k');
2835 #[rustfmt::skip]
2836 assert_items_eq!(linked_chunk, [] ['j']);
2837 assert_eq!(linked_chunk.num_items(), 1);
2838
2839 let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2840 let removed_item = linked_chunk.remove_item_at(position_of_j)?;
2841
2842 assert_eq!(removed_item, 'j');
2843 assert_items_eq!(linked_chunk, []);
2844 assert_eq!(linked_chunk.num_items(), 0);
2845
2846 assert_eq!(
2847 linked_chunk.updates().unwrap().take(),
2848 &[
2849 RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2850 RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2851 RemoveChunk(ChunkIdentifier(3)),
2852 ]
2853 );
2854 }
2855
2856 {
2858 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2859
2860 #[rustfmt::skip]
2861 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2862 assert_eq!(linked_chunk.num_items(), 4);
2863
2864 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2865 linked_chunk.insert_gap_at((), position_of_c)?;
2866
2867 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2868 assert_eq!(linked_chunk.num_items(), 4);
2869
2870 let _ = linked_chunk.updates().unwrap().take();
2872
2873 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2874 let removed_item = linked_chunk.remove_item_at(position_of_c)?;
2875
2876 assert_eq!(removed_item, 'c');
2877 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2878 assert_eq!(linked_chunk.num_items(), 3);
2879
2880 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2881 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2882
2883 assert_eq!(removed_item, 'd');
2884 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2885 assert_eq!(linked_chunk.num_items(), 2);
2886
2887 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2888 let removed_item = linked_chunk.remove_item_at(first_position)?;
2889
2890 assert_eq!(removed_item, 'a');
2891 assert_items_eq!(linked_chunk, ['b'] [-]);
2892 assert_eq!(linked_chunk.num_items(), 1);
2893
2894 let removed_item = linked_chunk.remove_item_at(first_position)?;
2895
2896 assert_eq!(removed_item, 'b');
2897 assert_items_eq!(linked_chunk, [] [-]);
2898 assert_eq!(linked_chunk.num_items(), 0);
2899
2900 assert_eq!(
2901 linked_chunk.updates().unwrap().take(),
2902 &[
2903 RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2904 RemoveChunk(ChunkIdentifier(6)),
2905 RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2906 RemoveChunk(ChunkIdentifier(4)),
2907 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2908 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2909 ]
2910 );
2911 }
2912
2913 Ok(())
2914 }
2915
2916 #[test]
2917 fn test_insert_gap_at() -> Result<(), Error> {
2918 use super::Update::*;
2919
2920 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2921
2922 let _ = linked_chunk.updates().unwrap().take();
2924
2925 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2926 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2927 assert_eq!(
2928 linked_chunk.updates().unwrap().take(),
2929 &[
2930 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2931 NewItemsChunk {
2932 previous: Some(ChunkIdentifier(0)),
2933 new: ChunkIdentifier(1),
2934 next: None
2935 },
2936 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2937 ]
2938 );
2939
2940 {
2942 let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
2943 linked_chunk.insert_gap_at((), position_of_b)?;
2944
2945 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2946 assert_eq!(
2947 linked_chunk.updates().unwrap().take(),
2948 &[
2949 DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
2950 NewGapChunk {
2951 previous: Some(ChunkIdentifier(0)),
2952 new: ChunkIdentifier(2),
2953 next: Some(ChunkIdentifier(1)),
2954 gap: (),
2955 },
2956 StartReattachItems,
2957 NewItemsChunk {
2958 previous: Some(ChunkIdentifier(2)),
2959 new: ChunkIdentifier(3),
2960 next: Some(ChunkIdentifier(1)),
2961 },
2962 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
2963 EndReattachItems,
2964 ]
2965 );
2966 }
2967
2968 {
2971 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2972 linked_chunk.insert_gap_at((), position_of_a)?;
2973
2974 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2977 assert_eq!(
2978 linked_chunk.updates().unwrap().take(),
2979 &[NewGapChunk {
2980 previous: None,
2981 new: ChunkIdentifier(4),
2982 next: Some(ChunkIdentifier(0)),
2983 gap: (),
2984 },]
2985 );
2986 }
2987
2988 {
2991 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2992 linked_chunk.insert_gap_at((), position_of_d)?;
2993
2994 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
2998 assert_eq!(
2999 linked_chunk.updates().unwrap().take(),
3000 &[NewGapChunk {
3001 previous: Some(ChunkIdentifier(3)),
3002 new: ChunkIdentifier(5),
3003 next: Some(ChunkIdentifier(1)),
3004 gap: (),
3005 }]
3006 );
3007 }
3008
3009 {
3011 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3013 let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
3014
3015 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
3016
3017 assert_eq!(
3018 linked_chunk.updates().unwrap().take(),
3019 &[
3020 NewItemsChunk {
3021 previous: Some(ChunkIdentifier(5)),
3022 new: ChunkIdentifier(6),
3023 next: Some(ChunkIdentifier(1)),
3024 },
3025 RemoveChunk(ChunkIdentifier(5)),
3026 ]
3027 );
3028
3029 linked_chunk.insert_gap_at((), position)?;
3030
3031 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
3032 assert_eq!(
3033 linked_chunk.updates().unwrap().take(),
3034 &[NewGapChunk {
3035 previous: Some(ChunkIdentifier(3)),
3036 new: ChunkIdentifier(7),
3037 next: Some(ChunkIdentifier(6)),
3038 gap: (),
3039 }]
3040 );
3041 }
3042
3043 {
3045 assert_matches!(
3046 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
3047 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
3048 );
3049 assert!(linked_chunk.updates().unwrap().take().is_empty());
3050 }
3051
3052 {
3054 assert_matches!(
3055 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
3056 Err(Error::InvalidItemIndex { index: 128 })
3057 );
3058 assert!(linked_chunk.updates().unwrap().take().is_empty());
3059 }
3060
3061 {
3063 let position_of_a_gap = Position(ChunkIdentifier(2), 0);
3066 assert_matches!(
3067 linked_chunk.insert_gap_at((), position_of_a_gap),
3068 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
3069 );
3070 assert!(linked_chunk.updates().unwrap().take().is_empty());
3071 }
3072
3073 assert_eq!(linked_chunk.num_items(), 6);
3074
3075 Ok(())
3076 }
3077
3078 #[test]
3079 fn test_replace_gap_at_middle() -> Result<(), Error> {
3080 use super::Update::*;
3081
3082 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3083
3084 let _ = linked_chunk.updates().unwrap().take();
3086
3087 linked_chunk.push_items_back(['a', 'b']);
3088 linked_chunk.push_gap_back(());
3089 linked_chunk.push_items_back(['l', 'm']);
3090 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
3091 assert_eq!(
3092 linked_chunk.updates().unwrap().take(),
3093 &[
3094 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3095 NewGapChunk {
3096 previous: Some(ChunkIdentifier(0)),
3097 new: ChunkIdentifier(1),
3098 next: None,
3099 gap: (),
3100 },
3101 NewItemsChunk {
3102 previous: Some(ChunkIdentifier(1)),
3103 new: ChunkIdentifier(2),
3104 next: None,
3105 },
3106 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
3107 ]
3108 );
3109
3110 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3112 assert_eq!(gap_identifier, ChunkIdentifier(1));
3113
3114 let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
3115 assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
3116 assert_items_eq!(
3117 linked_chunk,
3118 ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
3119 );
3120 assert_eq!(
3121 linked_chunk.updates().unwrap().take(),
3122 &[
3123 NewItemsChunk {
3124 previous: Some(ChunkIdentifier(1)),
3125 new: ChunkIdentifier(3),
3126 next: Some(ChunkIdentifier(2)),
3127 },
3128 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
3129 NewItemsChunk {
3130 previous: Some(ChunkIdentifier(3)),
3131 new: ChunkIdentifier(4),
3132 next: Some(ChunkIdentifier(2)),
3133 },
3134 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
3135 RemoveChunk(ChunkIdentifier(1)),
3136 ]
3137 );
3138
3139 assert_eq!(linked_chunk.num_items(), 9);
3140
3141 Ok(())
3142 }
3143
3144 #[test]
3145 fn test_replace_gap_at_end() -> Result<(), Error> {
3146 use super::Update::*;
3147
3148 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3149
3150 let _ = linked_chunk.updates().unwrap().take();
3152
3153 linked_chunk.push_items_back(['a', 'b']);
3154 linked_chunk.push_gap_back(());
3155 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3156 assert_eq!(
3157 linked_chunk.updates().unwrap().take(),
3158 &[
3159 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3160 NewGapChunk {
3161 previous: Some(ChunkIdentifier(0)),
3162 new: ChunkIdentifier(1),
3163 next: None,
3164 gap: (),
3165 },
3166 ]
3167 );
3168
3169 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3171 assert_eq!(gap_identifier, ChunkIdentifier(1));
3172
3173 let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3174 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3175 assert_items_eq!(
3176 linked_chunk,
3177 ['a', 'b'] ['w', 'x', 'y'] ['z']
3178 );
3179 assert_eq!(
3180 linked_chunk.updates().unwrap().take(),
3181 &[
3182 NewItemsChunk {
3183 previous: Some(ChunkIdentifier(1)),
3184 new: ChunkIdentifier(2),
3185 next: None,
3186 },
3187 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3188 NewItemsChunk {
3189 previous: Some(ChunkIdentifier(2)),
3190 new: ChunkIdentifier(3),
3191 next: None,
3192 },
3193 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3194 RemoveChunk(ChunkIdentifier(1)),
3195 ]
3196 );
3197
3198 assert_eq!(linked_chunk.num_items(), 6);
3199
3200 Ok(())
3201 }
3202
3203 #[test]
3204 fn test_replace_gap_at_beginning() -> Result<(), Error> {
3205 use super::Update::*;
3206
3207 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3208
3209 let _ = linked_chunk.updates().unwrap().take();
3211
3212 linked_chunk.push_items_back(['a', 'b']);
3213 assert_items_eq!(linked_chunk, ['a', 'b']);
3214 assert_eq!(
3215 linked_chunk.updates().unwrap().take(),
3216 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },]
3217 );
3218
3219 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3221 linked_chunk.insert_gap_at((), position_of_a).unwrap();
3222 assert_items_eq!(
3223 linked_chunk,
3224 [-] ['a', 'b']
3225 );
3226 assert_eq!(
3227 linked_chunk.updates().unwrap().take(),
3228 &[NewGapChunk {
3229 previous: None,
3230 new: ChunkIdentifier(1),
3231 next: Some(ChunkIdentifier(0)),
3232 gap: (),
3233 }]
3234 );
3235
3236 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3237 assert_eq!(gap_identifier, ChunkIdentifier(1));
3238
3239 let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3240 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3241 assert_items_eq!(
3242 linked_chunk,
3243 ['x'] ['a', 'b']
3244 );
3245 assert_eq!(
3246 linked_chunk.updates().unwrap().take(),
3247 &[
3248 NewItemsChunk {
3249 previous: Some(ChunkIdentifier(1)),
3250 new: ChunkIdentifier(2),
3251 next: Some(ChunkIdentifier(0)),
3252 },
3253 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3254 RemoveChunk(ChunkIdentifier(1)),
3255 ]
3256 );
3257
3258 assert_eq!(linked_chunk.num_items(), 3);
3259
3260 Ok(())
3261 }
3262
3263 #[test]
3264 fn test_remove_empty_chunk_at() -> Result<(), Error> {
3265 use super::Update::*;
3266
3267 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3268
3269 let _ = linked_chunk.updates().unwrap().take();
3271
3272 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3273 linked_chunk.push_items_back(['a', 'b']);
3274 linked_chunk.push_gap_back(());
3275 linked_chunk.push_items_back(['l', 'm']);
3276 linked_chunk.push_gap_back(());
3277 assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3278 assert_eq!(
3279 linked_chunk.updates().unwrap().take(),
3280 &[
3281 NewGapChunk {
3282 previous: None,
3283 new: ChunkIdentifier(1),
3284 next: Some(ChunkIdentifier(0)),
3285 gap: (),
3286 },
3287 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3288 NewGapChunk {
3289 previous: Some(ChunkIdentifier(0)),
3290 new: ChunkIdentifier(2),
3291 next: None,
3292 gap: (),
3293 },
3294 NewItemsChunk {
3295 previous: Some(ChunkIdentifier(2)),
3296 new: ChunkIdentifier(3),
3297 next: None,
3298 },
3299 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3300 NewGapChunk {
3301 previous: Some(ChunkIdentifier(3)),
3302 new: ChunkIdentifier(4),
3303 next: None,
3304 gap: (),
3305 },
3306 ]
3307 );
3308
3309 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3311 assert_matches!(err, Error::RemovingNonEmptyItemsChunk { .. });
3312
3313 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(42)).unwrap_err();
3315 assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3316
3317 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(2)).unwrap();
3319 let next = maybe_next.unwrap();
3320 assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3322 assert_eq!(next.index(), 0);
3323 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3324 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3325
3326 let next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(4)).unwrap();
3328 assert!(next.is_none());
3330 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3331 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3332
3333 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(1)).unwrap();
3335 let next = maybe_next.unwrap();
3336 assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3337 assert_eq!(next.index(), 0);
3338 assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3339 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3340
3341 Ok(())
3342 }
3343
3344 #[test]
3345 fn test_remove_empty_last_chunk() {
3346 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3347
3348 let _ = linked_chunk.updates().unwrap().take();
3350
3351 assert_items_eq!(linked_chunk, []);
3352 assert!(linked_chunk.updates().unwrap().take().is_empty());
3353
3354 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3356 assert_matches!(err, Error::RemovingLastChunk);
3357 }
3358
3359 #[test]
3360 fn test_chunk_item_positions() {
3361 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3362 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3363 linked_chunk.push_gap_back(());
3364 linked_chunk.push_items_back(['f']);
3365
3366 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3367
3368 let mut iterator = linked_chunk.chunks();
3369
3370 {
3372 let chunk = iterator.next().unwrap();
3373 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3374 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3375 }
3376
3377 {
3379 let chunk = iterator.next().unwrap();
3380 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3381 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3382 }
3383
3384 {
3386 let chunk = iterator.next().unwrap();
3387 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3388 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3389 }
3390
3391 {
3393 let chunk = iterator.next().unwrap();
3394 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3395 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3396 }
3397 }
3398
3399 #[test]
3400 fn test_is_first_and_last_chunk() {
3401 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3402
3403 let mut chunks = linked_chunk.chunks().peekable();
3404 assert!(chunks.peek().unwrap().is_first_chunk());
3405 assert!(chunks.next().unwrap().is_last_chunk());
3406 assert!(chunks.next().is_none());
3407
3408 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3409
3410 let mut chunks = linked_chunk.chunks().peekable();
3411 assert!(chunks.next().unwrap().is_first_chunk());
3412 assert!(chunks.peek().unwrap().is_first_chunk().not());
3413 assert!(chunks.next().unwrap().is_last_chunk().not());
3414 assert!(chunks.next().unwrap().is_last_chunk());
3415 assert!(chunks.next().is_none());
3416 }
3417
3418 #[test]
3423 fn test_clear() {
3424 let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3425
3426 let item = Arc::new('a');
3427 let gap = Arc::new(());
3428
3429 linked_chunk.push_items_back([
3430 item.clone(),
3431 item.clone(),
3432 item.clone(),
3433 item.clone(),
3434 item.clone(),
3435 ]);
3436 linked_chunk.push_gap_back(gap.clone());
3437 linked_chunk.push_items_back([item.clone()]);
3438
3439 assert_eq!(Arc::strong_count(&item), 7);
3440 assert_eq!(Arc::strong_count(&gap), 2);
3441 assert_eq!(linked_chunk.num_items(), 6);
3442 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3443
3444 linked_chunk.clear();
3446
3447 assert_eq!(Arc::strong_count(&item), 1);
3448 assert_eq!(Arc::strong_count(&gap), 1);
3449 assert_eq!(linked_chunk.num_items(), 0);
3450 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3451 }
3452
3453 #[test]
3454 fn test_clear_emit_an_update_clear() {
3455 use super::Update::*;
3456
3457 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3458
3459 assert_eq!(
3460 linked_chunk.updates().unwrap().take(),
3461 &[NewItemsChunk {
3462 previous: None,
3463 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3464 next: None
3465 }]
3466 );
3467
3468 linked_chunk.clear();
3469
3470 assert_eq!(
3471 linked_chunk.updates().unwrap().take(),
3472 &[
3473 Clear,
3474 NewItemsChunk {
3475 previous: None,
3476 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3477 next: None
3478 }
3479 ]
3480 );
3481 }
3482
3483 #[test]
3484 fn test_replace_item() {
3485 use super::Update::*;
3486
3487 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3488
3489 linked_chunk.push_items_back(['a', 'b', 'c']);
3490 linked_chunk.push_gap_back(());
3491 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3493
3494 let _ = linked_chunk.updates().unwrap().take();
3496
3497 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3499 assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3500
3501 assert_eq!(
3502 linked_chunk.updates().unwrap().take(),
3503 &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3504 );
3505
3506 assert_matches!(
3508 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3509 Err(Error::InvalidItemIndex { index: 3 })
3510 );
3511
3512 assert_matches!(
3514 linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3515 Err(Error::ChunkIsAGap { .. })
3516 );
3517 }
3518
3519 #[test]
3520 fn test_lazy_previous() {
3521 use std::marker::PhantomData;
3522
3523 use super::{Ends, ObservableUpdates, Update::*};
3524
3525 let first_chunk_identifier = ChunkIdentifier(0);
3527 let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3528 unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3529
3530 let mut linked_chunk = LinkedChunk::<3, char, ()> {
3531 links: Ends { first: first_loaded_chunk, last: None },
3532 chunk_identifier_generator:
3533 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3534 updates: Some(ObservableUpdates::new()),
3535 marker: PhantomData,
3536 };
3537
3538 {
3541 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3542
3543 assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3544
3545 {
3547 let mut chunks = linked_chunk.chunks();
3548
3549 assert_matches!(chunks.next(), Some(chunk) => {
3550 assert_eq!(chunk.identifier(), 1);
3551 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3552 });
3553 assert_matches!(chunks.next(), Some(chunk) => {
3554 assert_eq!(chunk.identifier(), 2);
3555 assert!(chunk.lazy_previous.is_none());
3556 });
3557 assert!(chunks.next().is_none());
3558 }
3559
3560 assert_eq!(
3562 linked_chunk.updates().unwrap().take(),
3563 &[
3564 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3565 NewItemsChunk {
3566 previous: Some(ChunkIdentifier(1)),
3567 new: ChunkIdentifier(2),
3568 next: None,
3569 },
3570 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3571 ]
3572 );
3573 }
3574
3575 {
3577 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3578
3579 assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3580
3581 {
3583 let mut chunks = linked_chunk.chunks();
3584
3585 assert_matches!(chunks.next(), Some(chunk) => {
3586 assert_eq!(chunk.identifier(), 3);
3587 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3589 });
3590 assert_matches!(chunks.next(), Some(chunk) => {
3591 assert_eq!(chunk.identifier(), 1);
3592 assert!(chunk.lazy_previous.is_none());
3594 });
3595 assert_matches!(chunks.next(), Some(chunk) => {
3596 assert_eq!(chunk.identifier(), 2);
3597 assert!(chunk.lazy_previous.is_none());
3598 });
3599 assert!(chunks.next().is_none());
3600 }
3601
3602 assert_eq!(
3604 linked_chunk.updates().unwrap().take(),
3605 &[NewGapChunk {
3606 previous: Some(ChunkIdentifier(0)),
3608 new: ChunkIdentifier(3),
3609 next: Some(ChunkIdentifier(1)),
3610 gap: ()
3611 }]
3612 );
3613 }
3614
3615 {
3617 linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3618
3619 assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3620
3621 {
3623 let mut chunks = linked_chunk.chunks();
3624
3625 assert_matches!(chunks.next(), Some(chunk) => {
3626 assert_eq!(chunk.identifier(), 4);
3627 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3629 });
3630 assert_matches!(chunks.next(), Some(chunk) => {
3631 assert_eq!(chunk.identifier(), 5);
3632 assert!(chunk.lazy_previous.is_none());
3633 });
3634 assert_matches!(chunks.next(), Some(chunk) => {
3635 assert_eq!(chunk.identifier(), 1);
3636 assert!(chunk.lazy_previous.is_none());
3637 });
3638 assert_matches!(chunks.next(), Some(chunk) => {
3639 assert_eq!(chunk.identifier(), 2);
3640 assert!(chunk.lazy_previous.is_none());
3641 });
3642 assert!(chunks.next().is_none());
3643 }
3644
3645 assert_eq!(
3647 linked_chunk.updates().unwrap().take(),
3648 &[
3649 NewItemsChunk {
3651 previous: Some(ChunkIdentifier(3)),
3652 new: ChunkIdentifier(4),
3653 next: Some(ChunkIdentifier(1)),
3654 },
3655 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3657 NewItemsChunk {
3659 previous: Some(ChunkIdentifier(4)),
3660 new: ChunkIdentifier(5),
3661 next: Some(ChunkIdentifier(1)),
3662 },
3663 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3665 RemoveChunk(ChunkIdentifier(3)),
3667 ]
3668 );
3669 }
3670
3671 {
3675 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3676
3677 assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3678
3679 {
3681 let mut chunks = linked_chunk.chunks();
3682
3683 assert_matches!(chunks.next(), Some(chunk) => {
3684 assert_eq!(chunk.identifier(), 6);
3685 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3687 });
3688 assert_matches!(chunks.next(), Some(chunk) => {
3689 assert_eq!(chunk.identifier(), 4);
3690 assert!(chunk.lazy_previous.is_none());
3692 });
3693 assert_matches!(chunks.next(), Some(chunk) => {
3694 assert_eq!(chunk.identifier(), 5);
3695 assert!(chunk.lazy_previous.is_none());
3696 });
3697 assert_matches!(chunks.next(), Some(chunk) => {
3698 assert_eq!(chunk.identifier(), 1);
3699 assert!(chunk.lazy_previous.is_none());
3700 });
3701 assert_matches!(chunks.next(), Some(chunk) => {
3702 assert_eq!(chunk.identifier(), 2);
3703 assert!(chunk.lazy_previous.is_none());
3704 });
3705 assert!(chunks.next().is_none());
3706 }
3707
3708 assert_eq!(
3710 linked_chunk.updates().unwrap().take(),
3711 &[NewGapChunk {
3712 previous: Some(ChunkIdentifier(0)),
3714 new: ChunkIdentifier(6),
3715 next: Some(ChunkIdentifier(4)),
3716 gap: ()
3717 }]
3718 );
3719 }
3720 }
3721}