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};
110use serde::{Deserialize, Serialize};
111pub use updates::*;
112
113#[derive(Debug, Clone, Copy, PartialEq)]
115pub enum LinkedChunkId<'a> {
116 Room(&'a RoomId),
117 Thread(&'a RoomId, &'a EventId),
118}
119
120impl Display for LinkedChunkId<'_> {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 match self {
123 Self::Room(room_id) => write!(f, "{room_id}"),
124 Self::Thread(room_id, thread_root) => {
125 write!(f, "{room_id}:thread:{thread_root}")
126 }
127 }
128 }
129}
130
131impl LinkedChunkId<'_> {
132 pub fn storage_key(&self) -> impl '_ + AsRef<[u8]> {
133 match self {
134 LinkedChunkId::Room(room_id) => room_id.to_string(),
135 LinkedChunkId::Thread(room_id, event_id) => format!("t:{room_id}:{event_id}"),
136 }
137 }
138
139 pub fn to_owned(&self) -> OwnedLinkedChunkId {
140 match self {
141 LinkedChunkId::Room(room_id) => OwnedLinkedChunkId::Room((*room_id).to_owned()),
142 LinkedChunkId::Thread(room_id, event_id) => {
143 OwnedLinkedChunkId::Thread((*room_id).to_owned(), (*event_id).to_owned())
144 }
145 }
146 }
147}
148
149impl<'a> From<&'a OwnedLinkedChunkId> for LinkedChunkId<'a> {
150 fn from(value: &'a OwnedLinkedChunkId) -> Self {
151 value.as_ref()
152 }
153}
154
155impl PartialEq<&OwnedLinkedChunkId> for LinkedChunkId<'_> {
156 fn eq(&self, other: &&OwnedLinkedChunkId) -> bool {
157 match (self, other) {
158 (LinkedChunkId::Room(a), OwnedLinkedChunkId::Room(b)) => *a == b,
159 (LinkedChunkId::Thread(r, ev), OwnedLinkedChunkId::Thread(r2, ev2)) => {
160 r == r2 && ev == ev2
161 }
162 (LinkedChunkId::Room(..), OwnedLinkedChunkId::Thread(..))
163 | (LinkedChunkId::Thread(..), OwnedLinkedChunkId::Room(..)) => false,
164 }
165 }
166}
167
168impl PartialEq<LinkedChunkId<'_>> for OwnedLinkedChunkId {
169 fn eq(&self, other: &LinkedChunkId<'_>) -> bool {
170 other.eq(&self)
171 }
172}
173
174#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
176pub enum OwnedLinkedChunkId {
177 Room(OwnedRoomId),
178 Thread(OwnedRoomId, OwnedEventId),
179}
180
181impl Display for OwnedLinkedChunkId {
182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 self.as_ref().fmt(f)
184 }
185}
186
187impl OwnedLinkedChunkId {
188 pub fn as_ref(&self) -> LinkedChunkId<'_> {
189 match self {
190 OwnedLinkedChunkId::Room(room_id) => LinkedChunkId::Room(room_id.as_ref()),
191 OwnedLinkedChunkId::Thread(room_id, event_id) => {
192 LinkedChunkId::Thread(room_id.as_ref(), event_id.as_ref())
193 }
194 }
195 }
196
197 pub fn room_id(&self) -> &RoomId {
198 match self {
199 OwnedLinkedChunkId::Room(room_id) => room_id,
200 OwnedLinkedChunkId::Thread(room_id, ..) => room_id,
201 }
202 }
203}
204
205impl From<LinkedChunkId<'_>> for OwnedLinkedChunkId {
206 fn from(value: LinkedChunkId<'_>) -> Self {
207 value.to_owned()
208 }
209}
210
211#[derive(thiserror::Error, Debug)]
213pub enum Error {
214 #[error("The chunk identifier is invalid: `{identifier:?}`")]
216 InvalidChunkIdentifier {
217 identifier: ChunkIdentifier,
219 },
220
221 #[error("The chunk is a gap: `{identifier:?}`")]
223 ChunkIsAGap {
224 identifier: ChunkIdentifier,
226 },
227
228 #[error("The chunk is an item: `{identifier:?}`")]
230 ChunkIsItems {
231 identifier: ChunkIdentifier,
233 },
234
235 #[error("The chunk is a non-empty item chunk: `{identifier:?}`")]
237 RemovingNonEmptyItemsChunk {
238 identifier: ChunkIdentifier,
240 },
241
242 #[error("Trying to remove the only chunk, but a linked chunk can't be empty")]
245 RemovingLastChunk,
246
247 #[error("The item index is invalid: `{index}`")]
249 InvalidItemIndex {
250 index: usize,
252 },
253}
254
255struct Ends<const CHUNK_CAPACITY: usize, Item, Gap> {
260 first: NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>,
262 last: Option<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
264}
265
266impl<const CAP: usize, Item, Gap> Ends<CAP, Item, Gap> {
267 fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
269 unsafe { self.first.as_ref() }
270 }
271
272 fn first_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
274 unsafe { self.first.as_mut() }
275 }
276
277 fn latest_chunk(&self) -> &Chunk<CAP, Item, Gap> {
279 unsafe { self.last.unwrap_or(self.first).as_ref() }
280 }
281
282 fn latest_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
284 unsafe { self.last.as_mut().unwrap_or(&mut self.first).as_mut() }
285 }
286
287 fn chunk(&self, identifier: ChunkIdentifier) -> Option<&Chunk<CAP, Item, Gap>> {
289 let mut chunk = self.latest_chunk();
290
291 loop {
292 if chunk.identifier() == identifier {
293 return Some(chunk);
294 }
295
296 chunk = chunk.previous()?;
297 }
298 }
299
300 fn chunk_mut(&mut self, identifier: ChunkIdentifier) -> Option<&mut Chunk<CAP, Item, Gap>> {
302 let mut chunk = self.latest_chunk_mut();
303
304 loop {
305 if chunk.identifier() == identifier {
306 return Some(chunk);
307 }
308
309 chunk = chunk.previous_mut()?;
310 }
311 }
312
313 unsafe fn clear(&mut self) {
320 let mut current_chunk_ptr = self.last.or(Some(self.first));
323
324 while let Some(chunk_ptr) = current_chunk_ptr {
326 let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
328
329 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
331
332 current_chunk_ptr = previous_ptr;
334 }
335
336 self.first = NonNull::dangling();
338 self.last = None;
339 }
340
341 fn replace_with(&mut self, first_chunk: NonNull<Chunk<CAP, Item, Gap>>) {
344 unsafe {
346 self.clear();
347 }
348
349 self.first = first_chunk;
351 }
352
353 fn reset(&mut self) {
358 self.replace_with(Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER));
359 }
360}
361
362pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
369 links: Ends<CHUNK_CAPACITY, Item, Gap>,
371
372 chunk_identifier_generator: ChunkIdentifierGenerator,
374
375 updates: Option<ObservableUpdates<Item, Gap>>,
379
380 marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
382}
383
384impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
385 fn default() -> Self {
386 Self::new()
387 }
388}
389
390impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
391 pub fn new() -> Self {
393 Self {
394 links: Ends {
395 first: Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER),
396 last: None,
397 },
398 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
399 updates: None,
400 marker: PhantomData,
401 }
402 }
403
404 pub fn new_with_update_history() -> Self {
410 let first_chunk_identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
411
412 let mut updates = ObservableUpdates::new();
413 updates.push(Update::NewItemsChunk {
414 previous: None,
415 new: first_chunk_identifier,
416 next: None,
417 });
418
419 Self {
420 links: Ends { first: Chunk::new_items_leaked(first_chunk_identifier), last: None },
421 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
422 updates: Some(updates),
423 marker: PhantomData,
424 }
425 }
426
427 pub fn clear(&mut self) {
429 self.links.reset();
431
432 self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
434
435 if let Some(updates) = self.updates.as_mut() {
437 updates.clear_pending();
440 updates.push(Update::Clear);
441 updates.push(Update::NewItemsChunk {
442 previous: None,
443 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
444 next: None,
445 })
446 }
447 }
448
449 pub fn push_items_back<I>(&mut self, items: I)
455 where
456 Item: Clone,
457 Gap: Clone,
458 I: IntoIterator<Item = Item>,
459 I::IntoIter: ExactSizeIterator,
460 {
461 let items = items.into_iter();
462
463 let last_chunk = self.links.latest_chunk_mut();
464
465 let last_chunk =
467 last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
468
469 debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
470
471 if !last_chunk.is_first_chunk() {
475 self.links.last = Some(last_chunk.as_ptr());
478 }
479 }
480
481 pub fn push_gap_back(&mut self, content: Gap)
484 where
485 Item: Clone,
486 Gap: Clone,
487 {
488 let last_chunk = self.links.latest_chunk_mut();
489 last_chunk.insert_next(
490 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
491 &mut self.updates,
492 );
493
494 self.links.last = last_chunk.next;
495 }
496
497 pub fn insert_items_at<I>(&mut self, position: Position, items: I) -> Result<(), Error>
502 where
503 Item: Clone,
504 Gap: Clone,
505 I: IntoIterator<Item = Item>,
506 I::IntoIter: ExactSizeIterator,
507 {
508 let chunk_identifier = position.chunk_identifier();
509 let item_index = position.index();
510
511 let chunk = self
512 .links
513 .chunk_mut(chunk_identifier)
514 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
515
516 let chunk = match &mut chunk.content {
517 ChunkContent::Gap(..) => {
518 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
519 }
520
521 ChunkContent::Items(current_items) => {
522 let current_items_length = current_items.len();
523
524 if item_index > current_items_length {
525 return Err(Error::InvalidItemIndex { index: item_index });
526 }
527
528 let items = items.into_iter();
530
531 if item_index == current_items_length {
533 chunk
534 .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
536 }
537 else {
539 if let Some(updates) = self.updates.as_mut() {
540 updates.push(Update::DetachLastItems {
541 at: Position(chunk_identifier, item_index),
542 });
543 }
544
545 let detached_items = current_items.split_off(item_index);
547
548 let chunk = chunk
549 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
551
552 if let Some(updates) = self.updates.as_mut() {
553 updates.push(Update::StartReattachItems);
554 }
555
556 let chunk = chunk
557 .push_items(
559 detached_items.into_iter(),
560 &self.chunk_identifier_generator,
561 &mut self.updates,
562 );
563
564 if let Some(updates) = self.updates.as_mut() {
565 updates.push(Update::EndReattachItems);
566 }
567
568 chunk
569 }
570 }
571 };
572
573 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
576 self.links.last = Some(chunk.as_ptr());
579 }
580
581 Ok(())
582 }
583
584 pub fn remove_item_at(&mut self, position: Position) -> Result<Item, Error> {
592 let chunk_identifier = position.chunk_identifier();
593 let item_index = position.index();
594
595 let mut chunk_ptr = None;
596 let removed_item;
597
598 {
599 let chunk = self
600 .links
601 .chunk_mut(chunk_identifier)
602 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
603
604 let current_items = match &mut chunk.content {
605 ChunkContent::Gap(..) => {
606 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
607 }
608 ChunkContent::Items(current_items) => current_items,
609 };
610
611 if item_index >= current_items.len() {
612 return Err(Error::InvalidItemIndex { index: item_index });
613 }
614
615 removed_item = current_items.remove(item_index);
616
617 if let Some(updates) = self.updates.as_mut() {
618 updates.push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
619 }
620
621 if current_items.is_empty() && !chunk.is_first_chunk() {
623 chunk.unlink(self.updates.as_mut());
625
626 chunk_ptr = Some(chunk.as_ptr());
627
628 if chunk.is_last_chunk() {
631 self.links.last = chunk.previous;
632 }
633 }
634
635 }
637
638 if let Some(chunk_ptr) = chunk_ptr {
639 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
646 }
647
648 Ok(removed_item)
649 }
650
651 pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
656 where
657 Item: Clone,
658 {
659 let chunk_identifier = position.chunk_identifier();
660 let item_index = position.index();
661
662 let chunk = self
663 .links
664 .chunk_mut(chunk_identifier)
665 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
666
667 match &mut chunk.content {
668 ChunkContent::Gap(..) => {
669 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
670 }
671
672 ChunkContent::Items(current_items) => {
673 if item_index >= current_items.len() {
674 return Err(Error::InvalidItemIndex { index: item_index });
675 }
676
677 if let Some(updates) = self.updates.as_mut() {
679 updates.push(Update::ReplaceItem {
680 at: Position(chunk_identifier, item_index),
681 item: item.clone(),
682 });
683 }
684
685 current_items[item_index] = item;
686 }
687 }
688
689 Ok(())
690 }
691
692 pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
697 where
698 Item: Clone,
699 Gap: Clone,
700 {
701 let chunk_identifier = position.chunk_identifier();
702 let item_index = position.index();
703
704 let chunk = self
705 .links
706 .chunk_mut(chunk_identifier)
707 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
708
709 let chunk = match &mut chunk.content {
710 ChunkContent::Gap(..) => {
711 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
712 }
713
714 ChunkContent::Items(current_items) => {
715 if item_index == 0 {
719 let chunk_was_first = chunk.is_first_chunk();
720 let chunk_was_last = chunk.is_last_chunk();
721
722 let new_chunk = chunk.insert_before(
723 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
724 self.updates.as_mut(),
725 );
726
727 let new_chunk_ptr = new_chunk.as_ptr();
728 let chunk_ptr = chunk.as_ptr();
729
730 if chunk_was_first {
735 self.links.first = new_chunk_ptr;
736
737 if chunk_was_last {
739 self.links.last = Some(chunk_ptr);
740 }
741 }
742
743 return Ok(());
744 }
745
746 let current_items_length = current_items.len();
747
748 if item_index >= current_items_length {
749 return Err(Error::InvalidItemIndex { index: item_index });
750 }
751
752 if let Some(updates) = self.updates.as_mut() {
753 updates.push(Update::DetachLastItems {
754 at: Position(chunk_identifier, item_index),
755 });
756 }
757
758 let detached_items = current_items.split_off(item_index);
760
761 let chunk = chunk
762 .insert_next(
764 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
765 &mut self.updates,
766 );
767
768 if let Some(updates) = self.updates.as_mut() {
769 updates.push(Update::StartReattachItems);
770 }
771
772 let chunk = chunk
773 .insert_next(
775 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
776 &mut self.updates,
777 )
778 .push_items(
780 detached_items.into_iter(),
781 &self.chunk_identifier_generator,
782 &mut self.updates,
783 );
784
785 if let Some(updates) = self.updates.as_mut() {
786 updates.push(Update::EndReattachItems);
787 }
788
789 chunk
790 }
791 };
792
793 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
796 self.links.last = Some(chunk.as_ptr());
799 }
800
801 Ok(())
802 }
803
804 pub fn remove_empty_chunk_at(
813 &mut self,
814 chunk_identifier: ChunkIdentifier,
815 ) -> Result<Option<Position>, Error> {
816 if self.links.first_chunk().is_last_chunk() {
818 return Err(Error::RemovingLastChunk);
819 }
820
821 let chunk = self
822 .links
823 .chunk_mut(chunk_identifier)
824 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
825
826 if chunk.num_items() > 0 {
827 return Err(Error::RemovingNonEmptyItemsChunk { identifier: chunk_identifier });
828 }
829
830 let chunk_was_first = chunk.is_first_chunk();
831 let chunk_was_last = chunk.is_last_chunk();
832 let next_ptr = chunk.next;
833 let previous_ptr = chunk.previous;
834 let position_of_next = chunk.next().map(|next| next.first_position());
835
836 chunk.unlink(self.updates.as_mut());
837
838 let chunk_ptr = chunk.as_ptr();
839
840 if chunk_was_first {
842 if let Some(next_ptr) = next_ptr {
844 self.links.first = next_ptr;
845 }
846 }
847
848 if chunk_was_last {
849 self.links.last = previous_ptr;
850 }
851
852 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
855
856 Ok(position_of_next)
858 }
859
860 pub fn replace_gap_at<I>(
868 &mut self,
869 items: I,
870 chunk_identifier: ChunkIdentifier,
871 ) -> Result<&Chunk<CAP, Item, Gap>, Error>
872 where
873 Item: Clone,
874 Gap: Clone,
875 I: IntoIterator<Item = Item>,
876 I::IntoIter: ExactSizeIterator,
877 {
878 let chunk_ptr;
879 let new_chunk_ptr;
880
881 {
882 let chunk = self
883 .links
884 .chunk_mut(chunk_identifier)
885 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
886
887 if chunk.is_items() {
888 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
889 }
890
891 let chunk_was_first = chunk.is_first_chunk();
892
893 let maybe_last_chunk_ptr = {
894 let items = items.into_iter();
895
896 let last_inserted_chunk = chunk
897 .insert_next(
899 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
900 &mut self.updates,
901 )
902 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
904
905 last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
906 };
907
908 new_chunk_ptr = chunk
909 .next
910 .unwrap();
912
913 chunk.unlink(self.updates.as_mut());
915
916 chunk_ptr = chunk.as_ptr();
918
919 if chunk_was_first {
921 self.links.first = new_chunk_ptr;
922 }
923
924 if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
927 self.links.last = Some(last_chunk_ptr);
928 }
929
930 }
932
933 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
938
939 Ok(
940 unsafe { new_chunk_ptr.as_ref() },
944 )
945 }
946
947 pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
949 where
950 P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
951 {
952 self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
953 }
954
955 pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
957 where
958 P: FnMut(&'a Item) -> bool,
959 {
960 self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
961 }
962
963 pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
967 IterBackward::new(self.links.latest_chunk())
968 }
969
970 pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
974 Iter::new(self.links.first_chunk())
975 }
976
977 pub fn rchunks_from(
982 &self,
983 identifier: ChunkIdentifier,
984 ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
985 Ok(IterBackward::new(
986 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
987 ))
988 }
989
990 pub fn chunks_from(
995 &self,
996 identifier: ChunkIdentifier,
997 ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
998 Ok(Iter::new(
999 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
1000 ))
1001 }
1002
1003 pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
1007 self.ritems_from(self.links.latest_chunk().last_position())
1008 .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
1009 }
1010
1011 pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
1015 let first_chunk = self.links.first_chunk();
1016
1017 self.items_from(first_chunk.first_position())
1018 .expect("`items` cannot fail because at least one empty chunk must exist")
1019 }
1020
1021 pub fn ritems_from(
1025 &self,
1026 position: Position,
1027 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1028 Ok(self
1029 .rchunks_from(position.chunk_identifier())?
1030 .filter_map(|chunk| match &chunk.content {
1031 ChunkContent::Gap(..) => None,
1032 ChunkContent::Items(items) => {
1033 let identifier = chunk.identifier();
1034
1035 Some(
1036 items.iter().enumerate().rev().map(move |(item_index, item)| {
1037 (Position(identifier, item_index), item)
1038 }),
1039 )
1040 }
1041 })
1042 .flatten()
1043 .skip_while({
1044 let expected_index = position.index();
1045
1046 move |(Position(chunk_identifier, item_index), _item)| {
1047 *chunk_identifier == position.chunk_identifier()
1048 && *item_index != expected_index
1049 }
1050 }))
1051 }
1052
1053 pub fn items_from(
1057 &self,
1058 position: Position,
1059 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1060 Ok(self
1061 .chunks_from(position.chunk_identifier())?
1062 .filter_map(|chunk| match &chunk.content {
1063 ChunkContent::Gap(..) => None,
1064 ChunkContent::Items(items) => {
1065 let identifier = chunk.identifier();
1066
1067 Some(
1068 items.iter().enumerate().map(move |(item_index, item)| {
1069 (Position(identifier, item_index), item)
1070 }),
1071 )
1072 }
1073 })
1074 .flatten()
1075 .skip(position.index()))
1076 }
1077
1078 #[must_use]
1090 pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
1091 self.updates.as_mut()
1092 }
1093
1094 pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
1101 let (updates, token) = self
1102 .updates
1103 .as_mut()
1104 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1105 let chunk_iterator = self.chunks();
1106
1107 Some(AsVector::new(updates, token, chunk_iterator))
1108 }
1109
1110 pub fn order_tracker(
1118 &mut self,
1119 all_chunks: Option<Vec<ChunkMetadata>>,
1120 ) -> Option<OrderTracker<Item, Gap>>
1121 where
1122 Item: Clone,
1123 {
1124 let (updates, token) = self
1125 .updates
1126 .as_mut()
1127 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1128
1129 Some(OrderTracker::new(
1130 updates,
1131 token,
1132 all_chunks.unwrap_or_else(|| {
1133 self.chunks()
1135 .map(|chunk| ChunkMetadata {
1136 identifier: chunk.identifier(),
1137 num_items: chunk.num_items(),
1138 previous: chunk.previous().map(|prev| prev.identifier()),
1139 next: chunk.next().map(|next| next.identifier()),
1140 })
1141 .collect()
1142 }),
1143 ))
1144 }
1145
1146 pub fn num_items(&self) -> usize {
1148 self.items().count()
1149 }
1150}
1151
1152impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
1153 fn drop(&mut self) {
1154 unsafe {
1164 self.links.clear();
1165 }
1166 }
1167}
1168
1169unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1173
1174unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1178
1179#[derive(Debug)]
1189pub struct ChunkIdentifierGenerator {
1190 next: AtomicU64,
1191}
1192
1193impl ChunkIdentifierGenerator {
1194 const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1196
1197 pub fn new_from_scratch() -> Self {
1200 Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1201 }
1202
1203 pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1206 Self { next: AtomicU64::new(last_chunk_identifier.0) }
1207 }
1208
1209 fn next(&self) -> ChunkIdentifier {
1214 let previous = self.next.fetch_add(1, atomic::Ordering::Relaxed);
1215
1216 if previous == u64::MAX {
1219 panic!(
1220 "No more chunk identifiers available. Congrats, you did it. \
1221 2^64 identifiers have been consumed."
1222 )
1223 }
1224
1225 ChunkIdentifier(previous + 1)
1226 }
1227
1228 #[doc(hidden)]
1232 pub fn current(&self) -> ChunkIdentifier {
1233 ChunkIdentifier(self.next.load(atomic::Ordering::Relaxed))
1234 }
1235}
1236
1237#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1243#[repr(transparent)]
1244pub struct ChunkIdentifier(u64);
1245
1246impl ChunkIdentifier {
1247 pub fn new(identifier: u64) -> Self {
1249 Self(identifier)
1250 }
1251
1252 pub fn index(&self) -> u64 {
1254 self.0
1255 }
1256}
1257
1258impl PartialEq<u64> for ChunkIdentifier {
1259 fn eq(&self, other: &u64) -> bool {
1260 self.0 == *other
1261 }
1262}
1263
1264#[derive(Copy, Clone, Debug, PartialEq)]
1268pub struct Position(ChunkIdentifier, usize);
1269
1270impl Position {
1271 pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1273 Self(chunk_identifier, index)
1274 }
1275
1276 pub fn chunk_identifier(&self) -> ChunkIdentifier {
1278 self.0
1279 }
1280
1281 pub fn index(&self) -> usize {
1283 self.1
1284 }
1285
1286 pub fn decrement_index(&mut self) {
1292 self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1293 }
1294
1295 pub fn increment_index(&mut self) {
1302 self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1303 }
1304}
1305
1306#[derive(Debug)]
1309pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1310 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1311}
1312
1313impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1314 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1316 Self { chunk: Some(from_chunk) }
1317 }
1318}
1319
1320impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1321 type Item = &'a Chunk<CAP, Item, Gap>;
1322
1323 fn next(&mut self) -> Option<Self::Item> {
1324 self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1325 }
1326}
1327
1328#[derive(Debug)]
1331pub struct Iter<'a, const CAP: usize, Item, Gap> {
1332 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1333}
1334
1335impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1336 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1338 Self { chunk: Some(from_chunk) }
1339 }
1340}
1341
1342impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1343 type Item = &'a Chunk<CAP, Item, Gap>;
1344
1345 fn next(&mut self) -> Option<Self::Item> {
1346 self.chunk.inspect(|chunk| self.chunk = chunk.next())
1347 }
1348}
1349
1350#[derive(Clone, Debug)]
1352pub enum ChunkContent<Item, Gap> {
1353 Gap(Gap),
1356
1357 Items(Vec<Item>),
1359}
1360
1361pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1363 previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1365
1366 lazy_previous: Option<ChunkIdentifier>,
1371
1372 next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1374
1375 identifier: ChunkIdentifier,
1377
1378 content: ChunkContent<Item, Gap>,
1380}
1381
1382impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1383 fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1385 Self::new(identifier, ChunkContent::Gap(content))
1386 }
1387
1388 fn new_items(identifier: ChunkIdentifier) -> Self {
1390 Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1391 }
1392
1393 fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1394 Self { previous: None, lazy_previous: None, next: None, identifier, content }
1395 }
1396
1397 fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1399 let chunk = Self::new(identifier, content);
1400 let chunk_box = Box::new(chunk);
1401
1402 NonNull::from(Box::leak(chunk_box))
1403 }
1404
1405 fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1407 let chunk = Self::new_gap(identifier, content);
1408 let chunk_box = Box::new(chunk);
1409
1410 NonNull::from(Box::leak(chunk_box))
1411 }
1412
1413 fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1415 let chunk = Self::new_items(identifier);
1416 let chunk_box = Box::new(chunk);
1417
1418 NonNull::from(Box::leak(chunk_box))
1419 }
1420
1421 pub fn as_ptr(&self) -> NonNull<Self> {
1423 NonNull::from(self)
1424 }
1425
1426 pub fn is_gap(&self) -> bool {
1428 matches!(self.content, ChunkContent::Gap(..))
1429 }
1430
1431 pub fn is_items(&self) -> bool {
1433 !self.is_gap()
1434 }
1435
1436 pub fn is_definitive_head(&self) -> bool {
1439 self.previous.is_none() && self.lazy_previous.is_none()
1440 }
1441
1442 fn is_first_chunk(&self) -> bool {
1444 self.previous.is_none()
1445 }
1446
1447 fn is_last_chunk(&self) -> bool {
1449 self.next.is_none()
1450 }
1451
1452 #[doc(hidden)]
1456 pub fn lazy_previous(&self) -> Option<ChunkIdentifier> {
1457 self.lazy_previous
1458 }
1459
1460 pub fn identifier(&self) -> ChunkIdentifier {
1462 self.identifier
1463 }
1464
1465 pub fn content(&self) -> &ChunkContent<Item, Gap> {
1467 &self.content
1468 }
1469
1470 pub fn first_position(&self) -> Position {
1474 Position(self.identifier(), 0)
1475 }
1476
1477 pub fn last_position(&self) -> Position {
1481 let identifier = self.identifier();
1482
1483 match &self.content {
1484 ChunkContent::Gap(..) => Position(identifier, 0),
1485 ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1486 }
1487 }
1488
1489 pub fn num_items(&self) -> usize {
1493 match &self.content {
1494 ChunkContent::Gap(..) => 0,
1495 ChunkContent::Items(items) => items.len(),
1496 }
1497 }
1498
1499 fn push_items<I>(
1511 &mut self,
1512 mut new_items: I,
1513 chunk_identifier_generator: &ChunkIdentifierGenerator,
1514 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1515 ) -> &mut Self
1516 where
1517 I: Iterator<Item = Item> + ExactSizeIterator,
1518 Item: Clone,
1519 Gap: Clone,
1520 {
1521 if new_items.len() == 0 {
1523 return self;
1524 }
1525
1526 let identifier = self.identifier();
1527 let prev_num_items = self.num_items();
1528
1529 match &mut self.content {
1530 ChunkContent::Gap(..) => {
1533 self
1534 .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1536 .push_items(new_items, chunk_identifier_generator, updates)
1539 }
1540
1541 ChunkContent::Items(items) => {
1542 let free_space = CAPACITY.saturating_sub(prev_num_items);
1544
1545 if new_items.len() <= free_space {
1547 let start = items.len();
1548 items.extend(new_items);
1549
1550 if let Some(updates) = updates.as_mut() {
1551 updates.push(Update::PushItems {
1552 at: Position(identifier, start),
1553 items: items[start..].to_vec(),
1554 });
1555 }
1556
1557 self
1559 } else {
1560 if free_space > 0 {
1561 let start = items.len();
1563 items.extend(new_items.by_ref().take(free_space));
1564
1565 if let Some(updates) = updates.as_mut() {
1566 updates.push(Update::PushItems {
1567 at: Position(identifier, start),
1568 items: items[start..].to_vec(),
1569 });
1570 }
1571 }
1572
1573 self
1574 .insert_next(
1576 Self::new_items_leaked(chunk_identifier_generator.next()),
1577 updates,
1578 )
1579 .push_items(new_items, chunk_identifier_generator, updates)
1582 }
1583 }
1584 }
1585 }
1586
1587 fn insert_next(
1592 &mut self,
1593 mut new_chunk_ptr: NonNull<Self>,
1594 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1595 ) -> &mut Self
1596 where
1597 Gap: Clone,
1598 {
1599 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1600
1601 if let Some(next_chunk) = self.next_mut() {
1603 next_chunk.previous = Some(new_chunk_ptr);
1605
1606 new_chunk.next = self.next;
1608 }
1609
1610 self.next = Some(new_chunk_ptr);
1612 new_chunk.previous = Some(self.as_ptr());
1614
1615 if let Some(updates) = updates.as_mut() {
1616 let previous = new_chunk.previous().map(Chunk::identifier);
1617 let new = new_chunk.identifier();
1618 let next = new_chunk.next().map(Chunk::identifier);
1619
1620 match new_chunk.content() {
1621 ChunkContent::Gap(gap) => {
1622 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1623 }
1624
1625 ChunkContent::Items(..) => {
1626 updates.push(Update::NewItemsChunk { previous, new, next })
1627 }
1628 }
1629 }
1630
1631 new_chunk
1632 }
1633
1634 fn insert_before(
1639 &mut self,
1640 mut new_chunk_ptr: NonNull<Self>,
1641 updates: Option<&mut ObservableUpdates<Item, Gap>>,
1642 ) -> &mut Self
1643 where
1644 Gap: Clone,
1645 {
1646 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1647
1648 if let Some(previous_chunk) = self.previous_mut() {
1650 previous_chunk.next = Some(new_chunk_ptr);
1652
1653 new_chunk.previous = self.previous;
1655 }
1656 else {
1659 new_chunk.lazy_previous = self.lazy_previous.take();
1660 }
1661
1662 self.previous = Some(new_chunk_ptr);
1664 new_chunk.next = Some(self.as_ptr());
1666
1667 if let Some(updates) = updates {
1668 let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1669 let new = new_chunk.identifier();
1670 let next = new_chunk.next().map(Chunk::identifier);
1671
1672 match new_chunk.content() {
1673 ChunkContent::Gap(gap) => {
1674 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1675 }
1676
1677 ChunkContent::Items(..) => {
1678 updates.push(Update::NewItemsChunk { previous, new, next })
1679 }
1680 }
1681 }
1682
1683 new_chunk
1684 }
1685
1686 fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1691 let previous_ptr = self.previous;
1692 let next_ptr = self.next;
1693 let lazy_previous = self.lazy_previous.take();
1697
1698 if let Some(previous) = self.previous_mut() {
1699 previous.next = next_ptr;
1700 }
1701
1702 if let Some(next) = self.next_mut() {
1703 next.previous = previous_ptr;
1704 next.lazy_previous = lazy_previous;
1705 }
1706
1707 if let Some(updates) = updates {
1708 updates.push(Update::RemoveChunk(self.identifier()));
1709 }
1710 }
1711
1712 fn previous(&self) -> Option<&Self> {
1714 self.previous.map(|non_null| unsafe { non_null.as_ref() })
1715 }
1716
1717 fn previous_mut(&mut self) -> Option<&mut Self> {
1719 self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1720 }
1721
1722 fn next(&self) -> Option<&Self> {
1724 self.next.map(|non_null| unsafe { non_null.as_ref() })
1725 }
1726
1727 fn next_mut(&mut self) -> Option<&mut Self> {
1729 self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1730 }
1731}
1732
1733impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<CAP, Item, Gap>
1734where
1735 Item: fmt::Debug,
1736 Gap: fmt::Debug,
1737{
1738 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1739 formatter
1740 .debug_struct("LinkedChunk")
1741 .field("first (deref)", unsafe { self.links.first.as_ref() })
1742 .field("last", &self.links.last)
1743 .finish_non_exhaustive()
1744 }
1745}
1746
1747impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1748where
1749 Item: fmt::Debug,
1750 Gap: fmt::Debug,
1751{
1752 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1753 formatter
1754 .debug_struct("Chunk")
1755 .field("identifier", &self.identifier)
1756 .field("content", &self.content)
1757 .field("previous", &self.previous)
1758 .field("ptr", &std::ptr::from_ref(self))
1759 .field("next", &self.next)
1760 .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1761 .finish()
1762 }
1763}
1764
1765#[derive(Clone, Debug)]
1771pub struct RawChunk<Item, Gap> {
1772 pub content: ChunkContent<Item, Gap>,
1774
1775 pub previous: Option<ChunkIdentifier>,
1777
1778 pub identifier: ChunkIdentifier,
1780
1781 pub next: Option<ChunkIdentifier>,
1783}
1784
1785#[derive(Clone, Debug)]
1788pub struct ChunkMetadata {
1789 pub num_items: usize,
1793
1794 pub previous: Option<ChunkIdentifier>,
1796
1797 pub identifier: ChunkIdentifier,
1799
1800 pub next: Option<ChunkIdentifier>,
1802}
1803
1804#[cfg(test)]
1805mod tests {
1806 use std::{
1807 ops::Not,
1808 sync::{Arc, atomic::Ordering},
1809 };
1810
1811 use assert_matches::assert_matches;
1812
1813 use super::{
1814 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Error, LinkedChunk,
1815 Position,
1816 };
1817
1818 #[test]
1819 fn test_chunk_identifier_generator() {
1820 let generator = ChunkIdentifierGenerator::new_from_scratch();
1821
1822 assert_eq!(generator.next(), ChunkIdentifier(1));
1823 assert_eq!(generator.next(), ChunkIdentifier(2));
1824 assert_eq!(generator.next(), ChunkIdentifier(3));
1825 assert_eq!(generator.next(), ChunkIdentifier(4));
1826
1827 let generator =
1828 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1829
1830 assert_eq!(generator.next(), ChunkIdentifier(43));
1831 assert_eq!(generator.next(), ChunkIdentifier(44));
1832 assert_eq!(generator.next(), ChunkIdentifier(45));
1833 assert_eq!(generator.next(), ChunkIdentifier(46));
1834 }
1835
1836 #[test]
1837 fn test_empty() {
1838 let items = LinkedChunk::<3, char, ()>::new();
1839
1840 assert_eq!(items.num_items(), 0);
1841
1842 }
1845
1846 #[test]
1847 fn test_updates() {
1848 assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1849 assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1850 }
1851
1852 #[test]
1853 fn test_new_with_initial_update() {
1854 use super::Update::*;
1855
1856 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1857
1858 assert_eq!(
1859 linked_chunk.updates().unwrap().take(),
1860 &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1861 );
1862 }
1863
1864 #[test]
1865 fn test_push_items() {
1866 use super::Update::*;
1867
1868 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1869
1870 let _ = linked_chunk.updates().unwrap().take();
1872
1873 linked_chunk.push_items_back(['a']);
1874
1875 assert_items_eq!(linked_chunk, ['a']);
1876 assert_eq!(
1877 linked_chunk.updates().unwrap().take(),
1878 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1879 );
1880
1881 linked_chunk.push_items_back(['b', 'c']);
1882 assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1883 assert_eq!(
1884 linked_chunk.updates().unwrap().take(),
1885 &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1886 );
1887
1888 linked_chunk.push_items_back(['d', 'e']);
1889 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
1890 assert_eq!(
1891 linked_chunk.updates().unwrap().take(),
1892 &[
1893 NewItemsChunk {
1894 previous: Some(ChunkIdentifier(0)),
1895 new: ChunkIdentifier(1),
1896 next: None
1897 },
1898 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1899 ]
1900 );
1901
1902 linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1903 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1904 assert_eq!(
1905 linked_chunk.updates().unwrap().take(),
1906 &[
1907 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1908 NewItemsChunk {
1909 previous: Some(ChunkIdentifier(1)),
1910 new: ChunkIdentifier(2),
1911 next: None,
1912 },
1913 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1914 NewItemsChunk {
1915 previous: Some(ChunkIdentifier(2)),
1916 new: ChunkIdentifier(3),
1917 next: None,
1918 },
1919 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1920 ]
1921 );
1922
1923 assert_eq!(linked_chunk.num_items(), 10);
1924 }
1925
1926 #[test]
1927 fn test_push_gap() {
1928 use super::Update::*;
1929
1930 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1931
1932 let _ = linked_chunk.updates().unwrap().take();
1934
1935 linked_chunk.push_items_back(['a']);
1936 assert_items_eq!(linked_chunk, ['a']);
1937 assert_eq!(
1938 linked_chunk.updates().unwrap().take(),
1939 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1940 );
1941
1942 linked_chunk.push_gap_back(());
1943 assert_items_eq!(linked_chunk, ['a'] [-]);
1944 assert_eq!(
1945 linked_chunk.updates().unwrap().take(),
1946 &[NewGapChunk {
1947 previous: Some(ChunkIdentifier(0)),
1948 new: ChunkIdentifier(1),
1949 next: None,
1950 gap: (),
1951 }]
1952 );
1953
1954 linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1955 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1956 assert_eq!(
1957 linked_chunk.updates().unwrap().take(),
1958 &[
1959 NewItemsChunk {
1960 previous: Some(ChunkIdentifier(1)),
1961 new: ChunkIdentifier(2),
1962 next: None,
1963 },
1964 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
1965 NewItemsChunk {
1966 previous: Some(ChunkIdentifier(2)),
1967 new: ChunkIdentifier(3),
1968 next: None,
1969 },
1970 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
1971 ]
1972 );
1973
1974 linked_chunk.push_gap_back(());
1975 linked_chunk.push_gap_back(()); assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
1977 assert_eq!(
1978 linked_chunk.updates().unwrap().take(),
1979 &[
1980 NewGapChunk {
1981 previous: Some(ChunkIdentifier(3)),
1982 new: ChunkIdentifier(4),
1983 next: None,
1984 gap: (),
1985 },
1986 NewGapChunk {
1987 previous: Some(ChunkIdentifier(4)),
1988 new: ChunkIdentifier(5),
1989 next: None,
1990 gap: (),
1991 }
1992 ]
1993 );
1994
1995 linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
1996 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
1997 assert_eq!(
1998 linked_chunk.updates().unwrap().take(),
1999 &[
2000 NewItemsChunk {
2001 previous: Some(ChunkIdentifier(5)),
2002 new: ChunkIdentifier(6),
2003 next: None,
2004 },
2005 PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
2006 NewItemsChunk {
2007 previous: Some(ChunkIdentifier(6)),
2008 new: ChunkIdentifier(7),
2009 next: None,
2010 },
2011 PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
2012 ]
2013 );
2014
2015 assert_eq!(linked_chunk.num_items(), 9);
2016 }
2017
2018 #[test]
2019 fn test_identifiers_and_positions() {
2020 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2021 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2022 linked_chunk.push_gap_back(());
2023 linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
2024 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
2025
2026 assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
2027 assert_eq!(
2028 linked_chunk.item_position(|item| *item == 'e'),
2029 Some(Position(ChunkIdentifier(1), 1))
2030 );
2031 }
2032
2033 #[test]
2034 fn test_rchunks() {
2035 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2036 linked_chunk.push_items_back(['a', 'b']);
2037 linked_chunk.push_gap_back(());
2038 linked_chunk.push_items_back(['c', 'd', 'e']);
2039
2040 let mut iterator = linked_chunk.rchunks();
2041
2042 assert_matches!(
2043 iterator.next(),
2044 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2045 assert_eq!(items, &['e']);
2046 }
2047 );
2048 assert_matches!(
2049 iterator.next(),
2050 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2051 assert_eq!(items, &['c', 'd']);
2052 }
2053 );
2054 assert_matches!(
2055 iterator.next(),
2056 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2057 );
2058 assert_matches!(
2059 iterator.next(),
2060 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2061 assert_eq!(items, &['a', 'b']);
2062 }
2063 );
2064 assert_matches!(iterator.next(), None);
2065 }
2066
2067 #[test]
2068 fn test_chunks() {
2069 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2070 linked_chunk.push_items_back(['a', 'b']);
2071 linked_chunk.push_gap_back(());
2072 linked_chunk.push_items_back(['c', 'd', 'e']);
2073
2074 let mut iterator = linked_chunk.chunks();
2075
2076 assert_matches!(
2077 iterator.next(),
2078 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2079 assert_eq!(items, &['a', 'b']);
2080 }
2081 );
2082 assert_matches!(
2083 iterator.next(),
2084 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2085 );
2086 assert_matches!(
2087 iterator.next(),
2088 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2089 assert_eq!(items, &['c', 'd']);
2090 }
2091 );
2092 assert_matches!(
2093 iterator.next(),
2094 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2095 assert_eq!(items, &['e']);
2096 }
2097 );
2098 assert_matches!(iterator.next(), None);
2099 }
2100
2101 #[test]
2102 fn test_rchunks_from() -> Result<(), Error> {
2103 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2104 linked_chunk.push_items_back(['a', 'b']);
2105 linked_chunk.push_gap_back(());
2106 linked_chunk.push_items_back(['c', 'd', 'e']);
2107
2108 let mut iterator = linked_chunk.rchunks_from(
2109 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2110 )?;
2111
2112 assert_matches!(
2113 iterator.next(),
2114 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2115 assert_eq!(items, &['c', 'd']);
2116 }
2117 );
2118 assert_matches!(
2119 iterator.next(),
2120 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2121 );
2122 assert_matches!(
2123 iterator.next(),
2124 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2125 assert_eq!(items, &['a', 'b']);
2126 }
2127 );
2128 assert_matches!(iterator.next(), None);
2129
2130 Ok(())
2131 }
2132
2133 #[test]
2134 fn test_chunks_from() -> Result<(), Error> {
2135 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2136 linked_chunk.push_items_back(['a', 'b']);
2137 linked_chunk.push_gap_back(());
2138 linked_chunk.push_items_back(['c', 'd', 'e']);
2139
2140 let mut iterator = linked_chunk.chunks_from(
2141 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2142 )?;
2143
2144 assert_matches!(
2145 iterator.next(),
2146 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2147 assert_eq!(items, &['c', 'd']);
2148 }
2149 );
2150 assert_matches!(
2151 iterator.next(),
2152 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2153 assert_eq!(items, &['e']);
2154 }
2155 );
2156 assert_matches!(iterator.next(), None);
2157
2158 Ok(())
2159 }
2160
2161 #[test]
2162 fn test_ritems() {
2163 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2164 linked_chunk.push_items_back(['a', 'b']);
2165 linked_chunk.push_gap_back(());
2166 linked_chunk.push_items_back(['c', 'd', 'e']);
2167
2168 let mut iterator = linked_chunk.ritems();
2169
2170 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2171 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2172 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2173 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2174 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2175 assert_matches!(iterator.next(), None);
2176 }
2177
2178 #[test]
2179 fn test_ritems_with_final_gap() -> Result<(), Error> {
2180 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2181 linked_chunk.push_items_back(['a', 'b']);
2182 linked_chunk.push_gap_back(());
2183 linked_chunk.push_items_back(['c', 'd', 'e']);
2184 linked_chunk.push_gap_back(());
2185
2186 let mut iterator = linked_chunk.ritems();
2187
2188 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
2189 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2190 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2191 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2192 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2193 assert_matches!(iterator.next(), None);
2194
2195 Ok(())
2196 }
2197
2198 #[test]
2199 fn test_ritems_empty() {
2200 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2201 let mut iterator = linked_chunk.ritems();
2202
2203 assert_matches!(iterator.next(), None);
2204 }
2205
2206 #[test]
2207 fn test_items() {
2208 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2209 linked_chunk.push_items_back(['a', 'b']);
2210 linked_chunk.push_gap_back(());
2211 linked_chunk.push_items_back(['c', 'd', 'e']);
2212
2213 let mut iterator = linked_chunk.items();
2214
2215 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2216 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2217 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2218 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2219 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2220 assert_matches!(iterator.next(), None);
2221 }
2222
2223 #[test]
2224 fn test_items_empty() {
2225 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2226 let mut iterator = linked_chunk.items();
2227
2228 assert_matches!(iterator.next(), None);
2229 }
2230
2231 #[test]
2232 fn test_ritems_from() -> Result<(), Error> {
2233 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2234 linked_chunk.push_items_back(['a', 'b']);
2235 linked_chunk.push_gap_back(());
2236 linked_chunk.push_items_back(['c', 'd', 'e']);
2237
2238 let mut iterator =
2239 linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2240
2241 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2242 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2243 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2244 assert_matches!(iterator.next(), None);
2245
2246 Ok(())
2247 }
2248
2249 #[test]
2250 fn test_items_from() -> Result<(), Error> {
2251 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2252 linked_chunk.push_items_back(['a', 'b']);
2253 linked_chunk.push_gap_back(());
2254 linked_chunk.push_items_back(['c', 'd', 'e']);
2255
2256 let mut iterator =
2257 linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2258
2259 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2260 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2261 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2262 assert_matches!(iterator.next(), None);
2263
2264 Ok(())
2265 }
2266
2267 #[test]
2268 fn test_insert_items_at() -> Result<(), Error> {
2269 use super::Update::*;
2270
2271 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2272
2273 let _ = linked_chunk.updates().unwrap().take();
2275
2276 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2277 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2278 assert_eq!(
2279 linked_chunk.updates().unwrap().take(),
2280 &[
2281 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2282 NewItemsChunk {
2283 previous: Some(ChunkIdentifier(0)),
2284 new: ChunkIdentifier(1),
2285 next: None,
2286 },
2287 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2288 ]
2289 );
2290
2291 {
2293 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2294
2295 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2298
2299 assert_items_eq!(
2300 linked_chunk,
2301 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2302 );
2303 assert_eq!(linked_chunk.num_items(), 10);
2304 assert_eq!(
2305 linked_chunk.updates().unwrap().take(),
2306 &[
2307 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2308 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2309 NewItemsChunk {
2310 previous: Some(ChunkIdentifier(1)),
2311 new: ChunkIdentifier(2),
2312 next: None,
2313 },
2314 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2315 StartReattachItems,
2316 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2317 NewItemsChunk {
2318 previous: Some(ChunkIdentifier(2)),
2319 new: ChunkIdentifier(3),
2320 next: None,
2321 },
2322 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2323 EndReattachItems,
2324 ]
2325 );
2326 }
2327
2328 {
2330 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2331 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2332
2333 assert_items_eq!(
2334 linked_chunk,
2335 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2336 );
2337 assert_eq!(linked_chunk.num_items(), 14);
2338 assert_eq!(
2339 linked_chunk.updates().unwrap().take(),
2340 &[
2341 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2342 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2343 NewItemsChunk {
2344 previous: Some(ChunkIdentifier(0)),
2345 new: ChunkIdentifier(4),
2346 next: Some(ChunkIdentifier(1)),
2347 },
2348 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2349 StartReattachItems,
2350 PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2351 NewItemsChunk {
2352 previous: Some(ChunkIdentifier(4)),
2353 new: ChunkIdentifier(5),
2354 next: Some(ChunkIdentifier(1)),
2355 },
2356 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2357 EndReattachItems,
2358 ]
2359 );
2360 }
2361
2362 {
2364 let pos_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2365 linked_chunk.insert_items_at(pos_c, ['r', 's'])?;
2366
2367 assert_items_eq!(
2368 linked_chunk,
2369 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2370 );
2371 assert_eq!(linked_chunk.num_items(), 16);
2372 assert_eq!(
2373 linked_chunk.updates().unwrap().take(),
2374 &[
2375 DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2376 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2377 StartReattachItems,
2378 PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2379 EndReattachItems,
2380 ]
2381 );
2382 }
2383
2384 {
2386 let pos_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2387 let pos_f = Position(pos_f.chunk_identifier(), pos_f.index() + 1);
2388
2389 linked_chunk.insert_items_at(pos_f, ['p', 'q'])?;
2390 assert_items_eq!(
2391 linked_chunk,
2392 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2393 );
2394 assert_eq!(
2395 linked_chunk.updates().unwrap().take(),
2396 &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2397 );
2398 assert_eq!(linked_chunk.num_items(), 18);
2399 }
2400
2401 {
2403 assert_matches!(
2404 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2405 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2406 );
2407 assert!(linked_chunk.updates().unwrap().take().is_empty());
2408 }
2409
2410 {
2412 assert_matches!(
2413 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2414 Err(Error::InvalidItemIndex { index: 128 })
2415 );
2416 assert!(linked_chunk.updates().unwrap().take().is_empty());
2417 }
2418
2419 {
2421 linked_chunk.push_gap_back(());
2423 assert_items_eq!(
2424 linked_chunk,
2425 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2426 );
2427 assert_eq!(
2428 linked_chunk.updates().unwrap().take(),
2429 &[NewGapChunk {
2430 previous: Some(ChunkIdentifier(3)),
2431 new: ChunkIdentifier(6),
2432 next: None,
2433 gap: ()
2434 }]
2435 );
2436
2437 assert_matches!(
2438 linked_chunk.insert_items_at(Position(ChunkIdentifier(6), 0), ['u', 'v'],),
2439 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2440 );
2441 }
2442
2443 assert_eq!(linked_chunk.num_items(), 18);
2444
2445 Ok(())
2446 }
2447
2448 #[test]
2449 fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2450 use super::Update::*;
2451
2452 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2453
2454 let _ = linked_chunk.updates().unwrap().take();
2456
2457 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2458 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2459 assert_eq!(
2460 linked_chunk.updates().unwrap().take(),
2461 &[
2462 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2463 NewItemsChunk {
2464 previous: Some(ChunkIdentifier(0)),
2465 new: ChunkIdentifier(1),
2466 next: None,
2467 },
2468 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2469 ]
2470 );
2471
2472 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2474
2475 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2478
2479 assert_items_eq!(
2480 linked_chunk,
2481 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2482 );
2483 assert_eq!(linked_chunk.num_items(), 10);
2484 assert_eq!(
2485 linked_chunk.updates().unwrap().take(),
2486 &[
2487 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2488 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2489 NewItemsChunk {
2490 previous: Some(ChunkIdentifier(1)),
2491 new: ChunkIdentifier(2),
2492 next: None,
2493 },
2494 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2495 StartReattachItems,
2496 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2497 NewItemsChunk {
2498 previous: Some(ChunkIdentifier(2)),
2499 new: ChunkIdentifier(3),
2500 next: None,
2501 },
2502 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2503 EndReattachItems,
2504 ]
2505 );
2506
2507 Ok(())
2508 }
2509
2510 #[test]
2511 fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2512 use super::Update::*;
2513
2514 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2515
2516 let _ = linked_chunk.updates().unwrap().take();
2518
2519 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2520 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2521 assert_eq!(
2522 linked_chunk.updates().unwrap().take(),
2523 &[
2524 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2525 NewItemsChunk {
2526 previous: Some(ChunkIdentifier(0)),
2527 new: ChunkIdentifier(1),
2528 next: None,
2529 },
2530 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2531 ]
2532 );
2533
2534 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2536 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2537
2538 assert_items_eq!(
2539 linked_chunk,
2540 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'e', 'f']
2541 );
2542 assert_eq!(linked_chunk.num_items(), 10);
2543 assert_eq!(
2544 linked_chunk.updates().unwrap().take(),
2545 &[
2546 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2547 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2548 NewItemsChunk {
2549 previous: Some(ChunkIdentifier(0)),
2550 new: ChunkIdentifier(2),
2551 next: Some(ChunkIdentifier(1)),
2552 },
2553 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2554 StartReattachItems,
2555 PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2556 NewItemsChunk {
2557 previous: Some(ChunkIdentifier(2)),
2558 new: ChunkIdentifier(3),
2559 next: Some(ChunkIdentifier(1)),
2560 },
2561 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2562 EndReattachItems,
2563 ]
2564 );
2565
2566 Ok(())
2567 }
2568
2569 #[test]
2570 fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2571 use super::Update::*;
2572
2573 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2574
2575 let _ = linked_chunk.updates().unwrap().take();
2577
2578 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2579 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2580 assert_eq!(
2581 linked_chunk.updates().unwrap().take(),
2582 &[
2583 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2584 NewItemsChunk {
2585 previous: Some(ChunkIdentifier(0)),
2586 new: ChunkIdentifier(1),
2587 next: None,
2588 },
2589 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2590 NewItemsChunk {
2591 previous: Some(ChunkIdentifier(1)),
2592 new: ChunkIdentifier(2),
2593 next: None,
2594 },
2595 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2596 ]
2597 );
2598
2599 let pos_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2600 linked_chunk.insert_items_at(pos_d, ['r', 's'])?;
2601
2602 assert_items_eq!(
2603 linked_chunk,
2604 ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2605 );
2606 assert_eq!(linked_chunk.num_items(), 10);
2607 assert_eq!(
2608 linked_chunk.updates().unwrap().take(),
2609 &[
2610 DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2611 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2612 StartReattachItems,
2613 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2614 NewItemsChunk {
2615 previous: Some(ChunkIdentifier(1)),
2616 new: ChunkIdentifier(3),
2617 next: Some(ChunkIdentifier(2)),
2618 },
2619 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2620 EndReattachItems,
2621 ]
2622 );
2623
2624 Ok(())
2625 }
2626
2627 #[test]
2628 fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2629 use super::Update::*;
2630
2631 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2632
2633 let _ = linked_chunk.updates().unwrap().take();
2635
2636 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2637 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2638 assert_eq!(
2639 linked_chunk.updates().unwrap().take(),
2640 &[
2641 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2642 NewItemsChunk {
2643 previous: Some(ChunkIdentifier(0)),
2644 new: ChunkIdentifier(1),
2645 next: None,
2646 },
2647 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2648 ]
2649 );
2650
2651 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2653 let pos_after_e = Position(pos_e.chunk_identifier(), pos_e.index() + 1);
2654
2655 linked_chunk.insert_items_at(pos_after_e, ['p', 'q'])?;
2656 assert_items_eq!(
2657 linked_chunk,
2658 ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2659 );
2660 assert_eq!(
2661 linked_chunk.updates().unwrap().take(),
2662 &[
2663 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2664 NewItemsChunk {
2665 previous: Some(ChunkIdentifier(1)),
2666 new: ChunkIdentifier(2),
2667 next: None
2668 },
2669 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2670 ]
2671 );
2672 assert_eq!(linked_chunk.num_items(), 7);
2673
2674 Ok(())
2675 }
2676
2677 #[test]
2678 fn test_insert_items_at_errs() -> Result<(), Error> {
2679 use super::Update::*;
2680
2681 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2682
2683 let _ = linked_chunk.updates().unwrap().take();
2685
2686 linked_chunk.push_items_back(['a', 'b', 'c']);
2687 linked_chunk.push_gap_back(());
2688 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2689 assert_eq!(
2690 linked_chunk.updates().unwrap().take(),
2691 &[
2692 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2693 NewGapChunk {
2694 previous: Some(ChunkIdentifier(0)),
2695 new: ChunkIdentifier(1),
2696 next: None,
2697 gap: (),
2698 },
2699 ]
2700 );
2701
2702 {
2704 assert_matches!(
2705 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2706 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2707 );
2708 assert!(linked_chunk.updates().unwrap().take().is_empty());
2709 }
2710
2711 {
2713 assert_matches!(
2714 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2715 Err(Error::InvalidItemIndex { index: 128 })
2716 );
2717 assert!(linked_chunk.updates().unwrap().take().is_empty());
2718 }
2719
2720 {
2722 assert_matches!(
2723 linked_chunk.insert_items_at(Position(ChunkIdentifier(1), 0), ['u', 'v'],),
2724 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2725 );
2726 }
2727
2728 Ok(())
2729 }
2730
2731 #[test]
2732 fn test_remove_item_at() -> Result<(), Error> {
2733 use super::Update::*;
2734
2735 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2736
2737 let _ = linked_chunk.updates().unwrap().take();
2739
2740 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2741 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2742 assert_eq!(linked_chunk.num_items(), 11);
2743
2744 let _ = linked_chunk.updates().unwrap().take();
2746
2747 {
2750 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2751 let removed_item = linked_chunk.remove_item_at(position_of_f)?;
2752
2753 assert_eq!(removed_item, 'f');
2754 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2755 assert_eq!(linked_chunk.num_items(), 10);
2756
2757 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2758 let removed_item = linked_chunk.remove_item_at(position_of_e)?;
2759
2760 assert_eq!(removed_item, 'e');
2761 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2762 assert_eq!(linked_chunk.num_items(), 9);
2763
2764 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2765 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2766
2767 assert_eq!(removed_item, 'd');
2768 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2769 assert_eq!(linked_chunk.num_items(), 8);
2770
2771 assert_eq!(
2772 linked_chunk.updates().unwrap().take(),
2773 &[
2774 RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2775 RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2776 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2777 RemoveChunk(ChunkIdentifier(1)),
2778 ]
2779 );
2780 }
2781
2782 {
2785 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2786 let removed_item = linked_chunk.remove_item_at(first_position)?;
2787
2788 assert_eq!(removed_item, 'a');
2789 assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2790 assert_eq!(linked_chunk.num_items(), 7);
2791
2792 let removed_item = linked_chunk.remove_item_at(first_position)?;
2793
2794 assert_eq!(removed_item, 'b');
2795 assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2796 assert_eq!(linked_chunk.num_items(), 6);
2797
2798 let removed_item = linked_chunk.remove_item_at(first_position)?;
2799
2800 assert_eq!(removed_item, 'c');
2801 assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2802 assert_eq!(linked_chunk.num_items(), 5);
2803
2804 assert_eq!(
2805 linked_chunk.updates().unwrap().take(),
2806 &[
2807 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2808 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2809 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2810 ]
2811 );
2812 }
2813
2814 {
2817 let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2818 let removed_item = linked_chunk.remove_item_at(first_position)?;
2819
2820 assert_eq!(removed_item, 'g');
2821 assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2822 assert_eq!(linked_chunk.num_items(), 4);
2823
2824 let removed_item = linked_chunk.remove_item_at(first_position)?;
2825
2826 assert_eq!(removed_item, 'h');
2827 assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2828 assert_eq!(linked_chunk.num_items(), 3);
2829
2830 let removed_item = linked_chunk.remove_item_at(first_position)?;
2831
2832 assert_eq!(removed_item, 'i');
2833 assert_items_eq!(linked_chunk, [] ['j', 'k']);
2834 assert_eq!(linked_chunk.num_items(), 2);
2835
2836 assert_eq!(
2837 linked_chunk.updates().unwrap().take(),
2838 &[
2839 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2840 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2841 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2842 RemoveChunk(ChunkIdentifier(2)),
2843 ]
2844 );
2845 }
2846
2847 {
2850 let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2851 let removed_item = linked_chunk.remove_item_at(position_of_k)?;
2852
2853 assert_eq!(removed_item, 'k');
2854 #[rustfmt::skip]
2855 assert_items_eq!(linked_chunk, [] ['j']);
2856 assert_eq!(linked_chunk.num_items(), 1);
2857
2858 let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2859 let removed_item = linked_chunk.remove_item_at(position_of_j)?;
2860
2861 assert_eq!(removed_item, 'j');
2862 assert_items_eq!(linked_chunk, []);
2863 assert_eq!(linked_chunk.num_items(), 0);
2864
2865 assert_eq!(
2866 linked_chunk.updates().unwrap().take(),
2867 &[
2868 RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2869 RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2870 RemoveChunk(ChunkIdentifier(3)),
2871 ]
2872 );
2873 }
2874
2875 {
2877 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2878
2879 #[rustfmt::skip]
2880 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2881 assert_eq!(linked_chunk.num_items(), 4);
2882
2883 assert_matches!(
2885 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 3)),
2886 Err(Error::InvalidItemIndex { index: 3 })
2887 );
2888
2889 assert_matches!(
2891 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 42)),
2892 Err(Error::InvalidItemIndex { index: 42 })
2893 );
2894
2895 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2896 linked_chunk.insert_gap_at((), position_of_c)?;
2897
2898 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2899 assert_eq!(linked_chunk.num_items(), 4);
2900
2901 let _ = linked_chunk.updates().unwrap().take();
2903
2904 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2905 let removed_item = linked_chunk.remove_item_at(position_of_c)?;
2906
2907 assert_eq!(removed_item, 'c');
2908 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2909 assert_eq!(linked_chunk.num_items(), 3);
2910
2911 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2912 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2913
2914 assert_eq!(removed_item, 'd');
2915 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2916 assert_eq!(linked_chunk.num_items(), 2);
2917
2918 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2919 let removed_item = linked_chunk.remove_item_at(first_position)?;
2920
2921 assert_eq!(removed_item, 'a');
2922 assert_items_eq!(linked_chunk, ['b'] [-]);
2923 assert_eq!(linked_chunk.num_items(), 1);
2924
2925 let removed_item = linked_chunk.remove_item_at(first_position)?;
2926
2927 assert_eq!(removed_item, 'b');
2928 assert_items_eq!(linked_chunk, [] [-]);
2929 assert_eq!(linked_chunk.num_items(), 0);
2930
2931 assert_eq!(
2932 linked_chunk.updates().unwrap().take(),
2933 &[
2934 RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2935 RemoveChunk(ChunkIdentifier(6)),
2936 RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2937 RemoveChunk(ChunkIdentifier(4)),
2938 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2939 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2940 ]
2941 );
2942 }
2943
2944 Ok(())
2945 }
2946
2947 #[test]
2948 fn test_insert_gap_at() -> Result<(), Error> {
2949 use super::Update::*;
2950
2951 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2952
2953 let _ = linked_chunk.updates().unwrap().take();
2955
2956 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2957 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2958 assert_eq!(
2959 linked_chunk.updates().unwrap().take(),
2960 &[
2961 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2962 NewItemsChunk {
2963 previous: Some(ChunkIdentifier(0)),
2964 new: ChunkIdentifier(1),
2965 next: None
2966 },
2967 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2968 ]
2969 );
2970
2971 {
2973 let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
2974 linked_chunk.insert_gap_at((), position_of_b)?;
2975
2976 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2977 assert_eq!(
2978 linked_chunk.updates().unwrap().take(),
2979 &[
2980 DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
2981 NewGapChunk {
2982 previous: Some(ChunkIdentifier(0)),
2983 new: ChunkIdentifier(2),
2984 next: Some(ChunkIdentifier(1)),
2985 gap: (),
2986 },
2987 StartReattachItems,
2988 NewItemsChunk {
2989 previous: Some(ChunkIdentifier(2)),
2990 new: ChunkIdentifier(3),
2991 next: Some(ChunkIdentifier(1)),
2992 },
2993 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
2994 EndReattachItems,
2995 ]
2996 );
2997 }
2998
2999 {
3002 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3003 linked_chunk.insert_gap_at((), position_of_a)?;
3004
3005 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
3008 assert_eq!(
3009 linked_chunk.updates().unwrap().take(),
3010 &[NewGapChunk {
3011 previous: None,
3012 new: ChunkIdentifier(4),
3013 next: Some(ChunkIdentifier(0)),
3014 gap: (),
3015 },]
3016 );
3017 }
3018
3019 {
3022 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
3023 linked_chunk.insert_gap_at((), position_of_d)?;
3024
3025 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
3029 assert_eq!(
3030 linked_chunk.updates().unwrap().take(),
3031 &[NewGapChunk {
3032 previous: Some(ChunkIdentifier(3)),
3033 new: ChunkIdentifier(5),
3034 next: Some(ChunkIdentifier(1)),
3035 gap: (),
3036 }]
3037 );
3038 }
3039
3040 {
3042 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3044 let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
3045
3046 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
3047
3048 assert_eq!(
3049 linked_chunk.updates().unwrap().take(),
3050 &[
3051 NewItemsChunk {
3052 previous: Some(ChunkIdentifier(5)),
3053 new: ChunkIdentifier(6),
3054 next: Some(ChunkIdentifier(1)),
3055 },
3056 RemoveChunk(ChunkIdentifier(5)),
3057 ]
3058 );
3059
3060 linked_chunk.insert_gap_at((), position)?;
3061
3062 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
3063 assert_eq!(
3064 linked_chunk.updates().unwrap().take(),
3065 &[NewGapChunk {
3066 previous: Some(ChunkIdentifier(3)),
3067 new: ChunkIdentifier(7),
3068 next: Some(ChunkIdentifier(6)),
3069 gap: (),
3070 }]
3071 );
3072 }
3073
3074 {
3076 assert_matches!(
3077 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
3078 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
3079 );
3080 assert!(linked_chunk.updates().unwrap().take().is_empty());
3081 }
3082
3083 {
3085 assert_matches!(
3086 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
3087 Err(Error::InvalidItemIndex { index: 128 })
3088 );
3089 assert!(linked_chunk.updates().unwrap().take().is_empty());
3090 }
3091
3092 {
3094 let position_of_a_gap = Position(ChunkIdentifier(2), 0);
3097 assert_matches!(
3098 linked_chunk.insert_gap_at((), position_of_a_gap),
3099 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
3100 );
3101 assert!(linked_chunk.updates().unwrap().take().is_empty());
3102 }
3103
3104 assert_eq!(linked_chunk.num_items(), 6);
3105
3106 Ok(())
3107 }
3108
3109 #[test]
3110 fn test_replace_gap_at_middle() -> Result<(), Error> {
3111 use super::Update::*;
3112
3113 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3114
3115 let _ = linked_chunk.updates().unwrap().take();
3117
3118 linked_chunk.push_items_back(['a', 'b']);
3119 linked_chunk.push_gap_back(());
3120 linked_chunk.push_items_back(['l', 'm']);
3121 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
3122 assert_eq!(
3123 linked_chunk.updates().unwrap().take(),
3124 &[
3125 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3126 NewGapChunk {
3127 previous: Some(ChunkIdentifier(0)),
3128 new: ChunkIdentifier(1),
3129 next: None,
3130 gap: (),
3131 },
3132 NewItemsChunk {
3133 previous: Some(ChunkIdentifier(1)),
3134 new: ChunkIdentifier(2),
3135 next: None,
3136 },
3137 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
3138 ]
3139 );
3140
3141 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3143 assert_eq!(gap_identifier, ChunkIdentifier(1));
3144
3145 let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
3146 assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
3147 assert_items_eq!(
3148 linked_chunk,
3149 ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
3150 );
3151 assert_eq!(
3152 linked_chunk.updates().unwrap().take(),
3153 &[
3154 NewItemsChunk {
3155 previous: Some(ChunkIdentifier(1)),
3156 new: ChunkIdentifier(3),
3157 next: Some(ChunkIdentifier(2)),
3158 },
3159 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
3160 NewItemsChunk {
3161 previous: Some(ChunkIdentifier(3)),
3162 new: ChunkIdentifier(4),
3163 next: Some(ChunkIdentifier(2)),
3164 },
3165 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
3166 RemoveChunk(ChunkIdentifier(1)),
3167 ]
3168 );
3169
3170 assert_eq!(linked_chunk.num_items(), 9);
3171
3172 Ok(())
3173 }
3174
3175 #[test]
3176 fn test_replace_gap_at_end() -> Result<(), Error> {
3177 use super::Update::*;
3178
3179 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3180
3181 let _ = linked_chunk.updates().unwrap().take();
3183
3184 linked_chunk.push_items_back(['a', 'b']);
3185 linked_chunk.push_gap_back(());
3186 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3187 assert_eq!(
3188 linked_chunk.updates().unwrap().take(),
3189 &[
3190 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3191 NewGapChunk {
3192 previous: Some(ChunkIdentifier(0)),
3193 new: ChunkIdentifier(1),
3194 next: None,
3195 gap: (),
3196 },
3197 ]
3198 );
3199
3200 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3202 assert_eq!(gap_identifier, ChunkIdentifier(1));
3203
3204 let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3205 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3206 assert_items_eq!(
3207 linked_chunk,
3208 ['a', 'b'] ['w', 'x', 'y'] ['z']
3209 );
3210 assert_eq!(
3211 linked_chunk.updates().unwrap().take(),
3212 &[
3213 NewItemsChunk {
3214 previous: Some(ChunkIdentifier(1)),
3215 new: ChunkIdentifier(2),
3216 next: None,
3217 },
3218 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3219 NewItemsChunk {
3220 previous: Some(ChunkIdentifier(2)),
3221 new: ChunkIdentifier(3),
3222 next: None,
3223 },
3224 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3225 RemoveChunk(ChunkIdentifier(1)),
3226 ]
3227 );
3228
3229 assert_eq!(linked_chunk.num_items(), 6);
3230
3231 Ok(())
3232 }
3233
3234 #[test]
3235 fn test_replace_gap_at_beginning() -> Result<(), Error> {
3236 use super::Update::*;
3237
3238 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3239
3240 let _ = linked_chunk.updates().unwrap().take();
3242
3243 linked_chunk.push_items_back(['a', 'b']);
3244 assert_items_eq!(linked_chunk, ['a', 'b']);
3245 assert_eq!(
3246 linked_chunk.updates().unwrap().take(),
3247 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },]
3248 );
3249
3250 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3252 linked_chunk.insert_gap_at((), position_of_a).unwrap();
3253 assert_items_eq!(
3254 linked_chunk,
3255 [-] ['a', 'b']
3256 );
3257 assert_eq!(
3258 linked_chunk.updates().unwrap().take(),
3259 &[NewGapChunk {
3260 previous: None,
3261 new: ChunkIdentifier(1),
3262 next: Some(ChunkIdentifier(0)),
3263 gap: (),
3264 }]
3265 );
3266
3267 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3268 assert_eq!(gap_identifier, ChunkIdentifier(1));
3269
3270 let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3271 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3272 assert_items_eq!(
3273 linked_chunk,
3274 ['x'] ['a', 'b']
3275 );
3276 assert_eq!(
3277 linked_chunk.updates().unwrap().take(),
3278 &[
3279 NewItemsChunk {
3280 previous: Some(ChunkIdentifier(1)),
3281 new: ChunkIdentifier(2),
3282 next: Some(ChunkIdentifier(0)),
3283 },
3284 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3285 RemoveChunk(ChunkIdentifier(1)),
3286 ]
3287 );
3288
3289 assert_eq!(linked_chunk.num_items(), 3);
3290
3291 Ok(())
3292 }
3293
3294 #[test]
3295 fn test_remove_empty_chunk_at() -> Result<(), Error> {
3296 use super::Update::*;
3297
3298 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3299
3300 let _ = linked_chunk.updates().unwrap().take();
3302
3303 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3304 linked_chunk.push_items_back(['a', 'b']);
3305 linked_chunk.push_gap_back(());
3306 linked_chunk.push_items_back(['l', 'm']);
3307 linked_chunk.push_gap_back(());
3308 assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3309 assert_eq!(
3310 linked_chunk.updates().unwrap().take(),
3311 &[
3312 NewGapChunk {
3313 previous: None,
3314 new: ChunkIdentifier(1),
3315 next: Some(ChunkIdentifier(0)),
3316 gap: (),
3317 },
3318 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3319 NewGapChunk {
3320 previous: Some(ChunkIdentifier(0)),
3321 new: ChunkIdentifier(2),
3322 next: None,
3323 gap: (),
3324 },
3325 NewItemsChunk {
3326 previous: Some(ChunkIdentifier(2)),
3327 new: ChunkIdentifier(3),
3328 next: None,
3329 },
3330 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3331 NewGapChunk {
3332 previous: Some(ChunkIdentifier(3)),
3333 new: ChunkIdentifier(4),
3334 next: None,
3335 gap: (),
3336 },
3337 ]
3338 );
3339
3340 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3342 assert_matches!(err, Error::RemovingNonEmptyItemsChunk { .. });
3343
3344 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(42)).unwrap_err();
3346 assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3347
3348 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(2)).unwrap();
3350 let next = maybe_next.unwrap();
3351 assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3353 assert_eq!(next.index(), 0);
3354 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3355 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3356
3357 let next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(4)).unwrap();
3359 assert!(next.is_none());
3361 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3362 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3363
3364 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(1)).unwrap();
3366 let next = maybe_next.unwrap();
3367 assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3368 assert_eq!(next.index(), 0);
3369 assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3370 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3371
3372 Ok(())
3373 }
3374
3375 #[test]
3376 fn test_remove_empty_last_chunk() {
3377 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3378
3379 let _ = linked_chunk.updates().unwrap().take();
3381
3382 assert_items_eq!(linked_chunk, []);
3383 assert!(linked_chunk.updates().unwrap().take().is_empty());
3384
3385 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3387 assert_matches!(err, Error::RemovingLastChunk);
3388 }
3389
3390 #[test]
3391 fn test_chunk_item_positions() {
3392 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3393 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3394 linked_chunk.push_gap_back(());
3395 linked_chunk.push_items_back(['f']);
3396
3397 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3398
3399 let mut iterator = linked_chunk.chunks();
3400
3401 {
3403 let chunk = iterator.next().unwrap();
3404 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3405 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3406 }
3407
3408 {
3410 let chunk = iterator.next().unwrap();
3411 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3412 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3413 }
3414
3415 {
3417 let chunk = iterator.next().unwrap();
3418 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3419 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3420 }
3421
3422 {
3424 let chunk = iterator.next().unwrap();
3425 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3426 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3427 }
3428 }
3429
3430 #[test]
3431 fn test_is_first_and_last_chunk() {
3432 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3433
3434 let mut chunks = linked_chunk.chunks().peekable();
3435 assert!(chunks.peek().unwrap().is_first_chunk());
3436 assert!(chunks.next().unwrap().is_last_chunk());
3437 assert!(chunks.next().is_none());
3438
3439 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3440
3441 let mut chunks = linked_chunk.chunks().peekable();
3442 assert!(chunks.next().unwrap().is_first_chunk());
3443 assert!(chunks.peek().unwrap().is_first_chunk().not());
3444 assert!(chunks.next().unwrap().is_last_chunk().not());
3445 assert!(chunks.next().unwrap().is_last_chunk());
3446 assert!(chunks.next().is_none());
3447 }
3448
3449 #[test]
3454 fn test_clear() {
3455 let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3456
3457 let item = Arc::new('a');
3458 let gap = Arc::new(());
3459
3460 linked_chunk.push_items_back([
3461 item.clone(),
3462 item.clone(),
3463 item.clone(),
3464 item.clone(),
3465 item.clone(),
3466 ]);
3467 linked_chunk.push_gap_back(gap.clone());
3468 linked_chunk.push_items_back([item.clone()]);
3469
3470 assert_eq!(Arc::strong_count(&item), 7);
3471 assert_eq!(Arc::strong_count(&gap), 2);
3472 assert_eq!(linked_chunk.num_items(), 6);
3473 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3474
3475 linked_chunk.clear();
3477
3478 assert_eq!(Arc::strong_count(&item), 1);
3479 assert_eq!(Arc::strong_count(&gap), 1);
3480 assert_eq!(linked_chunk.num_items(), 0);
3481 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3482 }
3483
3484 #[test]
3485 fn test_clear_emit_an_update_clear() {
3486 use super::Update::*;
3487
3488 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3489
3490 assert_eq!(
3491 linked_chunk.updates().unwrap().take(),
3492 &[NewItemsChunk {
3493 previous: None,
3494 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3495 next: None
3496 }]
3497 );
3498
3499 linked_chunk.clear();
3500
3501 assert_eq!(
3502 linked_chunk.updates().unwrap().take(),
3503 &[
3504 Clear,
3505 NewItemsChunk {
3506 previous: None,
3507 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3508 next: None
3509 }
3510 ]
3511 );
3512 }
3513
3514 #[test]
3515 fn test_replace_item() {
3516 use super::Update::*;
3517
3518 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3519
3520 linked_chunk.push_items_back(['a', 'b', 'c']);
3521 linked_chunk.push_gap_back(());
3522 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3524
3525 let _ = linked_chunk.updates().unwrap().take();
3527
3528 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3530 assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3531
3532 assert_eq!(
3533 linked_chunk.updates().unwrap().take(),
3534 &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3535 );
3536
3537 assert_matches!(
3539 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3540 Err(Error::InvalidItemIndex { index: 3 })
3541 );
3542
3543 assert_matches!(
3545 linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3546 Err(Error::ChunkIsAGap { .. })
3547 );
3548 }
3549
3550 #[test]
3551 fn test_lazy_previous() {
3552 use std::marker::PhantomData;
3553
3554 use super::{Ends, ObservableUpdates, Update::*};
3555
3556 let first_chunk_identifier = ChunkIdentifier(0);
3558 let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3559 unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3560
3561 let mut linked_chunk = LinkedChunk::<3, char, ()> {
3562 links: Ends { first: first_loaded_chunk, last: None },
3563 chunk_identifier_generator:
3564 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3565 updates: Some(ObservableUpdates::new()),
3566 marker: PhantomData,
3567 };
3568
3569 {
3572 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3573
3574 assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3575
3576 {
3578 let mut chunks = linked_chunk.chunks();
3579
3580 assert_matches!(chunks.next(), Some(chunk) => {
3581 assert_eq!(chunk.identifier(), 1);
3582 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3583 });
3584 assert_matches!(chunks.next(), Some(chunk) => {
3585 assert_eq!(chunk.identifier(), 2);
3586 assert!(chunk.lazy_previous.is_none());
3587 });
3588 assert!(chunks.next().is_none());
3589 }
3590
3591 assert_eq!(
3593 linked_chunk.updates().unwrap().take(),
3594 &[
3595 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3596 NewItemsChunk {
3597 previous: Some(ChunkIdentifier(1)),
3598 new: ChunkIdentifier(2),
3599 next: None,
3600 },
3601 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3602 ]
3603 );
3604 }
3605
3606 {
3608 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3609
3610 assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3611
3612 {
3614 let mut chunks = linked_chunk.chunks();
3615
3616 assert_matches!(chunks.next(), Some(chunk) => {
3617 assert_eq!(chunk.identifier(), 3);
3618 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3620 });
3621 assert_matches!(chunks.next(), Some(chunk) => {
3622 assert_eq!(chunk.identifier(), 1);
3623 assert!(chunk.lazy_previous.is_none());
3625 });
3626 assert_matches!(chunks.next(), Some(chunk) => {
3627 assert_eq!(chunk.identifier(), 2);
3628 assert!(chunk.lazy_previous.is_none());
3629 });
3630 assert!(chunks.next().is_none());
3631 }
3632
3633 assert_eq!(
3635 linked_chunk.updates().unwrap().take(),
3636 &[NewGapChunk {
3637 previous: Some(ChunkIdentifier(0)),
3639 new: ChunkIdentifier(3),
3640 next: Some(ChunkIdentifier(1)),
3641 gap: ()
3642 }]
3643 );
3644 }
3645
3646 {
3648 linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3649
3650 assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3651
3652 {
3654 let mut chunks = linked_chunk.chunks();
3655
3656 assert_matches!(chunks.next(), Some(chunk) => {
3657 assert_eq!(chunk.identifier(), 4);
3658 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3660 });
3661 assert_matches!(chunks.next(), Some(chunk) => {
3662 assert_eq!(chunk.identifier(), 5);
3663 assert!(chunk.lazy_previous.is_none());
3664 });
3665 assert_matches!(chunks.next(), Some(chunk) => {
3666 assert_eq!(chunk.identifier(), 1);
3667 assert!(chunk.lazy_previous.is_none());
3668 });
3669 assert_matches!(chunks.next(), Some(chunk) => {
3670 assert_eq!(chunk.identifier(), 2);
3671 assert!(chunk.lazy_previous.is_none());
3672 });
3673 assert!(chunks.next().is_none());
3674 }
3675
3676 assert_eq!(
3678 linked_chunk.updates().unwrap().take(),
3679 &[
3680 NewItemsChunk {
3682 previous: Some(ChunkIdentifier(3)),
3683 new: ChunkIdentifier(4),
3684 next: Some(ChunkIdentifier(1)),
3685 },
3686 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3688 NewItemsChunk {
3690 previous: Some(ChunkIdentifier(4)),
3691 new: ChunkIdentifier(5),
3692 next: Some(ChunkIdentifier(1)),
3693 },
3694 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3696 RemoveChunk(ChunkIdentifier(3)),
3698 ]
3699 );
3700 }
3701
3702 {
3706 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3707
3708 assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3709
3710 {
3712 let mut chunks = linked_chunk.chunks();
3713
3714 assert_matches!(chunks.next(), Some(chunk) => {
3715 assert_eq!(chunk.identifier(), 6);
3716 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3718 });
3719 assert_matches!(chunks.next(), Some(chunk) => {
3720 assert_eq!(chunk.identifier(), 4);
3721 assert!(chunk.lazy_previous.is_none());
3723 });
3724 assert_matches!(chunks.next(), Some(chunk) => {
3725 assert_eq!(chunk.identifier(), 5);
3726 assert!(chunk.lazy_previous.is_none());
3727 });
3728 assert_matches!(chunks.next(), Some(chunk) => {
3729 assert_eq!(chunk.identifier(), 1);
3730 assert!(chunk.lazy_previous.is_none());
3731 });
3732 assert_matches!(chunks.next(), Some(chunk) => {
3733 assert_eq!(chunk.identifier(), 2);
3734 assert!(chunk.lazy_previous.is_none());
3735 });
3736 assert!(chunks.next().is_none());
3737 }
3738
3739 assert_eq!(
3741 linked_chunk.updates().unwrap().take(),
3742 &[NewGapChunk {
3743 previous: Some(ChunkIdentifier(0)),
3745 new: ChunkIdentifier(6),
3746 next: Some(ChunkIdentifier(4)),
3747 gap: ()
3748 }]
3749 );
3750 }
3751 }
3752}