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;
95mod identifiers;
96pub mod lazy_loader;
97mod order_tracker;
98pub mod relational;
99mod updates;
100
101use std::{
102 fmt::{self},
103 marker::PhantomData,
104 ptr::NonNull,
105 sync::{
106 OnceLock,
107 atomic::{self, AtomicU64},
108 },
109};
110
111pub use self::{as_vector::*, identifiers::*, order_tracker::OrderTracker, updates::*};
112
113#[derive(thiserror::Error, Debug)]
115pub enum Error {
116 #[error("The chunk identifier is invalid: `{identifier:?}`")]
118 InvalidChunkIdentifier {
119 identifier: ChunkIdentifier,
121 },
122
123 #[error("The chunk is a gap: `{identifier:?}`")]
125 ChunkIsAGap {
126 identifier: ChunkIdentifier,
128 },
129
130 #[error("The chunk is an item: `{identifier:?}`")]
132 ChunkIsItems {
133 identifier: ChunkIdentifier,
135 },
136
137 #[error("The chunk is a non-empty item chunk: `{identifier:?}`")]
139 RemovingNonEmptyItemsChunk {
140 identifier: ChunkIdentifier,
142 },
143
144 #[error("Trying to remove the only chunk, but a linked chunk can't be empty")]
147 RemovingLastChunk,
148
149 #[error("The item index is invalid: `{index}`")]
151 InvalidItemIndex {
152 index: usize,
154 },
155}
156
157struct Ends<const CHUNK_CAPACITY: usize, Item, Gap> {
162 first: OnceLock<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
164
165 last: Option<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
167
168 updates_pusher: Option<ObservableUpdatesPusher<Item, Gap>>,
169}
170
171impl<const CAP: usize, Item, Gap> Ends<CAP, Item, Gap> {
172 fn new(updates: &Option<ObservableUpdates<Item, Gap>>) -> Self {
174 Self {
175 first: OnceLock::new(),
176 last: None,
177 updates_pusher: updates.as_ref().map(ObservableUpdates::new_pusher),
178 }
179 }
180
181 fn new_with_first_chunk(
183 first_chunk: NonNull<Chunk<CAP, Item, Gap>>,
184 updates: &Option<ObservableUpdates<Item, Gap>>,
185 ) -> Self {
186 Self {
187 first: {
188 let first = OnceLock::new();
189
190 first.get_or_init(|| first_chunk);
192
193 first
194 },
195 last: None,
196 updates_pusher: updates.as_ref().map(ObservableUpdates::new_pusher),
197 }
198 }
199
200 fn first_chunk_ptr(&self) -> &NonNull<Chunk<CAP, Item, Gap>> {
202 self.first
203 .get_or_init(|| {
205 let identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
206
207 if let Some(updates) = self.updates_pusher.as_ref() {
208 updates.push(Update::NewItemsChunk {
209 previous: None,
210 new: identifier,
211 next: None,
212 });
213 }
214
215 Chunk::new_items_leaked(identifier)
216 })
217 }
218
219 fn first_chunk_mut_ptr(&mut self) -> &mut NonNull<Chunk<CAP, Item, Gap>> {
221 let _ = self.first_chunk_ptr();
224
225 self.first
226 .get_mut()
227 .expect("`first` must have been initialised")
233 }
234
235 fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
237 unsafe { self.first_chunk_ptr().as_ref() }
240 }
241
242 fn first_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
244 unsafe { self.first_chunk_mut_ptr().as_mut() }
247 }
248
249 fn latest_chunk(&self) -> &Chunk<CAP, Item, Gap> {
251 if let Some(last) = &self.last {
252 unsafe { last.as_ref() }
255 } else {
256 self.first_chunk()
257 }
258 }
259
260 fn latest_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
262 if let Some(last) = &mut self.last {
263 unsafe { last.as_mut() }
266 } else {
267 self.first_chunk_mut()
268 }
269 }
270
271 fn chunk(&self, identifier: ChunkIdentifier) -> Option<&Chunk<CAP, Item, Gap>> {
273 let mut chunk = self.latest_chunk();
274
275 loop {
276 if chunk.identifier() == identifier {
277 return Some(chunk);
278 }
279
280 chunk = chunk.previous()?;
281 }
282 }
283
284 fn chunk_mut(&mut self, identifier: ChunkIdentifier) -> Option<&mut Chunk<CAP, Item, Gap>> {
286 let mut chunk = self.latest_chunk_mut();
287
288 loop {
289 if chunk.identifier() == identifier {
290 return Some(chunk);
291 }
292
293 chunk = chunk.previous_mut()?;
294 }
295 }
296
297 fn clear(&mut self) {
300 let mut current_chunk_ptr = self.last.or_else(|| self.first.get().copied());
303
304 while let Some(chunk_ptr) = current_chunk_ptr {
306 let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
308
309 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
311
312 current_chunk_ptr = previous_ptr;
314 }
315
316 self.first.take();
318 self.last = None;
319 }
320
321 unsafe fn replace_with(&mut self, first_chunk: NonNull<Chunk<CAP, Item, Gap>>) {
330 self.clear();
331
332 let mut first_chunk = Some(first_chunk);
335 self.first.get_or_init(|| first_chunk.take().unwrap());
336
337 if first_chunk.is_some() {
338 unreachable!(
339 "`first` must be initialised to `first_chunk` because `clear` has been called"
340 );
341 }
342 }
343}
344
345pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
352 links: Ends<CHUNK_CAPACITY, Item, Gap>,
354
355 chunk_identifier_generator: ChunkIdentifierGenerator,
357
358 updates: Option<ObservableUpdates<Item, Gap>>,
362
363 marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
365}
366
367impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
368 fn default() -> Self {
369 Self::new()
370 }
371}
372
373impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
374 pub fn new() -> Self {
376 let updates = None;
377
378 Self {
379 links: Ends::new(&updates),
380 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
381 updates,
382 marker: PhantomData,
383 }
384 }
385
386 pub fn new_with_update_history() -> Self {
392 let updates = Some(ObservableUpdates::new());
393
394 Self {
395 links: Ends::new(&updates),
396 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
397 updates,
398 marker: PhantomData,
399 }
400 }
401
402 pub fn clear(&mut self) {
404 self.links.clear();
406
407 self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
409
410 if let Some(updates) = self.updates.as_mut() {
412 updates.clear_pending();
415 updates.push(Update::Clear);
416 }
417 }
418
419 pub fn push_items_back<I>(&mut self, items: I)
424 where
425 Item: Clone,
426 Gap: Clone,
427 I: IntoIterator<Item = Item>,
428 I::IntoIter: ExactSizeIterator,
429 {
430 let items = items.into_iter();
431
432 let last_chunk = self.links.latest_chunk_mut();
433
434 let last_chunk =
436 last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
437
438 debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
439
440 if !last_chunk.is_first_chunk() {
444 self.links.last = Some(last_chunk.as_ptr());
447 }
448 }
449
450 pub fn push_gap_back(&mut self, content: Gap)
452 where
453 Item: Clone,
454 Gap: Clone,
455 {
456 let last_chunk = self.links.latest_chunk_mut();
457 last_chunk.insert_next(
458 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
459 &mut self.updates,
460 );
461
462 self.links.last = last_chunk.next;
463 }
464
465 pub fn insert_items_at<I>(&mut self, position: Position, items: I) -> Result<(), Error>
469 where
470 Item: Clone,
471 Gap: Clone,
472 I: IntoIterator<Item = Item>,
473 I::IntoIter: ExactSizeIterator,
474 {
475 let chunk_identifier = position.chunk_identifier();
476 let item_index = position.index();
477
478 let chunk = self
479 .links
480 .chunk_mut(chunk_identifier)
481 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
482
483 let chunk = match &mut chunk.content {
484 ChunkContent::Gap(..) => {
485 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
486 }
487
488 ChunkContent::Items(current_items) => {
489 let current_items_length = current_items.len();
490
491 if item_index > current_items_length {
492 return Err(Error::InvalidItemIndex { index: item_index });
493 }
494
495 let items = items.into_iter();
497
498 if item_index == current_items_length {
500 chunk
501 .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
503 }
504 else {
506 if let Some(updates) = self.updates.as_mut() {
507 updates.push(Update::DetachLastItems {
508 at: Position(chunk_identifier, item_index),
509 });
510 }
511
512 let detached_items = current_items.split_off(item_index);
514
515 let chunk = chunk
516 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
518
519 if let Some(updates) = self.updates.as_mut() {
520 updates.push(Update::StartReattachItems);
521 }
522
523 let chunk = chunk
524 .push_items(
526 detached_items.into_iter(),
527 &self.chunk_identifier_generator,
528 &mut self.updates,
529 );
530
531 if let Some(updates) = self.updates.as_mut() {
532 updates.push(Update::EndReattachItems);
533 }
534
535 chunk
536 }
537 }
538 };
539
540 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
543 self.links.last = Some(chunk.as_ptr());
546 }
547
548 Ok(())
549 }
550
551 pub fn remove_item_at(&mut self, position: Position) -> Result<Item, Error> {
559 let chunk_identifier = position.chunk_identifier();
560 let item_index = position.index();
561
562 let mut chunk_ptr = None;
563 let removed_item;
564
565 {
566 let chunk = self
567 .links
568 .chunk_mut(chunk_identifier)
569 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
570
571 let current_items = match &mut chunk.content {
572 ChunkContent::Gap(..) => {
573 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
574 }
575 ChunkContent::Items(current_items) => current_items,
576 };
577
578 if item_index >= current_items.len() {
579 return Err(Error::InvalidItemIndex { index: item_index });
580 }
581
582 removed_item = current_items.remove(item_index);
583
584 if let Some(updates) = self.updates.as_mut() {
585 updates.push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
586 }
587
588 if current_items.is_empty() && !chunk.is_first_chunk() {
590 chunk.unlink(self.updates.as_mut());
592
593 chunk_ptr = Some(chunk.as_ptr());
594
595 if chunk.is_last_chunk() {
599 self.links.last = chunk.previous;
600 }
601 }
602
603 }
605
606 if let Some(chunk_ptr) = chunk_ptr {
607 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
615 }
616
617 Ok(removed_item)
618 }
619
620 pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
625 where
626 Item: Clone,
627 {
628 let chunk_identifier = position.chunk_identifier();
629 let item_index = position.index();
630
631 let chunk = self
632 .links
633 .chunk_mut(chunk_identifier)
634 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
635
636 match &mut chunk.content {
637 ChunkContent::Gap(..) => {
638 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
639 }
640
641 ChunkContent::Items(current_items) => {
642 if item_index >= current_items.len() {
643 return Err(Error::InvalidItemIndex { index: item_index });
644 }
645
646 if let Some(updates) = self.updates.as_mut() {
649 updates.push(Update::ReplaceItem {
650 at: Position(chunk_identifier, item_index),
651 item: item.clone(),
652 });
653 }
654
655 current_items[item_index] = item;
656 }
657 }
658
659 Ok(())
660 }
661
662 pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
666 where
667 Item: Clone,
668 Gap: Clone,
669 {
670 let chunk_identifier = position.chunk_identifier();
671 let item_index = position.index();
672
673 let chunk = self
674 .links
675 .chunk_mut(chunk_identifier)
676 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
677
678 let chunk = match &mut chunk.content {
679 ChunkContent::Gap(..) => {
680 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
681 }
682
683 ChunkContent::Items(current_items) => {
684 if item_index == 0 {
689 let chunk_was_first = chunk.is_first_chunk();
690 let chunk_was_last = chunk.is_last_chunk();
691
692 let new_chunk = chunk.insert_before(
693 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
694 self.updates.as_mut(),
695 );
696
697 let new_chunk_ptr = new_chunk.as_ptr();
698 let chunk_ptr = chunk.as_ptr();
699
700 if chunk_was_first {
705 *self.links.first_chunk_mut_ptr() = new_chunk_ptr;
706
707 if chunk_was_last {
710 self.links.last = Some(chunk_ptr);
711 }
712 }
713
714 return Ok(());
715 }
716
717 let current_items_length = current_items.len();
718
719 if item_index >= current_items_length {
720 return Err(Error::InvalidItemIndex { index: item_index });
721 }
722
723 if let Some(updates) = self.updates.as_mut() {
724 updates.push(Update::DetachLastItems {
725 at: Position(chunk_identifier, item_index),
726 });
727 }
728
729 let detached_items = current_items.split_off(item_index);
731
732 let chunk = chunk
733 .insert_next(
735 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
736 &mut self.updates,
737 );
738
739 if let Some(updates) = self.updates.as_mut() {
740 updates.push(Update::StartReattachItems);
741 }
742
743 let chunk = chunk
744 .insert_next(
746 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
747 &mut self.updates,
748 )
749 .push_items(
751 detached_items.into_iter(),
752 &self.chunk_identifier_generator,
753 &mut self.updates,
754 );
755
756 if let Some(updates) = self.updates.as_mut() {
757 updates.push(Update::EndReattachItems);
758 }
759
760 chunk
761 }
762 };
763
764 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
767 self.links.last = Some(chunk.as_ptr());
770 }
771
772 Ok(())
773 }
774
775 pub fn remove_empty_chunk_at(
785 &mut self,
786 chunk_identifier: ChunkIdentifier,
787 ) -> Result<Option<Position>, Error> {
788 if self.links.first_chunk().is_last_chunk() {
790 return Err(Error::RemovingLastChunk);
791 }
792
793 let chunk = self
794 .links
795 .chunk_mut(chunk_identifier)
796 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
797
798 if chunk.num_items() > 0 {
799 return Err(Error::RemovingNonEmptyItemsChunk { identifier: chunk_identifier });
800 }
801
802 let chunk_was_first = chunk.is_first_chunk();
803 let chunk_was_last = chunk.is_last_chunk();
804 let next_ptr = chunk.next;
805 let previous_ptr = chunk.previous;
806 let position_of_next = chunk.next().map(|next| next.first_position());
807
808 chunk.unlink(self.updates.as_mut());
809
810 let chunk_ptr = chunk.as_ptr();
811
812 if chunk_was_first {
814 if let Some(next_ptr) = next_ptr {
816 *self.links.first_chunk_mut_ptr() = next_ptr;
817 }
818 }
819
820 if chunk_was_last {
821 self.links.last = previous_ptr;
822 }
823
824 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
828
829 Ok(position_of_next)
831 }
832
833 pub fn replace_gap_at<I>(
841 &mut self,
842 items: I,
843 chunk_identifier: ChunkIdentifier,
844 ) -> Result<&Chunk<CAP, Item, Gap>, Error>
845 where
846 Item: Clone,
847 Gap: Clone,
848 I: IntoIterator<Item = Item>,
849 I::IntoIter: ExactSizeIterator,
850 {
851 let chunk_ptr;
852 let new_chunk_ptr;
853
854 {
855 let chunk = self
856 .links
857 .chunk_mut(chunk_identifier)
858 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
859
860 if chunk.is_items() {
861 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
862 }
863
864 let chunk_was_first = chunk.is_first_chunk();
865
866 let maybe_last_chunk_ptr = {
867 let items = items.into_iter();
868
869 let last_inserted_chunk = chunk
870 .insert_next(
872 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
873 &mut self.updates,
874 )
875 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
877
878 last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
879 };
880
881 new_chunk_ptr = chunk
882 .next
883 .unwrap();
885
886 chunk.unlink(self.updates.as_mut());
888
889 chunk_ptr = chunk.as_ptr();
891
892 if chunk_was_first {
894 *self.links.first_chunk_mut_ptr() = new_chunk_ptr;
895 }
896
897 if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
900 self.links.last = Some(last_chunk_ptr);
901 }
902
903 }
905
906 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
912
913 Ok(
914 unsafe { new_chunk_ptr.as_ref() },
919 )
920 }
921
922 pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
924 where
925 P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
926 {
927 self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
928 }
929
930 pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
932 where
933 P: FnMut(&'a Item) -> bool,
934 {
935 self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
936 }
937
938 pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
942 IterBackward::new(self.links.latest_chunk())
943 }
944
945 pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
949 Iter::new(self.links.first_chunk())
950 }
951
952 pub fn rchunks_from(
957 &self,
958 identifier: ChunkIdentifier,
959 ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
960 Ok(IterBackward::new(
961 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
962 ))
963 }
964
965 pub fn chunks_from(
970 &self,
971 identifier: ChunkIdentifier,
972 ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
973 Ok(Iter::new(
974 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
975 ))
976 }
977
978 pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
982 self.ritems_from(self.links.latest_chunk().last_position())
983 .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
984 }
985
986 pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
990 let first_chunk = self.links.first_chunk();
991
992 self.items_from(first_chunk.first_position())
993 .expect("`items` cannot fail because at least one empty chunk must exist")
994 }
995
996 pub fn ritems_from(
1000 &self,
1001 position: Position,
1002 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1003 Ok(self
1004 .rchunks_from(position.chunk_identifier())?
1005 .filter_map(|chunk| match &chunk.content {
1006 ChunkContent::Gap(..) => None,
1007 ChunkContent::Items(items) => {
1008 let identifier = chunk.identifier();
1009
1010 Some(
1011 items.iter().enumerate().rev().map(move |(item_index, item)| {
1012 (Position(identifier, item_index), item)
1013 }),
1014 )
1015 }
1016 })
1017 .flatten()
1018 .skip_while({
1019 let expected_index = position.index();
1020
1021 move |(Position(chunk_identifier, item_index), _item)| {
1022 *chunk_identifier == position.chunk_identifier()
1023 && *item_index != expected_index
1024 }
1025 }))
1026 }
1027
1028 pub fn items_from(
1032 &self,
1033 position: Position,
1034 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
1035 Ok(self
1036 .chunks_from(position.chunk_identifier())?
1037 .filter_map(|chunk| match &chunk.content {
1038 ChunkContent::Gap(..) => None,
1039 ChunkContent::Items(items) => {
1040 let identifier = chunk.identifier();
1041
1042 Some(
1043 items.iter().enumerate().map(move |(item_index, item)| {
1044 (Position(identifier, item_index), item)
1045 }),
1046 )
1047 }
1048 })
1049 .flatten()
1050 .skip(position.index()))
1051 }
1052
1053 pub fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
1055 self.links.first_chunk()
1056 }
1057
1058 #[must_use]
1070 pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
1071 self.updates.as_mut()
1072 }
1073
1074 pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
1081 let (updates, token) = self
1082 .updates
1083 .as_mut()
1084 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1085 let chunk_iterator = self.chunks();
1086
1087 Some(AsVector::new(updates, token, chunk_iterator))
1088 }
1089
1090 pub fn order_tracker(
1098 &mut self,
1099 all_chunks: Option<Vec<ChunkMetadata>>,
1100 ) -> Option<OrderTracker<Item, Gap>>
1101 where
1102 Item: Clone,
1103 {
1104 let (updates, token) = self
1105 .updates
1106 .as_mut()
1107 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
1108
1109 Some(OrderTracker::new(
1110 updates,
1111 token,
1112 all_chunks.unwrap_or_else(|| {
1113 self.chunks()
1115 .map(|chunk| ChunkMetadata {
1116 identifier: chunk.identifier(),
1117 num_items: chunk.num_items(),
1118 previous: chunk.previous().map(|prev| prev.identifier()),
1119 next: chunk.next().map(|next| next.identifier()),
1120 })
1121 .collect()
1122 }),
1123 ))
1124 }
1125
1126 pub fn num_items(&self) -> usize {
1128 self.items().count()
1129 }
1130}
1131
1132impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
1133 fn drop(&mut self) {
1134 self.links.clear();
1142 }
1143}
1144
1145unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1149
1150unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1154
1155#[derive(Debug)]
1165pub struct ChunkIdentifierGenerator {
1166 next: AtomicU64,
1167}
1168
1169impl ChunkIdentifierGenerator {
1170 const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1172
1173 pub fn new_from_scratch() -> Self {
1176 Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1177 }
1178
1179 pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1182 Self { next: AtomicU64::new(last_chunk_identifier.0) }
1183 }
1184
1185 fn next(&self) -> ChunkIdentifier {
1190 let previous = self.next.fetch_add(1, atomic::Ordering::Relaxed);
1191
1192 if previous == u64::MAX {
1195 panic!(
1196 "No more chunk identifiers available. Congrats, you did it. \
1197 2^64 identifiers have been consumed."
1198 )
1199 }
1200
1201 ChunkIdentifier(previous + 1)
1202 }
1203
1204 #[doc(hidden)]
1208 pub fn current(&self) -> ChunkIdentifier {
1209 ChunkIdentifier(self.next.load(atomic::Ordering::Relaxed))
1210 }
1211}
1212
1213#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1219#[repr(transparent)]
1220pub struct ChunkIdentifier(u64);
1221
1222impl ChunkIdentifier {
1223 pub fn new(identifier: u64) -> Self {
1225 Self(identifier)
1226 }
1227
1228 pub fn index(&self) -> u64 {
1230 self.0
1231 }
1232}
1233
1234impl PartialEq<u64> for ChunkIdentifier {
1235 fn eq(&self, other: &u64) -> bool {
1236 self.0 == *other
1237 }
1238}
1239
1240#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1244pub struct Position(ChunkIdentifier, usize);
1245
1246impl Position {
1247 pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1249 Self(chunk_identifier, index)
1250 }
1251
1252 pub fn chunk_identifier(&self) -> ChunkIdentifier {
1254 self.0
1255 }
1256
1257 pub fn index(&self) -> usize {
1259 self.1
1260 }
1261
1262 pub fn decrement_index(&mut self) {
1268 self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1269 }
1270
1271 pub fn increment_index(&mut self) {
1278 self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1279 }
1280}
1281
1282#[derive(Debug)]
1285pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1286 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1287}
1288
1289impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1290 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1292 Self { chunk: Some(from_chunk) }
1293 }
1294}
1295
1296impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1297 type Item = &'a Chunk<CAP, Item, Gap>;
1298
1299 fn next(&mut self) -> Option<Self::Item> {
1300 self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1301 }
1302}
1303
1304#[derive(Debug)]
1307pub struct Iter<'a, const CAP: usize, Item, Gap> {
1308 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1309}
1310
1311impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1312 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1314 Self { chunk: Some(from_chunk) }
1315 }
1316}
1317
1318impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1319 type Item = &'a Chunk<CAP, Item, Gap>;
1320
1321 fn next(&mut self) -> Option<Self::Item> {
1322 self.chunk.inspect(|chunk| self.chunk = chunk.next())
1323 }
1324}
1325
1326#[derive(Clone, Debug)]
1328pub enum ChunkContent<Item, Gap> {
1329 Gap(Gap),
1332
1333 Items(Vec<Item>),
1335}
1336
1337pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1339 previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1341
1342 lazy_previous: Option<ChunkIdentifier>,
1347
1348 next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1350
1351 identifier: ChunkIdentifier,
1353
1354 content: ChunkContent<Item, Gap>,
1356}
1357
1358impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1359 fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1361 Self::new(identifier, ChunkContent::Gap(content))
1362 }
1363
1364 fn new_items(identifier: ChunkIdentifier) -> Self {
1366 Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1367 }
1368
1369 fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1370 Self { previous: None, lazy_previous: None, next: None, identifier, content }
1371 }
1372
1373 fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1375 let chunk = Self::new(identifier, content);
1376 let chunk_box = Box::new(chunk);
1377
1378 NonNull::from(Box::leak(chunk_box))
1379 }
1380
1381 fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1383 let chunk = Self::new_gap(identifier, content);
1384 let chunk_box = Box::new(chunk);
1385
1386 NonNull::from(Box::leak(chunk_box))
1387 }
1388
1389 fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1391 let chunk = Self::new_items(identifier);
1392 let chunk_box = Box::new(chunk);
1393
1394 NonNull::from(Box::leak(chunk_box))
1395 }
1396
1397 pub fn as_ptr(&self) -> NonNull<Self> {
1399 NonNull::from(self)
1400 }
1401
1402 pub fn is_gap(&self) -> bool {
1404 matches!(self.content, ChunkContent::Gap(..))
1405 }
1406
1407 pub fn is_items(&self) -> bool {
1409 !self.is_gap()
1410 }
1411
1412 pub fn is_definitive_head(&self) -> bool {
1415 self.previous.is_none() && self.lazy_previous.is_none()
1416 }
1417
1418 fn is_first_chunk(&self) -> bool {
1420 self.previous.is_none()
1421 }
1422
1423 fn is_last_chunk(&self) -> bool {
1425 self.next.is_none()
1426 }
1427
1428 #[doc(hidden)]
1432 pub fn lazy_previous(&self) -> Option<ChunkIdentifier> {
1433 self.lazy_previous
1434 }
1435
1436 pub fn identifier(&self) -> ChunkIdentifier {
1438 self.identifier
1439 }
1440
1441 pub fn content(&self) -> &ChunkContent<Item, Gap> {
1443 &self.content
1444 }
1445
1446 pub fn first_position(&self) -> Position {
1450 Position(self.identifier(), 0)
1451 }
1452
1453 pub fn last_position(&self) -> Position {
1457 let identifier = self.identifier();
1458
1459 match &self.content {
1460 ChunkContent::Gap(..) => Position(identifier, 0),
1461 ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1462 }
1463 }
1464
1465 pub fn num_items(&self) -> usize {
1469 match &self.content {
1470 ChunkContent::Gap(..) => 0,
1471 ChunkContent::Items(items) => items.len(),
1472 }
1473 }
1474
1475 fn push_items<I>(
1487 &mut self,
1488 mut new_items: I,
1489 chunk_identifier_generator: &ChunkIdentifierGenerator,
1490 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1491 ) -> &mut Self
1492 where
1493 I: Iterator<Item = Item> + ExactSizeIterator,
1494 Item: Clone,
1495 Gap: Clone,
1496 {
1497 if new_items.len() == 0 {
1499 return self;
1500 }
1501
1502 let identifier = self.identifier();
1503 let prev_num_items = self.num_items();
1504
1505 match &mut self.content {
1506 ChunkContent::Gap(..) => {
1509 self
1510 .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1512 .push_items(new_items, chunk_identifier_generator, updates)
1515 }
1516
1517 ChunkContent::Items(items) => {
1518 let free_space = CAPACITY.saturating_sub(prev_num_items);
1520
1521 if new_items.len() <= free_space {
1523 let start = items.len();
1524 items.extend(new_items);
1525
1526 if let Some(updates) = updates.as_mut() {
1527 updates.push(Update::PushItems {
1528 at: Position(identifier, start),
1529 items: items[start..].to_vec(),
1530 });
1531 }
1532
1533 self
1535 } else {
1536 if free_space > 0 {
1537 let start = items.len();
1539 items.extend(new_items.by_ref().take(free_space));
1540
1541 if let Some(updates) = updates.as_mut() {
1542 updates.push(Update::PushItems {
1543 at: Position(identifier, start),
1544 items: items[start..].to_vec(),
1545 });
1546 }
1547 }
1548
1549 self
1550 .insert_next(
1552 Self::new_items_leaked(chunk_identifier_generator.next()),
1553 updates,
1554 )
1555 .push_items(new_items, chunk_identifier_generator, updates)
1558 }
1559 }
1560 }
1561 }
1562
1563 fn insert_next(
1568 &mut self,
1569 mut new_chunk_ptr: NonNull<Self>,
1570 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1571 ) -> &mut Self
1572 where
1573 Gap: Clone,
1574 {
1575 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1576
1577 if let Some(next_chunk) = self.next_mut() {
1579 next_chunk.previous = Some(new_chunk_ptr);
1581
1582 new_chunk.next = self.next;
1584 }
1585
1586 self.next = Some(new_chunk_ptr);
1588 new_chunk.previous = Some(self.as_ptr());
1590
1591 if let Some(updates) = updates.as_mut() {
1592 let previous = new_chunk.previous().map(Chunk::identifier);
1593 let new = new_chunk.identifier();
1594 let next = new_chunk.next().map(Chunk::identifier);
1595
1596 match new_chunk.content() {
1597 ChunkContent::Gap(gap) => {
1598 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1599 }
1600
1601 ChunkContent::Items(..) => {
1602 updates.push(Update::NewItemsChunk { previous, new, next })
1603 }
1604 }
1605 }
1606
1607 new_chunk
1608 }
1609
1610 fn insert_before(
1615 &mut self,
1616 mut new_chunk_ptr: NonNull<Self>,
1617 updates: Option<&mut ObservableUpdates<Item, Gap>>,
1618 ) -> &mut Self
1619 where
1620 Gap: Clone,
1621 {
1622 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1623
1624 if let Some(previous_chunk) = self.previous_mut() {
1626 previous_chunk.next = Some(new_chunk_ptr);
1628
1629 new_chunk.previous = self.previous;
1631 }
1632 else {
1635 new_chunk.lazy_previous = self.lazy_previous.take();
1636 }
1637
1638 self.previous = Some(new_chunk_ptr);
1640 new_chunk.next = Some(self.as_ptr());
1642
1643 if let Some(updates) = updates {
1644 let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1645 let new = new_chunk.identifier();
1646 let next = new_chunk.next().map(Chunk::identifier);
1647
1648 match new_chunk.content() {
1649 ChunkContent::Gap(gap) => {
1650 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1651 }
1652
1653 ChunkContent::Items(..) => {
1654 updates.push(Update::NewItemsChunk { previous, new, next })
1655 }
1656 }
1657 }
1658
1659 new_chunk
1660 }
1661
1662 fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1667 let previous_ptr = self.previous;
1668 let next_ptr = self.next;
1669 let lazy_previous = self.lazy_previous.take();
1673
1674 if let Some(previous) = self.previous_mut() {
1675 previous.next = next_ptr;
1676 }
1677
1678 if let Some(next) = self.next_mut() {
1679 next.previous = previous_ptr;
1680 next.lazy_previous = lazy_previous;
1681 }
1682
1683 if let Some(updates) = updates {
1684 updates.push(Update::RemoveChunk(self.identifier()));
1685 }
1686 }
1687
1688 fn previous(&self) -> Option<&Self> {
1690 self.previous.map(|non_null| unsafe { non_null.as_ref() })
1691 }
1692
1693 fn previous_mut(&mut self) -> Option<&mut Self> {
1695 self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1696 }
1697
1698 fn next(&self) -> Option<&Self> {
1700 self.next.map(|non_null| unsafe { non_null.as_ref() })
1701 }
1702
1703 fn next_mut(&mut self) -> Option<&mut Self> {
1705 self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1706 }
1707}
1708
1709impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<CAP, Item, Gap>
1710where
1711 Item: fmt::Debug,
1712 Gap: fmt::Debug,
1713{
1714 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1715 formatter
1716 .debug_struct("LinkedChunk")
1717 .field("first (deref)", self.links.first_chunk())
1718 .field("last", &self.links.last)
1719 .finish_non_exhaustive()
1720 }
1721}
1722
1723impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1724where
1725 Item: fmt::Debug,
1726 Gap: fmt::Debug,
1727{
1728 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1729 formatter
1730 .debug_struct("Chunk")
1731 .field("identifier", &self.identifier)
1732 .field("content", &self.content)
1733 .field("previous", &self.previous)
1734 .field("ptr", &std::ptr::from_ref(self))
1735 .field("next", &self.next)
1736 .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1737 .finish()
1738 }
1739}
1740
1741#[derive(Clone, Debug)]
1747pub struct RawChunk<Item, Gap> {
1748 pub content: ChunkContent<Item, Gap>,
1750
1751 pub previous: Option<ChunkIdentifier>,
1753
1754 pub identifier: ChunkIdentifier,
1756
1757 pub next: Option<ChunkIdentifier>,
1759}
1760
1761#[derive(Clone, Debug)]
1764pub struct ChunkMetadata {
1765 pub num_items: usize,
1769
1770 pub previous: Option<ChunkIdentifier>,
1772
1773 pub identifier: ChunkIdentifier,
1775
1776 pub next: Option<ChunkIdentifier>,
1778}
1779
1780#[cfg(test)]
1781mod tests {
1782 use std::{
1783 ops::Not,
1784 sync::{Arc, atomic::Ordering},
1785 };
1786
1787 use assert_matches::assert_matches;
1788
1789 use super::{
1790 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Error, LinkedChunk,
1791 Position, Update::*,
1792 };
1793
1794 #[test]
1795 fn test_chunk_identifier_generator() {
1796 let generator = ChunkIdentifierGenerator::new_from_scratch();
1797
1798 assert_eq!(generator.next(), ChunkIdentifier(1));
1799 assert_eq!(generator.next(), ChunkIdentifier(2));
1800 assert_eq!(generator.next(), ChunkIdentifier(3));
1801 assert_eq!(generator.next(), ChunkIdentifier(4));
1802
1803 let generator =
1804 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1805
1806 assert_eq!(generator.next(), ChunkIdentifier(43));
1807 assert_eq!(generator.next(), ChunkIdentifier(44));
1808 assert_eq!(generator.next(), ChunkIdentifier(45));
1809 assert_eq!(generator.next(), ChunkIdentifier(46));
1810 }
1811
1812 #[test]
1813 fn test_empty() {
1814 let items = LinkedChunk::<3, char, ()>::new();
1815
1816 assert_eq!(items.num_items(), 0);
1817
1818 }
1821
1822 #[test]
1823 fn test_updates() {
1824 assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1825 assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1826 }
1827
1828 #[test]
1829 fn test_new_with_initial_update() {
1830 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1831
1832 assert!(linked_chunk.updates().unwrap().take().is_empty());
1834
1835 let _ = linked_chunk.first_chunk();
1837
1838 assert_eq!(
1839 linked_chunk.updates().unwrap().take(),
1840 &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1841 );
1842 }
1843
1844 #[test]
1845 fn test_push_items() {
1846 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1847
1848 linked_chunk.push_items_back(['a']);
1849
1850 assert_items_eq!(linked_chunk, ['a']);
1851 assert_eq!(
1852 linked_chunk.updates().unwrap().take(),
1853 &[
1854 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
1855 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }
1856 ]
1857 );
1858
1859 linked_chunk.push_items_back(['b', 'c']);
1860 assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1861 assert_eq!(
1862 linked_chunk.updates().unwrap().take(),
1863 &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1864 );
1865
1866 linked_chunk.push_items_back(['d', 'e']);
1867 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
1868 assert_eq!(
1869 linked_chunk.updates().unwrap().take(),
1870 &[
1871 NewItemsChunk {
1872 previous: Some(ChunkIdentifier(0)),
1873 new: ChunkIdentifier(1),
1874 next: None
1875 },
1876 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1877 ]
1878 );
1879
1880 linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1881 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1882 assert_eq!(
1883 linked_chunk.updates().unwrap().take(),
1884 &[
1885 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1886 NewItemsChunk {
1887 previous: Some(ChunkIdentifier(1)),
1888 new: ChunkIdentifier(2),
1889 next: None,
1890 },
1891 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1892 NewItemsChunk {
1893 previous: Some(ChunkIdentifier(2)),
1894 new: ChunkIdentifier(3),
1895 next: None,
1896 },
1897 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1898 ]
1899 );
1900
1901 assert_eq!(linked_chunk.num_items(), 10);
1902 }
1903
1904 #[test]
1905 fn test_push_gap() {
1906 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1907
1908 linked_chunk.push_items_back(['a']);
1909 assert_items_eq!(linked_chunk, ['a']);
1910 assert_eq!(
1911 linked_chunk.updates().unwrap().take(),
1912 &[
1913 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
1914 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }
1915 ]
1916 );
1917
1918 linked_chunk.push_gap_back(());
1919 assert_items_eq!(linked_chunk, ['a'] [-]);
1920 assert_eq!(
1921 linked_chunk.updates().unwrap().take(),
1922 &[NewGapChunk {
1923 previous: Some(ChunkIdentifier(0)),
1924 new: ChunkIdentifier(1),
1925 next: None,
1926 gap: (),
1927 }]
1928 );
1929
1930 linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1931 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1932 assert_eq!(
1933 linked_chunk.updates().unwrap().take(),
1934 &[
1935 NewItemsChunk {
1936 previous: Some(ChunkIdentifier(1)),
1937 new: ChunkIdentifier(2),
1938 next: None,
1939 },
1940 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
1941 NewItemsChunk {
1942 previous: Some(ChunkIdentifier(2)),
1943 new: ChunkIdentifier(3),
1944 next: None,
1945 },
1946 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
1947 ]
1948 );
1949
1950 linked_chunk.push_gap_back(());
1951 linked_chunk.push_gap_back(()); assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
1953 assert_eq!(
1954 linked_chunk.updates().unwrap().take(),
1955 &[
1956 NewGapChunk {
1957 previous: Some(ChunkIdentifier(3)),
1958 new: ChunkIdentifier(4),
1959 next: None,
1960 gap: (),
1961 },
1962 NewGapChunk {
1963 previous: Some(ChunkIdentifier(4)),
1964 new: ChunkIdentifier(5),
1965 next: None,
1966 gap: (),
1967 }
1968 ]
1969 );
1970
1971 linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
1972 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
1973 assert_eq!(
1974 linked_chunk.updates().unwrap().take(),
1975 &[
1976 NewItemsChunk {
1977 previous: Some(ChunkIdentifier(5)),
1978 new: ChunkIdentifier(6),
1979 next: None,
1980 },
1981 PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
1982 NewItemsChunk {
1983 previous: Some(ChunkIdentifier(6)),
1984 new: ChunkIdentifier(7),
1985 next: None,
1986 },
1987 PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
1988 ]
1989 );
1990
1991 assert_eq!(linked_chunk.num_items(), 9);
1992 }
1993
1994 #[test]
1995 fn test_identifiers_and_positions() {
1996 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
1997 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
1998 linked_chunk.push_gap_back(());
1999 linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
2000 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
2001
2002 assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
2003 assert_eq!(
2004 linked_chunk.item_position(|item| *item == 'e'),
2005 Some(Position(ChunkIdentifier(1), 1))
2006 );
2007 }
2008
2009 #[test]
2010 fn test_rchunks() {
2011 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2012 linked_chunk.push_items_back(['a', 'b']);
2013 linked_chunk.push_gap_back(());
2014 linked_chunk.push_items_back(['c', 'd', 'e']);
2015
2016 let mut iterator = linked_chunk.rchunks();
2017
2018 assert_matches!(
2019 iterator.next(),
2020 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2021 assert_eq!(items, &['e']);
2022 }
2023 );
2024 assert_matches!(
2025 iterator.next(),
2026 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2027 assert_eq!(items, &['c', 'd']);
2028 }
2029 );
2030 assert_matches!(
2031 iterator.next(),
2032 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2033 );
2034 assert_matches!(
2035 iterator.next(),
2036 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2037 assert_eq!(items, &['a', 'b']);
2038 }
2039 );
2040 assert_matches!(iterator.next(), None);
2041 }
2042
2043 #[test]
2044 fn test_chunks() {
2045 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2046 linked_chunk.push_items_back(['a', 'b']);
2047 linked_chunk.push_gap_back(());
2048 linked_chunk.push_items_back(['c', 'd', 'e']);
2049
2050 let mut iterator = linked_chunk.chunks();
2051
2052 assert_matches!(
2053 iterator.next(),
2054 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2055 assert_eq!(items, &['a', 'b']);
2056 }
2057 );
2058 assert_matches!(
2059 iterator.next(),
2060 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2061 );
2062 assert_matches!(
2063 iterator.next(),
2064 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2065 assert_eq!(items, &['c', 'd']);
2066 }
2067 );
2068 assert_matches!(
2069 iterator.next(),
2070 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2071 assert_eq!(items, &['e']);
2072 }
2073 );
2074 assert_matches!(iterator.next(), None);
2075 }
2076
2077 #[test]
2078 fn test_rchunks_from() -> Result<(), Error> {
2079 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2080 linked_chunk.push_items_back(['a', 'b']);
2081 linked_chunk.push_gap_back(());
2082 linked_chunk.push_items_back(['c', 'd', 'e']);
2083
2084 let mut iterator = linked_chunk.rchunks_from(
2085 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2086 )?;
2087
2088 assert_matches!(
2089 iterator.next(),
2090 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2091 assert_eq!(items, &['c', 'd']);
2092 }
2093 );
2094 assert_matches!(
2095 iterator.next(),
2096 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
2097 );
2098 assert_matches!(
2099 iterator.next(),
2100 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
2101 assert_eq!(items, &['a', 'b']);
2102 }
2103 );
2104 assert_matches!(iterator.next(), None);
2105
2106 Ok(())
2107 }
2108
2109 #[test]
2110 fn test_chunks_from() -> Result<(), Error> {
2111 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2112 linked_chunk.push_items_back(['a', 'b']);
2113 linked_chunk.push_gap_back(());
2114 linked_chunk.push_items_back(['c', 'd', 'e']);
2115
2116 let mut iterator = linked_chunk.chunks_from(
2117 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
2118 )?;
2119
2120 assert_matches!(
2121 iterator.next(),
2122 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
2123 assert_eq!(items, &['c', 'd']);
2124 }
2125 );
2126 assert_matches!(
2127 iterator.next(),
2128 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
2129 assert_eq!(items, &['e']);
2130 }
2131 );
2132 assert_matches!(iterator.next(), None);
2133
2134 Ok(())
2135 }
2136
2137 #[test]
2138 fn test_ritems() {
2139 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2140 linked_chunk.push_items_back(['a', 'b']);
2141 linked_chunk.push_gap_back(());
2142 linked_chunk.push_items_back(['c', 'd', 'e']);
2143
2144 let mut iterator = linked_chunk.ritems();
2145
2146 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2147 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2148 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2149 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2150 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2151 assert_matches!(iterator.next(), None);
2152 }
2153
2154 #[test]
2155 fn test_ritems_with_final_gap() -> Result<(), Error> {
2156 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2157 linked_chunk.push_items_back(['a', 'b']);
2158 linked_chunk.push_gap_back(());
2159 linked_chunk.push_items_back(['c', 'd', 'e']);
2160 linked_chunk.push_gap_back(());
2161
2162 let mut iterator = linked_chunk.ritems();
2163
2164 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
2165 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2166 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2167 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2168 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2169 assert_matches!(iterator.next(), None);
2170
2171 Ok(())
2172 }
2173
2174 #[test]
2175 fn test_ritems_empty() {
2176 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2177 let mut iterator = linked_chunk.ritems();
2178
2179 assert_matches!(iterator.next(), None);
2180 }
2181
2182 #[test]
2183 fn test_items() {
2184 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2185 linked_chunk.push_items_back(['a', 'b']);
2186 linked_chunk.push_gap_back(());
2187 linked_chunk.push_items_back(['c', 'd', 'e']);
2188
2189 let mut iterator = linked_chunk.items();
2190
2191 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2192 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2193 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2194 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2195 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2196 assert_matches!(iterator.next(), None);
2197 }
2198
2199 #[test]
2200 fn test_items_empty() {
2201 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2202 let mut iterator = linked_chunk.items();
2203
2204 assert_matches!(iterator.next(), None);
2205 }
2206
2207 #[test]
2208 fn test_ritems_from() -> Result<(), Error> {
2209 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2210 linked_chunk.push_items_back(['a', 'b']);
2211 linked_chunk.push_gap_back(());
2212 linked_chunk.push_items_back(['c', 'd', 'e']);
2213
2214 let mut iterator =
2215 linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2216
2217 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2218 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2219 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2220 assert_matches!(iterator.next(), None);
2221
2222 Ok(())
2223 }
2224
2225 #[test]
2226 fn test_items_from() -> Result<(), Error> {
2227 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2228 linked_chunk.push_items_back(['a', 'b']);
2229 linked_chunk.push_gap_back(());
2230 linked_chunk.push_items_back(['c', 'd', 'e']);
2231
2232 let mut iterator =
2233 linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2234
2235 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2236 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2237 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2238 assert_matches!(iterator.next(), None);
2239
2240 Ok(())
2241 }
2242
2243 #[test]
2244 fn test_insert_items_at() -> Result<(), Error> {
2245 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2246
2247 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2248 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2249 assert_eq!(
2250 linked_chunk.updates().unwrap().take(),
2251 &[
2252 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2253 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2254 NewItemsChunk {
2255 previous: Some(ChunkIdentifier(0)),
2256 new: ChunkIdentifier(1),
2257 next: None,
2258 },
2259 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2260 ]
2261 );
2262
2263 {
2265 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2266
2267 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2270
2271 assert_items_eq!(
2272 linked_chunk,
2273 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2274 );
2275 assert_eq!(linked_chunk.num_items(), 10);
2276 assert_eq!(
2277 linked_chunk.updates().unwrap().take(),
2278 &[
2279 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2280 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2281 NewItemsChunk {
2282 previous: Some(ChunkIdentifier(1)),
2283 new: ChunkIdentifier(2),
2284 next: None,
2285 },
2286 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2287 StartReattachItems,
2288 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2289 NewItemsChunk {
2290 previous: Some(ChunkIdentifier(2)),
2291 new: ChunkIdentifier(3),
2292 next: None,
2293 },
2294 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2295 EndReattachItems,
2296 ]
2297 );
2298 }
2299
2300 {
2302 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2303 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2304
2305 assert_items_eq!(
2306 linked_chunk,
2307 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2308 );
2309 assert_eq!(linked_chunk.num_items(), 14);
2310 assert_eq!(
2311 linked_chunk.updates().unwrap().take(),
2312 &[
2313 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2314 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2315 NewItemsChunk {
2316 previous: Some(ChunkIdentifier(0)),
2317 new: ChunkIdentifier(4),
2318 next: Some(ChunkIdentifier(1)),
2319 },
2320 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2321 StartReattachItems,
2322 PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2323 NewItemsChunk {
2324 previous: Some(ChunkIdentifier(4)),
2325 new: ChunkIdentifier(5),
2326 next: Some(ChunkIdentifier(1)),
2327 },
2328 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2329 EndReattachItems,
2330 ]
2331 );
2332 }
2333
2334 {
2336 let pos_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2337 linked_chunk.insert_items_at(pos_c, ['r', 's'])?;
2338
2339 assert_items_eq!(
2340 linked_chunk,
2341 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2342 );
2343 assert_eq!(linked_chunk.num_items(), 16);
2344 assert_eq!(
2345 linked_chunk.updates().unwrap().take(),
2346 &[
2347 DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2348 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2349 StartReattachItems,
2350 PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2351 EndReattachItems,
2352 ]
2353 );
2354 }
2355
2356 {
2358 let pos_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2359 let pos_f = Position(pos_f.chunk_identifier(), pos_f.index() + 1);
2360
2361 linked_chunk.insert_items_at(pos_f, ['p', 'q'])?;
2362 assert_items_eq!(
2363 linked_chunk,
2364 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2365 );
2366 assert_eq!(
2367 linked_chunk.updates().unwrap().take(),
2368 &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2369 );
2370 assert_eq!(linked_chunk.num_items(), 18);
2371 }
2372
2373 {
2375 assert_matches!(
2376 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2377 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2378 );
2379 assert!(linked_chunk.updates().unwrap().take().is_empty());
2380 }
2381
2382 {
2384 assert_matches!(
2385 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2386 Err(Error::InvalidItemIndex { index: 128 })
2387 );
2388 assert!(linked_chunk.updates().unwrap().take().is_empty());
2389 }
2390
2391 {
2393 linked_chunk.push_gap_back(());
2395 assert_items_eq!(
2396 linked_chunk,
2397 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2398 );
2399 assert_eq!(
2400 linked_chunk.updates().unwrap().take(),
2401 &[NewGapChunk {
2402 previous: Some(ChunkIdentifier(3)),
2403 new: ChunkIdentifier(6),
2404 next: None,
2405 gap: ()
2406 }]
2407 );
2408
2409 assert_matches!(
2410 linked_chunk.insert_items_at(Position(ChunkIdentifier(6), 0), ['u', 'v'],),
2411 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2412 );
2413 }
2414
2415 assert_eq!(linked_chunk.num_items(), 18);
2416
2417 Ok(())
2418 }
2419
2420 #[test]
2421 fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2422 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2423
2424 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2425 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2426 assert_eq!(
2427 linked_chunk.updates().unwrap().take(),
2428 &[
2429 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2430 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2431 NewItemsChunk {
2432 previous: Some(ChunkIdentifier(0)),
2433 new: ChunkIdentifier(1),
2434 next: None,
2435 },
2436 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2437 ]
2438 );
2439
2440 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2442
2443 linked_chunk.insert_items_at(pos_e, ['w', 'x', 'y', 'z'])?;
2446
2447 assert_items_eq!(
2448 linked_chunk,
2449 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2450 );
2451 assert_eq!(linked_chunk.num_items(), 10);
2452 assert_eq!(
2453 linked_chunk.updates().unwrap().take(),
2454 &[
2455 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2456 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2457 NewItemsChunk {
2458 previous: Some(ChunkIdentifier(1)),
2459 new: ChunkIdentifier(2),
2460 next: None,
2461 },
2462 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2463 StartReattachItems,
2464 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2465 NewItemsChunk {
2466 previous: Some(ChunkIdentifier(2)),
2467 new: ChunkIdentifier(3),
2468 next: None,
2469 },
2470 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2471 EndReattachItems,
2472 ]
2473 );
2474
2475 Ok(())
2476 }
2477
2478 #[test]
2479 fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2480 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2481
2482 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2483 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2484 assert_eq!(
2485 linked_chunk.updates().unwrap().take(),
2486 &[
2487 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2488 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2489 NewItemsChunk {
2490 previous: Some(ChunkIdentifier(0)),
2491 new: ChunkIdentifier(1),
2492 next: None,
2493 },
2494 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2495 ]
2496 );
2497
2498 let pos_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2500 linked_chunk.insert_items_at(pos_a, ['l', 'm', 'n', 'o'])?;
2501
2502 assert_items_eq!(
2503 linked_chunk,
2504 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'e', 'f']
2505 );
2506 assert_eq!(linked_chunk.num_items(), 10);
2507 assert_eq!(
2508 linked_chunk.updates().unwrap().take(),
2509 &[
2510 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2511 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2512 NewItemsChunk {
2513 previous: Some(ChunkIdentifier(0)),
2514 new: ChunkIdentifier(2),
2515 next: Some(ChunkIdentifier(1)),
2516 },
2517 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2518 StartReattachItems,
2519 PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2520 NewItemsChunk {
2521 previous: Some(ChunkIdentifier(2)),
2522 new: ChunkIdentifier(3),
2523 next: Some(ChunkIdentifier(1)),
2524 },
2525 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2526 EndReattachItems,
2527 ]
2528 );
2529
2530 Ok(())
2531 }
2532
2533 #[test]
2534 fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2535 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2536
2537 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2538 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2539 assert_eq!(
2540 linked_chunk.updates().unwrap().take(),
2541 &[
2542 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2543 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2544 NewItemsChunk {
2545 previous: Some(ChunkIdentifier(0)),
2546 new: ChunkIdentifier(1),
2547 next: None,
2548 },
2549 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2550 NewItemsChunk {
2551 previous: Some(ChunkIdentifier(1)),
2552 new: ChunkIdentifier(2),
2553 next: None,
2554 },
2555 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2556 ]
2557 );
2558
2559 let pos_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2560 linked_chunk.insert_items_at(pos_d, ['r', 's'])?;
2561
2562 assert_items_eq!(
2563 linked_chunk,
2564 ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2565 );
2566 assert_eq!(linked_chunk.num_items(), 10);
2567 assert_eq!(
2568 linked_chunk.updates().unwrap().take(),
2569 &[
2570 DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2571 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2572 StartReattachItems,
2573 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2574 NewItemsChunk {
2575 previous: Some(ChunkIdentifier(1)),
2576 new: ChunkIdentifier(3),
2577 next: Some(ChunkIdentifier(2)),
2578 },
2579 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2580 EndReattachItems,
2581 ]
2582 );
2583
2584 Ok(())
2585 }
2586
2587 #[test]
2588 fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2589 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2590
2591 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2592 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2593 assert_eq!(
2594 linked_chunk.updates().unwrap().take(),
2595 &[
2596 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2597 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2598 NewItemsChunk {
2599 previous: Some(ChunkIdentifier(0)),
2600 new: ChunkIdentifier(1),
2601 next: None,
2602 },
2603 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2604 ]
2605 );
2606
2607 let pos_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2609 let pos_after_e = Position(pos_e.chunk_identifier(), pos_e.index() + 1);
2610
2611 linked_chunk.insert_items_at(pos_after_e, ['p', 'q'])?;
2612 assert_items_eq!(
2613 linked_chunk,
2614 ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2615 );
2616 assert_eq!(
2617 linked_chunk.updates().unwrap().take(),
2618 &[
2619 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2620 NewItemsChunk {
2621 previous: Some(ChunkIdentifier(1)),
2622 new: ChunkIdentifier(2),
2623 next: None
2624 },
2625 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2626 ]
2627 );
2628 assert_eq!(linked_chunk.num_items(), 7);
2629
2630 Ok(())
2631 }
2632
2633 #[test]
2634 fn test_insert_items_at_errs() -> Result<(), Error> {
2635 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2636
2637 linked_chunk.push_items_back(['a', 'b', 'c']);
2638 linked_chunk.push_gap_back(());
2639 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2640 assert_eq!(
2641 linked_chunk.updates().unwrap().take(),
2642 &[
2643 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2644 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2645 NewGapChunk {
2646 previous: Some(ChunkIdentifier(0)),
2647 new: ChunkIdentifier(1),
2648 next: None,
2649 gap: (),
2650 },
2651 ]
2652 );
2653
2654 {
2656 assert_matches!(
2657 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
2658 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2659 );
2660 assert!(linked_chunk.updates().unwrap().take().is_empty());
2661 }
2662
2663 {
2665 assert_matches!(
2666 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
2667 Err(Error::InvalidItemIndex { index: 128 })
2668 );
2669 assert!(linked_chunk.updates().unwrap().take().is_empty());
2670 }
2671
2672 {
2674 assert_matches!(
2675 linked_chunk.insert_items_at(Position(ChunkIdentifier(1), 0), ['u', 'v'],),
2676 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2677 );
2678 }
2679
2680 Ok(())
2681 }
2682
2683 #[test]
2684 fn test_remove_item_at() -> Result<(), Error> {
2685 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2686
2687 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2688 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2689 assert_eq!(linked_chunk.num_items(), 11);
2690
2691 let _ = linked_chunk.updates().unwrap().take();
2693
2694 {
2697 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2698 let removed_item = linked_chunk.remove_item_at(position_of_f)?;
2699
2700 assert_eq!(removed_item, 'f');
2701 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2702 assert_eq!(linked_chunk.num_items(), 10);
2703
2704 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2705 let removed_item = linked_chunk.remove_item_at(position_of_e)?;
2706
2707 assert_eq!(removed_item, 'e');
2708 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2709 assert_eq!(linked_chunk.num_items(), 9);
2710
2711 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2712 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2713
2714 assert_eq!(removed_item, 'd');
2715 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2716 assert_eq!(linked_chunk.num_items(), 8);
2717
2718 assert_eq!(
2719 linked_chunk.updates().unwrap().take(),
2720 &[
2721 RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2722 RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2723 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2724 RemoveChunk(ChunkIdentifier(1)),
2725 ]
2726 );
2727 }
2728
2729 {
2732 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2733 let removed_item = linked_chunk.remove_item_at(first_position)?;
2734
2735 assert_eq!(removed_item, 'a');
2736 assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2737 assert_eq!(linked_chunk.num_items(), 7);
2738
2739 let removed_item = linked_chunk.remove_item_at(first_position)?;
2740
2741 assert_eq!(removed_item, 'b');
2742 assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2743 assert_eq!(linked_chunk.num_items(), 6);
2744
2745 let removed_item = linked_chunk.remove_item_at(first_position)?;
2746
2747 assert_eq!(removed_item, 'c');
2748 assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2749 assert_eq!(linked_chunk.num_items(), 5);
2750
2751 assert_eq!(
2752 linked_chunk.updates().unwrap().take(),
2753 &[
2754 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2755 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2756 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2757 ]
2758 );
2759 }
2760
2761 {
2764 let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2765 let removed_item = linked_chunk.remove_item_at(first_position)?;
2766
2767 assert_eq!(removed_item, 'g');
2768 assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2769 assert_eq!(linked_chunk.num_items(), 4);
2770
2771 let removed_item = linked_chunk.remove_item_at(first_position)?;
2772
2773 assert_eq!(removed_item, 'h');
2774 assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2775 assert_eq!(linked_chunk.num_items(), 3);
2776
2777 let removed_item = linked_chunk.remove_item_at(first_position)?;
2778
2779 assert_eq!(removed_item, 'i');
2780 assert_items_eq!(linked_chunk, [] ['j', 'k']);
2781 assert_eq!(linked_chunk.num_items(), 2);
2782
2783 assert_eq!(
2784 linked_chunk.updates().unwrap().take(),
2785 &[
2786 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2787 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2788 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2789 RemoveChunk(ChunkIdentifier(2)),
2790 ]
2791 );
2792 }
2793
2794 {
2797 let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2798 let removed_item = linked_chunk.remove_item_at(position_of_k)?;
2799
2800 assert_eq!(removed_item, 'k');
2801 #[rustfmt::skip]
2802 assert_items_eq!(linked_chunk, [] ['j']);
2803 assert_eq!(linked_chunk.num_items(), 1);
2804
2805 let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2806 let removed_item = linked_chunk.remove_item_at(position_of_j)?;
2807
2808 assert_eq!(removed_item, 'j');
2809 assert_items_eq!(linked_chunk, []);
2810 assert_eq!(linked_chunk.num_items(), 0);
2811
2812 assert_eq!(
2813 linked_chunk.updates().unwrap().take(),
2814 &[
2815 RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2816 RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2817 RemoveChunk(ChunkIdentifier(3)),
2818 ]
2819 );
2820 }
2821
2822 {
2825 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2826
2827 #[rustfmt::skip]
2828 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2829 assert_eq!(linked_chunk.num_items(), 4);
2830
2831 assert_matches!(
2833 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 3)),
2834 Err(Error::InvalidItemIndex { index: 3 })
2835 );
2836
2837 assert_matches!(
2840 linked_chunk.remove_item_at(Position(ChunkIdentifier(0), 42)),
2841 Err(Error::InvalidItemIndex { index: 42 })
2842 );
2843
2844 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2845 linked_chunk.insert_gap_at((), position_of_c)?;
2846
2847 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2848 assert_eq!(linked_chunk.num_items(), 4);
2849
2850 let _ = linked_chunk.updates().unwrap().take();
2852
2853 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2854 let removed_item = linked_chunk.remove_item_at(position_of_c)?;
2855
2856 assert_eq!(removed_item, 'c');
2857 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2858 assert_eq!(linked_chunk.num_items(), 3);
2859
2860 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2861 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2862
2863 assert_eq!(removed_item, 'd');
2864 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2865 assert_eq!(linked_chunk.num_items(), 2);
2866
2867 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2868 let removed_item = linked_chunk.remove_item_at(first_position)?;
2869
2870 assert_eq!(removed_item, 'a');
2871 assert_items_eq!(linked_chunk, ['b'] [-]);
2872 assert_eq!(linked_chunk.num_items(), 1);
2873
2874 let removed_item = linked_chunk.remove_item_at(first_position)?;
2875
2876 assert_eq!(removed_item, 'b');
2877 assert_items_eq!(linked_chunk, [] [-]);
2878 assert_eq!(linked_chunk.num_items(), 0);
2879
2880 assert_eq!(
2881 linked_chunk.updates().unwrap().take(),
2882 &[
2883 RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2884 RemoveChunk(ChunkIdentifier(6)),
2885 RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2886 RemoveChunk(ChunkIdentifier(4)),
2887 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2888 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2889 ]
2890 );
2891 }
2892
2893 Ok(())
2894 }
2895
2896 #[test]
2897 fn test_insert_gap_at() -> Result<(), Error> {
2898 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2899
2900 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2901 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2902 assert_eq!(
2903 linked_chunk.updates().unwrap().take(),
2904 &[
2905 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
2906 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2907 NewItemsChunk {
2908 previous: Some(ChunkIdentifier(0)),
2909 new: ChunkIdentifier(1),
2910 next: None
2911 },
2912 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2913 ]
2914 );
2915
2916 {
2918 let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
2919 linked_chunk.insert_gap_at((), position_of_b)?;
2920
2921 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2922 assert_eq!(
2923 linked_chunk.updates().unwrap().take(),
2924 &[
2925 DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
2926 NewGapChunk {
2927 previous: Some(ChunkIdentifier(0)),
2928 new: ChunkIdentifier(2),
2929 next: Some(ChunkIdentifier(1)),
2930 gap: (),
2931 },
2932 StartReattachItems,
2933 NewItemsChunk {
2934 previous: Some(ChunkIdentifier(2)),
2935 new: ChunkIdentifier(3),
2936 next: Some(ChunkIdentifier(1)),
2937 },
2938 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
2939 EndReattachItems,
2940 ]
2941 );
2942 }
2943
2944 {
2947 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2948 linked_chunk.insert_gap_at((), position_of_a)?;
2949
2950 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2953 assert_eq!(
2954 linked_chunk.updates().unwrap().take(),
2955 &[NewGapChunk {
2956 previous: None,
2957 new: ChunkIdentifier(4),
2958 next: Some(ChunkIdentifier(0)),
2959 gap: (),
2960 },]
2961 );
2962 }
2963
2964 {
2968 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2969 linked_chunk.insert_gap_at((), position_of_d)?;
2970
2971 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
2975 assert_eq!(
2976 linked_chunk.updates().unwrap().take(),
2977 &[NewGapChunk {
2978 previous: Some(ChunkIdentifier(3)),
2979 new: ChunkIdentifier(5),
2980 next: Some(ChunkIdentifier(1)),
2981 gap: (),
2982 }]
2983 );
2984 }
2985
2986 {
2988 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
2990 let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
2991
2992 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
2993
2994 assert_eq!(
2995 linked_chunk.updates().unwrap().take(),
2996 &[
2997 NewItemsChunk {
2998 previous: Some(ChunkIdentifier(5)),
2999 new: ChunkIdentifier(6),
3000 next: Some(ChunkIdentifier(1)),
3001 },
3002 RemoveChunk(ChunkIdentifier(5)),
3003 ]
3004 );
3005
3006 linked_chunk.insert_gap_at((), position)?;
3007
3008 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
3009 assert_eq!(
3010 linked_chunk.updates().unwrap().take(),
3011 &[NewGapChunk {
3012 previous: Some(ChunkIdentifier(3)),
3013 new: ChunkIdentifier(7),
3014 next: Some(ChunkIdentifier(6)),
3015 gap: (),
3016 }]
3017 );
3018 }
3019
3020 {
3022 assert_matches!(
3023 linked_chunk.insert_items_at(Position(ChunkIdentifier(128), 0), ['u', 'v'],),
3024 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
3025 );
3026 assert!(linked_chunk.updates().unwrap().take().is_empty());
3027 }
3028
3029 {
3031 assert_matches!(
3032 linked_chunk.insert_items_at(Position(ChunkIdentifier(0), 128), ['u', 'v'],),
3033 Err(Error::InvalidItemIndex { index: 128 })
3034 );
3035 assert!(linked_chunk.updates().unwrap().take().is_empty());
3036 }
3037
3038 {
3040 let position_of_a_gap = Position(ChunkIdentifier(2), 0);
3043 assert_matches!(
3044 linked_chunk.insert_gap_at((), position_of_a_gap),
3045 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
3046 );
3047 assert!(linked_chunk.updates().unwrap().take().is_empty());
3048 }
3049
3050 assert_eq!(linked_chunk.num_items(), 6);
3051
3052 Ok(())
3053 }
3054
3055 #[test]
3056 fn test_replace_gap_at_middle() -> Result<(), Error> {
3057 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3058
3059 linked_chunk.push_items_back(['a', 'b']);
3060 linked_chunk.push_gap_back(());
3061 linked_chunk.push_items_back(['l', 'm']);
3062 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
3063 assert_eq!(
3064 linked_chunk.updates().unwrap().take(),
3065 &[
3066 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
3067 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3068 NewGapChunk {
3069 previous: Some(ChunkIdentifier(0)),
3070 new: ChunkIdentifier(1),
3071 next: None,
3072 gap: (),
3073 },
3074 NewItemsChunk {
3075 previous: Some(ChunkIdentifier(1)),
3076 new: ChunkIdentifier(2),
3077 next: None,
3078 },
3079 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
3080 ]
3081 );
3082
3083 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3085 assert_eq!(gap_identifier, ChunkIdentifier(1));
3086
3087 let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
3088 assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
3089 assert_items_eq!(
3090 linked_chunk,
3091 ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
3092 );
3093 assert_eq!(
3094 linked_chunk.updates().unwrap().take(),
3095 &[
3096 NewItemsChunk {
3097 previous: Some(ChunkIdentifier(1)),
3098 new: ChunkIdentifier(3),
3099 next: Some(ChunkIdentifier(2)),
3100 },
3101 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
3102 NewItemsChunk {
3103 previous: Some(ChunkIdentifier(3)),
3104 new: ChunkIdentifier(4),
3105 next: Some(ChunkIdentifier(2)),
3106 },
3107 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
3108 RemoveChunk(ChunkIdentifier(1)),
3109 ]
3110 );
3111
3112 assert_eq!(linked_chunk.num_items(), 9);
3113
3114 Ok(())
3115 }
3116
3117 #[test]
3118 fn test_replace_gap_at_end() -> Result<(), Error> {
3119 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3120
3121 linked_chunk.push_items_back(['a', 'b']);
3122 linked_chunk.push_gap_back(());
3123 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3124 assert_eq!(
3125 linked_chunk.updates().unwrap().take(),
3126 &[
3127 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
3128 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3129 NewGapChunk {
3130 previous: Some(ChunkIdentifier(0)),
3131 new: ChunkIdentifier(1),
3132 next: None,
3133 gap: (),
3134 },
3135 ]
3136 );
3137
3138 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3140 assert_eq!(gap_identifier, ChunkIdentifier(1));
3141
3142 let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3143 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3144 assert_items_eq!(
3145 linked_chunk,
3146 ['a', 'b'] ['w', 'x', 'y'] ['z']
3147 );
3148 assert_eq!(
3149 linked_chunk.updates().unwrap().take(),
3150 &[
3151 NewItemsChunk {
3152 previous: Some(ChunkIdentifier(1)),
3153 new: ChunkIdentifier(2),
3154 next: None,
3155 },
3156 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3157 NewItemsChunk {
3158 previous: Some(ChunkIdentifier(2)),
3159 new: ChunkIdentifier(3),
3160 next: None,
3161 },
3162 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3163 RemoveChunk(ChunkIdentifier(1)),
3164 ]
3165 );
3166
3167 assert_eq!(linked_chunk.num_items(), 6);
3168
3169 Ok(())
3170 }
3171
3172 #[test]
3173 fn test_replace_gap_at_beginning() -> Result<(), Error> {
3174 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3175
3176 linked_chunk.push_items_back(['a', 'b']);
3177 assert_items_eq!(linked_chunk, ['a', 'b']);
3178 assert_eq!(
3179 linked_chunk.updates().unwrap().take(),
3180 &[
3181 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
3182 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3183 ]
3184 );
3185
3186 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3188 linked_chunk.insert_gap_at((), position_of_a).unwrap();
3189 assert_items_eq!(
3190 linked_chunk,
3191 [-] ['a', 'b']
3192 );
3193 assert_eq!(
3194 linked_chunk.updates().unwrap().take(),
3195 &[NewGapChunk {
3196 previous: None,
3197 new: ChunkIdentifier(1),
3198 next: Some(ChunkIdentifier(0)),
3199 gap: (),
3200 }]
3201 );
3202
3203 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3204 assert_eq!(gap_identifier, ChunkIdentifier(1));
3205
3206 let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3207 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3208 assert_items_eq!(
3209 linked_chunk,
3210 ['x'] ['a', 'b']
3211 );
3212 assert_eq!(
3213 linked_chunk.updates().unwrap().take(),
3214 &[
3215 NewItemsChunk {
3216 previous: Some(ChunkIdentifier(1)),
3217 new: ChunkIdentifier(2),
3218 next: Some(ChunkIdentifier(0)),
3219 },
3220 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3221 RemoveChunk(ChunkIdentifier(1)),
3222 ]
3223 );
3224
3225 assert_eq!(linked_chunk.num_items(), 3);
3226
3227 Ok(())
3228 }
3229
3230 #[test]
3231 fn test_remove_empty_chunk_at() -> Result<(), Error> {
3232 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3233
3234 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3235 linked_chunk.push_items_back(['a', 'b']);
3236 linked_chunk.push_gap_back(());
3237 linked_chunk.push_items_back(['l', 'm']);
3238 linked_chunk.push_gap_back(());
3239 assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3240 assert_eq!(
3241 linked_chunk.updates().unwrap().take(),
3242 &[
3243 NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None },
3244 NewGapChunk {
3245 previous: None,
3246 new: ChunkIdentifier(1),
3247 next: Some(ChunkIdentifier(0)),
3248 gap: (),
3249 },
3250 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3251 NewGapChunk {
3252 previous: Some(ChunkIdentifier(0)),
3253 new: ChunkIdentifier(2),
3254 next: None,
3255 gap: (),
3256 },
3257 NewItemsChunk {
3258 previous: Some(ChunkIdentifier(2)),
3259 new: ChunkIdentifier(3),
3260 next: None,
3261 },
3262 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3263 NewGapChunk {
3264 previous: Some(ChunkIdentifier(3)),
3265 new: ChunkIdentifier(4),
3266 next: None,
3267 gap: (),
3268 },
3269 ]
3270 );
3271
3272 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3274 assert_matches!(err, Error::RemovingNonEmptyItemsChunk { .. });
3275
3276 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(42)).unwrap_err();
3278 assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3279
3280 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(2)).unwrap();
3282 let next = maybe_next.unwrap();
3283 assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3285 assert_eq!(next.index(), 0);
3286 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3287 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3288
3289 let next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(4)).unwrap();
3291 assert!(next.is_none());
3293 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3294 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3295
3296 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(1)).unwrap();
3298 let next = maybe_next.unwrap();
3299 assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3300 assert_eq!(next.index(), 0);
3301 assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3302 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3303
3304 Ok(())
3305 }
3306
3307 #[test]
3308 fn test_remove_empty_last_chunk() {
3309 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3310
3311 assert!(linked_chunk.updates().unwrap().take().is_empty());
3312
3313 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3315 assert_matches!(err, Error::RemovingLastChunk);
3316 }
3317
3318 #[test]
3319 fn test_chunk_item_positions() {
3320 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3321 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3322 linked_chunk.push_gap_back(());
3323 linked_chunk.push_items_back(['f']);
3324
3325 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3326
3327 let mut iterator = linked_chunk.chunks();
3328
3329 {
3331 let chunk = iterator.next().unwrap();
3332 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3333 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3334 }
3335
3336 {
3338 let chunk = iterator.next().unwrap();
3339 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3340 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3341 }
3342
3343 {
3345 let chunk = iterator.next().unwrap();
3346 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3347 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3348 }
3349
3350 {
3352 let chunk = iterator.next().unwrap();
3353 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3354 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3355 }
3356 }
3357
3358 #[test]
3359 fn test_is_first_and_last_chunk() {
3360 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3361
3362 let mut chunks = linked_chunk.chunks().peekable();
3363 assert!(chunks.peek().unwrap().is_first_chunk());
3364 assert!(chunks.next().unwrap().is_last_chunk());
3365 assert!(chunks.next().is_none());
3366
3367 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3368
3369 let mut chunks = linked_chunk.chunks().peekable();
3370 assert!(chunks.next().unwrap().is_first_chunk());
3371 assert!(chunks.peek().unwrap().is_first_chunk().not());
3372 assert!(chunks.next().unwrap().is_last_chunk().not());
3373 assert!(chunks.next().unwrap().is_last_chunk());
3374 assert!(chunks.next().is_none());
3375 }
3376
3377 #[test]
3382 fn test_clear() {
3383 let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3384
3385 let item = Arc::new('a');
3386 let gap = Arc::new(());
3387
3388 linked_chunk.push_items_back([
3389 item.clone(),
3390 item.clone(),
3391 item.clone(),
3392 item.clone(),
3393 item.clone(),
3394 ]);
3395 linked_chunk.push_gap_back(gap.clone());
3396 linked_chunk.push_items_back([item.clone()]);
3397
3398 assert_eq!(Arc::strong_count(&item), 7);
3399 assert_eq!(Arc::strong_count(&gap), 2);
3400 assert_eq!(linked_chunk.chunks().filter(|chunk| chunk.is_items()).count(), 3);
3401 assert_eq!(linked_chunk.chunks().filter(|chunk| chunk.is_gap()).count(), 1);
3402 assert_eq!(linked_chunk.num_items(), 6);
3403 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3404
3405 linked_chunk.clear();
3407
3408 assert_eq!(Arc::strong_count(&item), 1);
3409 assert_eq!(Arc::strong_count(&gap), 1);
3410 assert_eq!(linked_chunk.chunks().filter(|chunk| chunk.is_items()).count(), 1);
3413 assert_eq!(linked_chunk.chunks().filter(|chunk| chunk.is_gap()).count(), 0);
3414 assert_eq!(linked_chunk.num_items(), 0);
3415 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3416 }
3417
3418 #[test]
3419 fn test_clear_emits_an_update_clear() {
3420 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3421
3422 linked_chunk.push_items_back(['a']);
3424
3425 assert_eq!(
3427 linked_chunk.updates().unwrap().take(),
3428 &[
3429 NewItemsChunk {
3430 previous: None,
3431 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3432 next: None
3433 },
3434 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }
3435 ]
3436 );
3437
3438 linked_chunk.clear();
3440
3441 assert_eq!(linked_chunk.updates().unwrap().take(), &[Clear]);
3443 }
3444
3445 #[test]
3446 fn test_clear_emits_an_update_clear_and_forget_about_pending_updates() {
3447 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3448
3449 linked_chunk.push_items_back(['a']);
3451
3452 linked_chunk.clear();
3454
3455 assert_eq!(linked_chunk.updates().unwrap().take(), &[Clear]);
3457 }
3458
3459 #[test]
3460 fn test_clear_emits_no_new_items_chunk_if_already_clear() {
3461 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3462
3463 linked_chunk.clear();
3465
3466 assert_eq!(linked_chunk.updates().unwrap().take(), &[Clear]);
3469 }
3470
3471 #[test]
3472 fn test_replace_item() {
3473 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3474
3475 linked_chunk.push_items_back(['a', 'b', 'c']);
3476 linked_chunk.push_gap_back(());
3477 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3479
3480 let _ = linked_chunk.updates().unwrap().take();
3482
3483 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3485 assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3486
3487 assert_eq!(
3488 linked_chunk.updates().unwrap().take(),
3489 &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3490 );
3491
3492 assert_matches!(
3494 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3495 Err(Error::InvalidItemIndex { index: 3 })
3496 );
3497
3498 assert_matches!(
3500 linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3501 Err(Error::ChunkIsAGap { .. })
3502 );
3503 }
3504
3505 #[test]
3506 fn test_lazy_previous() {
3507 use std::marker::PhantomData;
3508
3509 use super::{Ends, ObservableUpdates};
3510
3511 let first_chunk_identifier = ChunkIdentifier(0);
3513 let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3514 unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3515
3516 let updates = Some(ObservableUpdates::new());
3517
3518 let mut linked_chunk = LinkedChunk::<3, char, ()> {
3519 links: Ends::new_with_first_chunk(first_loaded_chunk, &updates),
3520 chunk_identifier_generator:
3521 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3522 updates,
3523 marker: PhantomData,
3524 };
3525
3526 {
3529 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3530
3531 assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3532
3533 {
3535 let mut chunks = linked_chunk.chunks();
3536
3537 assert_matches!(chunks.next(), Some(chunk) => {
3538 assert_eq!(chunk.identifier(), 1);
3539 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3540 });
3541 assert_matches!(chunks.next(), Some(chunk) => {
3542 assert_eq!(chunk.identifier(), 2);
3543 assert!(chunk.lazy_previous.is_none());
3544 });
3545 assert!(chunks.next().is_none());
3546 }
3547
3548 assert_eq!(
3550 linked_chunk.updates().unwrap().take(),
3551 &[
3552 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3553 NewItemsChunk {
3554 previous: Some(ChunkIdentifier(1)),
3555 new: ChunkIdentifier(2),
3556 next: None,
3557 },
3558 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3559 ]
3560 );
3561 }
3562
3563 {
3565 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3566
3567 assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3568
3569 {
3571 let mut chunks = linked_chunk.chunks();
3572
3573 assert_matches!(chunks.next(), Some(chunk) => {
3574 assert_eq!(chunk.identifier(), 3);
3575 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3577 });
3578 assert_matches!(chunks.next(), Some(chunk) => {
3579 assert_eq!(chunk.identifier(), 1);
3580 assert!(chunk.lazy_previous.is_none());
3582 });
3583 assert_matches!(chunks.next(), Some(chunk) => {
3584 assert_eq!(chunk.identifier(), 2);
3585 assert!(chunk.lazy_previous.is_none());
3586 });
3587 assert!(chunks.next().is_none());
3588 }
3589
3590 assert_eq!(
3593 linked_chunk.updates().unwrap().take(),
3594 &[NewGapChunk {
3595 previous: Some(ChunkIdentifier(0)),
3597 new: ChunkIdentifier(3),
3598 next: Some(ChunkIdentifier(1)),
3599 gap: ()
3600 }]
3601 );
3602 }
3603
3604 {
3606 linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3607
3608 assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3609
3610 {
3612 let mut chunks = linked_chunk.chunks();
3613
3614 assert_matches!(chunks.next(), Some(chunk) => {
3615 assert_eq!(chunk.identifier(), 4);
3616 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3618 });
3619 assert_matches!(chunks.next(), Some(chunk) => {
3620 assert_eq!(chunk.identifier(), 5);
3621 assert!(chunk.lazy_previous.is_none());
3622 });
3623 assert_matches!(chunks.next(), Some(chunk) => {
3624 assert_eq!(chunk.identifier(), 1);
3625 assert!(chunk.lazy_previous.is_none());
3626 });
3627 assert_matches!(chunks.next(), Some(chunk) => {
3628 assert_eq!(chunk.identifier(), 2);
3629 assert!(chunk.lazy_previous.is_none());
3630 });
3631 assert!(chunks.next().is_none());
3632 }
3633
3634 assert_eq!(
3636 linked_chunk.updates().unwrap().take(),
3637 &[
3638 NewItemsChunk {
3640 previous: Some(ChunkIdentifier(3)),
3641 new: ChunkIdentifier(4),
3642 next: Some(ChunkIdentifier(1)),
3643 },
3644 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3646 NewItemsChunk {
3648 previous: Some(ChunkIdentifier(4)),
3649 new: ChunkIdentifier(5),
3650 next: Some(ChunkIdentifier(1)),
3651 },
3652 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3654 RemoveChunk(ChunkIdentifier(3)),
3656 ]
3657 );
3658 }
3659
3660 {
3664 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3665
3666 assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3667
3668 {
3670 let mut chunks = linked_chunk.chunks();
3671
3672 assert_matches!(chunks.next(), Some(chunk) => {
3673 assert_eq!(chunk.identifier(), 6);
3674 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3676 });
3677 assert_matches!(chunks.next(), Some(chunk) => {
3678 assert_eq!(chunk.identifier(), 4);
3679 assert!(chunk.lazy_previous.is_none());
3681 });
3682 assert_matches!(chunks.next(), Some(chunk) => {
3683 assert_eq!(chunk.identifier(), 5);
3684 assert!(chunk.lazy_previous.is_none());
3685 });
3686 assert_matches!(chunks.next(), Some(chunk) => {
3687 assert_eq!(chunk.identifier(), 1);
3688 assert!(chunk.lazy_previous.is_none());
3689 });
3690 assert_matches!(chunks.next(), Some(chunk) => {
3691 assert_eq!(chunk.identifier(), 2);
3692 assert!(chunk.lazy_previous.is_none());
3693 });
3694 assert!(chunks.next().is_none());
3695 }
3696
3697 assert_eq!(
3700 linked_chunk.updates().unwrap().take(),
3701 &[NewGapChunk {
3702 previous: Some(ChunkIdentifier(0)),
3704 new: ChunkIdentifier(6),
3705 next: Some(ChunkIdentifier(4)),
3706 gap: ()
3707 }]
3708 );
3709 }
3710 }
3711}