1#![allow(rustdoc::private_intra_doc_links)]
16
17#[cfg(test)]
32macro_rules! assert_items_eq {
33 ( @_ [ $iterator:ident ] { [-] $( $rest:tt )* } { $( $accumulator:tt )* } ) => {
34 assert_items_eq!(
35 @_
36 [ $iterator ]
37 { $( $rest )* }
38 {
39 $( $accumulator )*
40 {
41 let chunk = $iterator .next().expect("next chunk (expect gap)");
42 assert!(chunk.is_gap(), "chunk should be a gap");
43 }
44 }
45 )
46 };
47
48 ( @_ [ $iterator:ident ] { [ $( $item:expr ),* ] $( $rest:tt )* } { $( $accumulator:tt )* } ) => {
49 assert_items_eq!(
50 @_
51 [ $iterator ]
52 { $( $rest )* }
53 {
54 $( $accumulator )*
55 {
56 let chunk = $iterator .next().expect("next chunk (expect items)");
57 assert!(chunk.is_items(), "chunk should contain items");
58
59 let $crate::linked_chunk::ChunkContent::Items(items) = chunk.content() else {
60 unreachable!()
61 };
62
63 let mut items_iterator = items.iter();
64
65 $(
66 assert_eq!(items_iterator.next(), Some(& $item ));
67 )*
68
69 assert!(items_iterator.next().is_none(), "no more items");
70 }
71 }
72 )
73 };
74
75 ( @_ [ $iterator:ident ] {} { $( $accumulator:tt )* } ) => {
76 {
77 $( $accumulator )*
78 assert!( $iterator .next().is_none(), "no more chunks");
79 }
80 };
81
82 ( $linked_chunk:expr, $( $all:tt )* ) => {
83 assert_items_eq!(
84 @_
85 [ iterator ]
86 { $( $all )* }
87 {
88 let mut iterator = $linked_chunk.chunks();
89 }
90 )
91 }
92}
93
94mod as_vector;
95pub mod lazy_loader;
96pub mod relational;
97mod updates;
98
99use std::{
100 fmt,
101 marker::PhantomData,
102 ops::Not,
103 ptr::NonNull,
104 sync::atomic::{AtomicU64, Ordering},
105};
106
107pub use as_vector::*;
108pub use updates::*;
109
110#[derive(thiserror::Error, Debug)]
112pub enum Error {
113 #[error("The chunk identifier is invalid: `{identifier:?}`")]
115 InvalidChunkIdentifier {
116 identifier: ChunkIdentifier,
118 },
119
120 #[error("The chunk is a gap: `{identifier:?}`")]
122 ChunkIsAGap {
123 identifier: ChunkIdentifier,
125 },
126
127 #[error("The chunk is an item: `{identifier:?}`")]
129 ChunkIsItems {
130 identifier: ChunkIdentifier,
132 },
133
134 #[error("The item index is invalid: `{index}`")]
136 InvalidItemIndex {
137 index: usize,
139 },
140}
141
142struct Ends<const CHUNK_CAPACITY: usize, Item, Gap> {
147 first: NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>,
149 last: Option<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
151}
152
153impl<const CAP: usize, Item, Gap> Ends<CAP, Item, Gap> {
154 fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
156 unsafe { self.first.as_ref() }
157 }
158
159 fn first_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
161 unsafe { self.first.as_mut() }
162 }
163
164 fn latest_chunk(&self) -> &Chunk<CAP, Item, Gap> {
166 unsafe { self.last.unwrap_or(self.first).as_ref() }
167 }
168
169 fn latest_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
171 unsafe { self.last.as_mut().unwrap_or(&mut self.first).as_mut() }
172 }
173
174 fn chunk(&self, identifier: ChunkIdentifier) -> Option<&Chunk<CAP, Item, Gap>> {
176 let mut chunk = self.latest_chunk();
177
178 loop {
179 if chunk.identifier() == identifier {
180 return Some(chunk);
181 }
182
183 chunk = chunk.previous()?;
184 }
185 }
186
187 fn chunk_mut(&mut self, identifier: ChunkIdentifier) -> Option<&mut Chunk<CAP, Item, Gap>> {
189 let mut chunk = self.latest_chunk_mut();
190
191 loop {
192 if chunk.identifier() == identifier {
193 return Some(chunk);
194 }
195
196 chunk = chunk.previous_mut()?;
197 }
198 }
199
200 fn clear(&mut self) {
202 {
204 let mut current_chunk_ptr = self.last.or(Some(self.first));
206
207 while let Some(chunk_ptr) = current_chunk_ptr {
209 let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
211
212 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
214
215 current_chunk_ptr = previous_ptr;
217 }
218
219 }
222
223 self.first = Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER);
225
226 self.last = None;
228 }
229}
230
231pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
238 links: Ends<CHUNK_CAPACITY, Item, Gap>,
240
241 chunk_identifier_generator: ChunkIdentifierGenerator,
243
244 updates: Option<ObservableUpdates<Item, Gap>>,
248
249 marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
251}
252
253impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
254 fn default() -> Self {
255 Self::new()
256 }
257}
258
259impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
260 pub fn new() -> Self {
262 Self {
263 links: Ends {
264 first: Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER),
265 last: None,
266 },
267 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
268 updates: None,
269 marker: PhantomData,
270 }
271 }
272
273 pub fn new_with_update_history() -> Self {
279 let first_chunk_identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
280
281 let mut updates = ObservableUpdates::new();
282 updates.push(Update::NewItemsChunk {
283 previous: None,
284 new: first_chunk_identifier,
285 next: None,
286 });
287
288 Self {
289 links: Ends { first: Chunk::new_items_leaked(first_chunk_identifier), last: None },
290 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
291 updates: Some(updates),
292 marker: PhantomData,
293 }
294 }
295
296 pub fn clear(&mut self) {
298 self.links.clear();
300
301 self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
303
304 if let Some(updates) = self.updates.as_mut() {
306 updates.push(Update::Clear);
308 updates.push(Update::NewItemsChunk {
309 previous: None,
310 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
311 next: None,
312 })
313 }
314 }
315
316 pub fn push_items_back<I>(&mut self, items: I)
322 where
323 Item: Clone,
324 Gap: Clone,
325 I: IntoIterator<Item = Item>,
326 I::IntoIter: ExactSizeIterator,
327 {
328 let items = items.into_iter();
329
330 let last_chunk = self.links.latest_chunk_mut();
331
332 let last_chunk =
334 last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
335
336 debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
337
338 if last_chunk.is_first_chunk().not() {
342 self.links.last = Some(last_chunk.as_ptr());
345 }
346 }
347
348 pub fn push_gap_back(&mut self, content: Gap)
351 where
352 Item: Clone,
353 Gap: Clone,
354 {
355 let last_chunk = self.links.latest_chunk_mut();
356 last_chunk.insert_next(
357 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
358 &mut self.updates,
359 );
360
361 self.links.last = last_chunk.next;
362 }
363
364 pub fn insert_items_at<I>(&mut self, items: I, position: Position) -> Result<(), Error>
369 where
370 Item: Clone,
371 Gap: Clone,
372 I: IntoIterator<Item = Item>,
373 I::IntoIter: ExactSizeIterator,
374 {
375 let chunk_identifier = position.chunk_identifier();
376 let item_index = position.index();
377
378 let chunk = self
379 .links
380 .chunk_mut(chunk_identifier)
381 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
382
383 let chunk = match &mut chunk.content {
384 ChunkContent::Gap(..) => {
385 return Err(Error::ChunkIsAGap { identifier: chunk_identifier })
386 }
387
388 ChunkContent::Items(current_items) => {
389 let current_items_length = current_items.len();
390
391 if item_index > current_items_length {
392 return Err(Error::InvalidItemIndex { index: item_index });
393 }
394
395 let items = items.into_iter();
397
398 if item_index == current_items_length {
400 chunk
401 .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
403 }
404 else {
406 if let Some(updates) = self.updates.as_mut() {
407 updates.push(Update::DetachLastItems {
408 at: Position(chunk_identifier, item_index),
409 });
410 }
411
412 let detached_items = current_items.split_off(item_index);
414
415 let chunk = chunk
416 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
418
419 if let Some(updates) = self.updates.as_mut() {
420 updates.push(Update::StartReattachItems);
421 }
422
423 let chunk = chunk
424 .push_items(
426 detached_items.into_iter(),
427 &self.chunk_identifier_generator,
428 &mut self.updates,
429 );
430
431 if let Some(updates) = self.updates.as_mut() {
432 updates.push(Update::EndReattachItems);
433 }
434
435 chunk
436 }
437 }
438 };
439
440 if chunk.is_first_chunk().not() && chunk.is_last_chunk() {
443 self.links.last = Some(chunk.as_ptr());
446 }
447
448 Ok(())
449 }
450
451 pub fn remove_item_at(
461 &mut self,
462 position: Position,
463 empty_chunk: EmptyChunk,
464 ) -> Result<Item, Error> {
465 let chunk_identifier = position.chunk_identifier();
466 let item_index = position.index();
467
468 let mut chunk_ptr = None;
469 let removed_item;
470
471 {
472 let chunk = self
473 .links
474 .chunk_mut(chunk_identifier)
475 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
476
477 let can_unlink_chunk = match &mut chunk.content {
478 ChunkContent::Gap(..) => {
479 return Err(Error::ChunkIsAGap { identifier: chunk_identifier })
480 }
481
482 ChunkContent::Items(current_items) => {
483 let current_items_length = current_items.len();
484
485 if item_index > current_items_length {
486 return Err(Error::InvalidItemIndex { index: item_index });
487 }
488
489 removed_item = current_items.remove(item_index);
490
491 if let Some(updates) = self.updates.as_mut() {
492 updates
493 .push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
494 }
495
496 current_items.is_empty()
497 }
498 };
499
500 if empty_chunk.remove() && can_unlink_chunk && chunk.is_first_chunk().not() {
503 chunk.unlink(self.updates.as_mut());
505
506 chunk_ptr = Some(chunk.as_ptr());
507
508 if chunk.is_last_chunk() {
511 self.links.last = chunk.previous;
512 }
513 }
514
515 }
517
518 if let Some(chunk_ptr) = chunk_ptr {
519 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
526 }
527
528 Ok(removed_item)
529 }
530
531 pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
536 where
537 Item: Clone,
538 {
539 let chunk_identifier = position.chunk_identifier();
540 let item_index = position.index();
541
542 let chunk = self
543 .links
544 .chunk_mut(chunk_identifier)
545 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
546
547 match &mut chunk.content {
548 ChunkContent::Gap(..) => {
549 return Err(Error::ChunkIsAGap { identifier: chunk_identifier })
550 }
551
552 ChunkContent::Items(current_items) => {
553 if item_index >= current_items.len() {
554 return Err(Error::InvalidItemIndex { index: item_index });
555 }
556
557 if let Some(updates) = self.updates.as_mut() {
559 updates.push(Update::ReplaceItem {
560 at: Position(chunk_identifier, item_index),
561 item: item.clone(),
562 });
563 }
564
565 current_items[item_index] = item;
566 }
567 }
568
569 Ok(())
570 }
571
572 pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
577 where
578 Item: Clone,
579 Gap: Clone,
580 {
581 let chunk_identifier = position.chunk_identifier();
582 let item_index = position.index();
583
584 let chunk = self
585 .links
586 .chunk_mut(chunk_identifier)
587 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
588
589 let chunk = match &mut chunk.content {
590 ChunkContent::Gap(..) => {
591 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
592 }
593
594 ChunkContent::Items(current_items) => {
595 if item_index == 0 {
599 let chunk_was_first = chunk.is_first_chunk();
600 let chunk_was_last = chunk.is_last_chunk();
601
602 let new_chunk = chunk.insert_before(
603 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
604 self.updates.as_mut(),
605 );
606
607 let new_chunk_ptr = new_chunk.as_ptr();
608 let chunk_ptr = chunk.as_ptr();
609
610 if chunk_was_first {
615 self.links.first = new_chunk_ptr;
616
617 if chunk_was_last {
619 self.links.last = Some(chunk_ptr);
620 }
621 }
622
623 return Ok(());
624 }
625
626 let current_items_length = current_items.len();
627
628 if item_index >= current_items_length {
629 return Err(Error::InvalidItemIndex { index: item_index });
630 }
631
632 if let Some(updates) = self.updates.as_mut() {
633 updates.push(Update::DetachLastItems {
634 at: Position(chunk_identifier, item_index),
635 });
636 }
637
638 let detached_items = current_items.split_off(item_index);
640
641 let chunk = chunk
642 .insert_next(
644 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
645 &mut self.updates,
646 );
647
648 if let Some(updates) = self.updates.as_mut() {
649 updates.push(Update::StartReattachItems);
650 }
651
652 let chunk = chunk
653 .insert_next(
655 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
656 &mut self.updates,
657 )
658 .push_items(
660 detached_items.into_iter(),
661 &self.chunk_identifier_generator,
662 &mut self.updates,
663 );
664
665 if let Some(updates) = self.updates.as_mut() {
666 updates.push(Update::EndReattachItems);
667 }
668
669 chunk
670 }
671 };
672
673 if chunk.is_first_chunk().not() && chunk.is_last_chunk() {
676 self.links.last = Some(chunk.as_ptr());
679 }
680
681 Ok(())
682 }
683
684 pub fn remove_gap_at(
689 &mut self,
690 chunk_identifier: ChunkIdentifier,
691 ) -> Result<Option<Position>, Error> {
692 let chunk = self
693 .links
694 .chunk_mut(chunk_identifier)
695 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
696
697 if chunk.is_items() {
698 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
699 };
700
701 let chunk_was_first = chunk.is_first_chunk();
702 let chunk_was_last = chunk.is_last_chunk();
703 let next_ptr = chunk.next;
704 let previous_ptr = chunk.previous;
705 let position_of_next = chunk.next().map(|next| next.first_position());
706
707 chunk.unlink(self.updates.as_mut());
708
709 let chunk_ptr = chunk.as_ptr();
710
711 if chunk_was_first {
713 if let Some(next_ptr) = next_ptr {
715 self.links.first = next_ptr;
716 }
717 }
718
719 if chunk_was_last {
720 self.links.last = previous_ptr;
721 }
722
723 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
726
727 Ok(position_of_next)
729 }
730
731 pub fn replace_gap_at<I>(
739 &mut self,
740 items: I,
741 chunk_identifier: ChunkIdentifier,
742 ) -> Result<&Chunk<CAP, Item, Gap>, Error>
743 where
744 Item: Clone,
745 Gap: Clone,
746 I: IntoIterator<Item = Item>,
747 I::IntoIter: ExactSizeIterator,
748 {
749 let chunk_ptr;
750 let new_chunk_ptr;
751
752 {
753 let chunk = self
754 .links
755 .chunk_mut(chunk_identifier)
756 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
757
758 if chunk.is_items() {
759 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
760 };
761
762 let chunk_was_first = chunk.is_first_chunk();
763
764 let maybe_last_chunk_ptr = {
765 let items = items.into_iter();
766
767 let last_inserted_chunk = chunk
768 .insert_next(
770 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
771 &mut self.updates,
772 )
773 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
775
776 last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
777 };
778
779 new_chunk_ptr = chunk
780 .next
781 .unwrap();
783
784 chunk.unlink(self.updates.as_mut());
786
787 chunk_ptr = chunk.as_ptr();
789
790 if chunk_was_first {
792 self.links.first = new_chunk_ptr;
793 }
794
795 if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
798 self.links.last = Some(last_chunk_ptr);
799 }
800
801 }
803
804 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
809
810 Ok(
811 unsafe { new_chunk_ptr.as_ref() },
815 )
816 }
817
818 pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
820 where
821 P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
822 {
823 self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
824 }
825
826 pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
828 where
829 P: FnMut(&'a Item) -> bool,
830 {
831 self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
832 }
833
834 pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
838 IterBackward::new(self.links.latest_chunk())
839 }
840
841 pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
845 Iter::new(self.links.first_chunk())
846 }
847
848 pub fn rchunks_from(
853 &self,
854 identifier: ChunkIdentifier,
855 ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
856 Ok(IterBackward::new(
857 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
858 ))
859 }
860
861 pub fn chunks_from(
866 &self,
867 identifier: ChunkIdentifier,
868 ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
869 Ok(Iter::new(
870 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
871 ))
872 }
873
874 pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
878 self.ritems_from(self.links.latest_chunk().last_position())
879 .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
880 }
881
882 pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
886 let first_chunk = self.links.first_chunk();
887
888 self.items_from(first_chunk.first_position())
889 .expect("`items` cannot fail because at least one empty chunk must exist")
890 }
891
892 pub fn ritems_from(
896 &self,
897 position: Position,
898 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
899 Ok(self
900 .rchunks_from(position.chunk_identifier())?
901 .filter_map(|chunk| match &chunk.content {
902 ChunkContent::Gap(..) => None,
903 ChunkContent::Items(items) => {
904 let identifier = chunk.identifier();
905
906 Some(
907 items.iter().enumerate().rev().map(move |(item_index, item)| {
908 (Position(identifier, item_index), item)
909 }),
910 )
911 }
912 })
913 .flatten()
914 .skip_while({
915 let expected_index = position.index();
916
917 move |(Position(chunk_identifier, item_index), _item)| {
918 *chunk_identifier == position.chunk_identifier()
919 && *item_index != expected_index
920 }
921 }))
922 }
923
924 pub fn items_from(
928 &self,
929 position: Position,
930 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
931 Ok(self
932 .chunks_from(position.chunk_identifier())?
933 .filter_map(|chunk| match &chunk.content {
934 ChunkContent::Gap(..) => None,
935 ChunkContent::Items(items) => {
936 let identifier = chunk.identifier();
937
938 Some(
939 items.iter().enumerate().map(move |(item_index, item)| {
940 (Position(identifier, item_index), item)
941 }),
942 )
943 }
944 })
945 .flatten()
946 .skip(position.index()))
947 }
948
949 #[must_use]
961 pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
962 self.updates.as_mut()
963 }
964
965 pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
972 let (updates, token) = self
973 .updates
974 .as_mut()
975 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
976 let chunk_iterator = self.chunks();
977
978 Some(AsVector::new(updates, token, chunk_iterator))
979 }
980
981 pub fn num_items(&self) -> usize {
983 self.items().count()
984 }
985}
986
987impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
988 fn drop(&mut self) {
989 self.links.clear();
994 }
995}
996
997unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1001
1002unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1006
1007#[derive(Debug)]
1017pub struct ChunkIdentifierGenerator {
1018 next: AtomicU64,
1019}
1020
1021impl ChunkIdentifierGenerator {
1022 const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1024
1025 pub fn new_from_scratch() -> Self {
1028 Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1029 }
1030
1031 pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1034 Self { next: AtomicU64::new(last_chunk_identifier.0) }
1035 }
1036
1037 fn next(&self) -> ChunkIdentifier {
1042 let previous = self.next.fetch_add(1, Ordering::Relaxed);
1043
1044 if previous == u64::MAX {
1047 panic!("No more chunk identifiers available. Congrats, you did it. 2^64 identifiers have been consumed.")
1048 }
1049
1050 ChunkIdentifier(previous + 1)
1051 }
1052
1053 #[doc(hidden)]
1057 pub fn current(&self) -> ChunkIdentifier {
1058 ChunkIdentifier(self.next.load(Ordering::Relaxed))
1059 }
1060}
1061
1062#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1068#[repr(transparent)]
1069pub struct ChunkIdentifier(u64);
1070
1071impl ChunkIdentifier {
1072 pub fn new(identifier: u64) -> Self {
1074 Self(identifier)
1075 }
1076
1077 pub fn index(&self) -> u64 {
1079 self.0
1080 }
1081}
1082
1083impl PartialEq<u64> for ChunkIdentifier {
1084 fn eq(&self, other: &u64) -> bool {
1085 self.0 == *other
1086 }
1087}
1088
1089#[derive(Copy, Clone, Debug, PartialEq)]
1093pub struct Position(ChunkIdentifier, usize);
1094
1095impl Position {
1096 pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1098 Self(chunk_identifier, index)
1099 }
1100
1101 pub fn chunk_identifier(&self) -> ChunkIdentifier {
1103 self.0
1104 }
1105
1106 pub fn index(&self) -> usize {
1108 self.1
1109 }
1110
1111 pub fn decrement_index(&mut self) {
1117 self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1118 }
1119
1120 pub fn increment_index(&mut self) {
1127 self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1128 }
1129}
1130
1131#[derive(Debug)]
1134pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1135 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1136}
1137
1138impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1139 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1141 Self { chunk: Some(from_chunk) }
1142 }
1143}
1144
1145impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1146 type Item = &'a Chunk<CAP, Item, Gap>;
1147
1148 fn next(&mut self) -> Option<Self::Item> {
1149 self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1150 }
1151}
1152
1153#[derive(Debug)]
1156pub struct Iter<'a, const CAP: usize, Item, Gap> {
1157 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1158}
1159
1160impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1161 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1163 Self { chunk: Some(from_chunk) }
1164 }
1165}
1166
1167impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1168 type Item = &'a Chunk<CAP, Item, Gap>;
1169
1170 fn next(&mut self) -> Option<Self::Item> {
1171 self.chunk.inspect(|chunk| self.chunk = chunk.next())
1172 }
1173}
1174
1175#[derive(Clone, Debug)]
1177pub enum ChunkContent<Item, Gap> {
1178 Gap(Gap),
1181
1182 Items(Vec<Item>),
1184}
1185
1186pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1188 previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1190
1191 lazy_previous: Option<ChunkIdentifier>,
1196
1197 next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1199
1200 identifier: ChunkIdentifier,
1202
1203 content: ChunkContent<Item, Gap>,
1205}
1206
1207impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1208 fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1210 Self::new(identifier, ChunkContent::Gap(content))
1211 }
1212
1213 fn new_items(identifier: ChunkIdentifier) -> Self {
1215 Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1216 }
1217
1218 fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1219 Self { previous: None, lazy_previous: None, next: None, identifier, content }
1220 }
1221
1222 fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1224 let chunk = Self::new(identifier, content);
1225 let chunk_box = Box::new(chunk);
1226
1227 NonNull::from(Box::leak(chunk_box))
1228 }
1229
1230 fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1232 let chunk = Self::new_gap(identifier, content);
1233 let chunk_box = Box::new(chunk);
1234
1235 NonNull::from(Box::leak(chunk_box))
1236 }
1237
1238 fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1240 let chunk = Self::new_items(identifier);
1241 let chunk_box = Box::new(chunk);
1242
1243 NonNull::from(Box::leak(chunk_box))
1244 }
1245
1246 pub fn as_ptr(&self) -> NonNull<Self> {
1248 NonNull::from(self)
1249 }
1250
1251 pub fn is_gap(&self) -> bool {
1253 matches!(self.content, ChunkContent::Gap(..))
1254 }
1255
1256 pub fn is_items(&self) -> bool {
1258 !self.is_gap()
1259 }
1260
1261 fn is_first_chunk(&self) -> bool {
1263 self.previous.is_none()
1264 }
1265
1266 fn is_last_chunk(&self) -> bool {
1268 self.next.is_none()
1269 }
1270
1271 pub fn identifier(&self) -> ChunkIdentifier {
1273 self.identifier
1274 }
1275
1276 pub fn content(&self) -> &ChunkContent<Item, Gap> {
1278 &self.content
1279 }
1280
1281 pub fn first_position(&self) -> Position {
1285 Position(self.identifier(), 0)
1286 }
1287
1288 pub fn last_position(&self) -> Position {
1292 let identifier = self.identifier();
1293
1294 match &self.content {
1295 ChunkContent::Gap(..) => Position(identifier, 0),
1296 ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1297 }
1298 }
1299
1300 fn len(&self) -> usize {
1304 match &self.content {
1305 ChunkContent::Gap(..) => 0,
1306 ChunkContent::Items(items) => items.len(),
1307 }
1308 }
1309
1310 fn push_items<I>(
1322 &mut self,
1323 mut new_items: I,
1324 chunk_identifier_generator: &ChunkIdentifierGenerator,
1325 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1326 ) -> &mut Self
1327 where
1328 I: Iterator<Item = Item> + ExactSizeIterator,
1329 Item: Clone,
1330 Gap: Clone,
1331 {
1332 let number_of_new_items = new_items.len();
1333 let chunk_length = self.len();
1334
1335 if number_of_new_items == 0 {
1337 return self;
1338 }
1339
1340 let identifier = self.identifier();
1341
1342 match &mut self.content {
1343 ChunkContent::Gap(..) => {
1346 self
1347 .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1349 .push_items(new_items, chunk_identifier_generator, updates)
1352 }
1353
1354 ChunkContent::Items(items) => {
1355 let free_space = CAPACITY.saturating_sub(chunk_length);
1357
1358 if number_of_new_items <= free_space {
1360 let start = items.len();
1361 items.extend(new_items);
1362
1363 if let Some(updates) = updates.as_mut() {
1364 updates.push(Update::PushItems {
1365 at: Position(identifier, start),
1366 items: items[start..].to_vec(),
1367 });
1368 }
1369
1370 self
1372 } else {
1373 if free_space > 0 {
1374 let start = items.len();
1376 items.extend(new_items.by_ref().take(free_space));
1377
1378 if let Some(updates) = updates.as_mut() {
1379 updates.push(Update::PushItems {
1380 at: Position(identifier, start),
1381 items: items[start..].to_vec(),
1382 });
1383 }
1384 }
1385
1386 self
1387 .insert_next(
1389 Self::new_items_leaked(chunk_identifier_generator.next()),
1390 updates,
1391 )
1392 .push_items(new_items, chunk_identifier_generator, updates)
1395 }
1396 }
1397 }
1398 }
1399
1400 fn insert_next(
1405 &mut self,
1406 mut new_chunk_ptr: NonNull<Self>,
1407 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1408 ) -> &mut Self
1409 where
1410 Gap: Clone,
1411 {
1412 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1413
1414 if let Some(next_chunk) = self.next_mut() {
1416 next_chunk.previous = Some(new_chunk_ptr);
1418
1419 new_chunk.next = self.next;
1421 }
1422
1423 self.next = Some(new_chunk_ptr);
1425 new_chunk.previous = Some(self.as_ptr());
1427
1428 if let Some(updates) = updates.as_mut() {
1429 let previous = new_chunk.previous().map(Chunk::identifier);
1430 let new = new_chunk.identifier();
1431 let next = new_chunk.next().map(Chunk::identifier);
1432
1433 match new_chunk.content() {
1434 ChunkContent::Gap(gap) => {
1435 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1436 }
1437
1438 ChunkContent::Items(..) => {
1439 updates.push(Update::NewItemsChunk { previous, new, next })
1440 }
1441 }
1442 }
1443
1444 new_chunk
1445 }
1446
1447 fn insert_before(
1452 &mut self,
1453 mut new_chunk_ptr: NonNull<Self>,
1454 updates: Option<&mut ObservableUpdates<Item, Gap>>,
1455 ) -> &mut Self
1456 where
1457 Gap: Clone,
1458 {
1459 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1460
1461 if let Some(previous_chunk) = self.previous_mut() {
1463 previous_chunk.next = Some(new_chunk_ptr);
1465
1466 new_chunk.previous = self.previous;
1468 }
1469 else {
1472 new_chunk.lazy_previous = self.lazy_previous.take();
1473 }
1474
1475 self.previous = Some(new_chunk_ptr);
1477 new_chunk.next = Some(self.as_ptr());
1479
1480 if let Some(updates) = updates {
1481 let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1482 let new = new_chunk.identifier();
1483 let next = new_chunk.next().map(Chunk::identifier);
1484
1485 match new_chunk.content() {
1486 ChunkContent::Gap(gap) => {
1487 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1488 }
1489
1490 ChunkContent::Items(..) => {
1491 updates.push(Update::NewItemsChunk { previous, new, next })
1492 }
1493 }
1494 }
1495
1496 new_chunk
1497 }
1498
1499 fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1504 let previous_ptr = self.previous;
1505 let next_ptr = self.next;
1506 let lazy_previous = self.lazy_previous.take();
1510
1511 if let Some(previous) = self.previous_mut() {
1512 previous.next = next_ptr;
1513 }
1514
1515 if let Some(next) = self.next_mut() {
1516 next.previous = previous_ptr;
1517 next.lazy_previous = lazy_previous;
1518 }
1519
1520 if let Some(updates) = updates {
1521 updates.push(Update::RemoveChunk(self.identifier()));
1522 }
1523 }
1524
1525 fn previous(&self) -> Option<&Self> {
1527 self.previous.map(|non_null| unsafe { non_null.as_ref() })
1528 }
1529
1530 fn previous_mut(&mut self) -> Option<&mut Self> {
1532 self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1533 }
1534
1535 fn next(&self) -> Option<&Self> {
1537 self.next.map(|non_null| unsafe { non_null.as_ref() })
1538 }
1539
1540 fn next_mut(&mut self) -> Option<&mut Self> {
1542 self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1543 }
1544}
1545
1546impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<CAP, Item, Gap>
1547where
1548 Item: fmt::Debug,
1549 Gap: fmt::Debug,
1550{
1551 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1552 formatter
1553 .debug_struct("LinkedChunk")
1554 .field("first (deref)", unsafe { self.links.first.as_ref() })
1555 .field("last", &self.links.last)
1556 .finish_non_exhaustive()
1557 }
1558}
1559
1560impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1561where
1562 Item: fmt::Debug,
1563 Gap: fmt::Debug,
1564{
1565 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1566 formatter
1567 .debug_struct("Chunk")
1568 .field("identifier", &self.identifier)
1569 .field("content", &self.content)
1570 .field("previous", &self.previous)
1571 .field("ptr", &std::ptr::from_ref(self))
1572 .field("next", &self.next)
1573 .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1574 .finish()
1575 }
1576}
1577
1578#[derive(Debug)]
1580pub enum EmptyChunk {
1581 Keep,
1583
1584 Remove,
1586}
1587
1588impl EmptyChunk {
1589 fn remove(&self) -> bool {
1590 matches!(self, Self::Remove)
1591 }
1592}
1593
1594#[derive(Clone, Debug)]
1600pub struct RawChunk<Item, Gap> {
1601 pub content: ChunkContent<Item, Gap>,
1603
1604 pub previous: Option<ChunkIdentifier>,
1606
1607 pub identifier: ChunkIdentifier,
1609
1610 pub next: Option<ChunkIdentifier>,
1612}
1613
1614#[cfg(test)]
1615mod tests {
1616 use std::{
1617 ops::Not,
1618 sync::{atomic::Ordering, Arc},
1619 };
1620
1621 use assert_matches::assert_matches;
1622
1623 use super::{
1624 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, EmptyChunk, Error,
1625 LinkedChunk, Position,
1626 };
1627
1628 #[test]
1629 fn test_chunk_identifier_generator() {
1630 let generator = ChunkIdentifierGenerator::new_from_scratch();
1631
1632 assert_eq!(generator.next(), ChunkIdentifier(1));
1633 assert_eq!(generator.next(), ChunkIdentifier(2));
1634 assert_eq!(generator.next(), ChunkIdentifier(3));
1635 assert_eq!(generator.next(), ChunkIdentifier(4));
1636
1637 let generator =
1638 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1639
1640 assert_eq!(generator.next(), ChunkIdentifier(43));
1641 assert_eq!(generator.next(), ChunkIdentifier(44));
1642 assert_eq!(generator.next(), ChunkIdentifier(45));
1643 assert_eq!(generator.next(), ChunkIdentifier(46));
1644 }
1645
1646 #[test]
1647 fn test_empty() {
1648 let items = LinkedChunk::<3, char, ()>::new();
1649
1650 assert_eq!(items.num_items(), 0);
1651
1652 }
1655
1656 #[test]
1657 fn test_updates() {
1658 assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1659 assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1660 }
1661
1662 #[test]
1663 fn test_new_with_initial_update() {
1664 use super::Update::*;
1665
1666 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1667
1668 assert_eq!(
1669 linked_chunk.updates().unwrap().take(),
1670 &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1671 );
1672 }
1673
1674 #[test]
1675 fn test_push_items() {
1676 use super::Update::*;
1677
1678 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1679
1680 let _ = linked_chunk.updates().unwrap().take();
1682
1683 linked_chunk.push_items_back(['a']);
1684
1685 assert_items_eq!(linked_chunk, ['a']);
1686 assert_eq!(
1687 linked_chunk.updates().unwrap().take(),
1688 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1689 );
1690
1691 linked_chunk.push_items_back(['b', 'c']);
1692 assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1693 assert_eq!(
1694 linked_chunk.updates().unwrap().take(),
1695 &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1696 );
1697
1698 linked_chunk.push_items_back(['d', 'e']);
1699 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
1700 assert_eq!(
1701 linked_chunk.updates().unwrap().take(),
1702 &[
1703 NewItemsChunk {
1704 previous: Some(ChunkIdentifier(0)),
1705 new: ChunkIdentifier(1),
1706 next: None
1707 },
1708 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1709 ]
1710 );
1711
1712 linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1713 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1714 assert_eq!(
1715 linked_chunk.updates().unwrap().take(),
1716 &[
1717 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1718 NewItemsChunk {
1719 previous: Some(ChunkIdentifier(1)),
1720 new: ChunkIdentifier(2),
1721 next: None,
1722 },
1723 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1724 NewItemsChunk {
1725 previous: Some(ChunkIdentifier(2)),
1726 new: ChunkIdentifier(3),
1727 next: None,
1728 },
1729 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1730 ]
1731 );
1732
1733 assert_eq!(linked_chunk.num_items(), 10);
1734 }
1735
1736 #[test]
1737 fn test_push_gap() {
1738 use super::Update::*;
1739
1740 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1741
1742 let _ = linked_chunk.updates().unwrap().take();
1744
1745 linked_chunk.push_items_back(['a']);
1746 assert_items_eq!(linked_chunk, ['a']);
1747 assert_eq!(
1748 linked_chunk.updates().unwrap().take(),
1749 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1750 );
1751
1752 linked_chunk.push_gap_back(());
1753 assert_items_eq!(linked_chunk, ['a'] [-]);
1754 assert_eq!(
1755 linked_chunk.updates().unwrap().take(),
1756 &[NewGapChunk {
1757 previous: Some(ChunkIdentifier(0)),
1758 new: ChunkIdentifier(1),
1759 next: None,
1760 gap: (),
1761 }]
1762 );
1763
1764 linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1765 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1766 assert_eq!(
1767 linked_chunk.updates().unwrap().take(),
1768 &[
1769 NewItemsChunk {
1770 previous: Some(ChunkIdentifier(1)),
1771 new: ChunkIdentifier(2),
1772 next: None,
1773 },
1774 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
1775 NewItemsChunk {
1776 previous: Some(ChunkIdentifier(2)),
1777 new: ChunkIdentifier(3),
1778 next: None,
1779 },
1780 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
1781 ]
1782 );
1783
1784 linked_chunk.push_gap_back(());
1785 linked_chunk.push_gap_back(()); assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
1787 assert_eq!(
1788 linked_chunk.updates().unwrap().take(),
1789 &[
1790 NewGapChunk {
1791 previous: Some(ChunkIdentifier(3)),
1792 new: ChunkIdentifier(4),
1793 next: None,
1794 gap: (),
1795 },
1796 NewGapChunk {
1797 previous: Some(ChunkIdentifier(4)),
1798 new: ChunkIdentifier(5),
1799 next: None,
1800 gap: (),
1801 }
1802 ]
1803 );
1804
1805 linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
1806 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
1807 assert_eq!(
1808 linked_chunk.updates().unwrap().take(),
1809 &[
1810 NewItemsChunk {
1811 previous: Some(ChunkIdentifier(5)),
1812 new: ChunkIdentifier(6),
1813 next: None,
1814 },
1815 PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
1816 NewItemsChunk {
1817 previous: Some(ChunkIdentifier(6)),
1818 new: ChunkIdentifier(7),
1819 next: None,
1820 },
1821 PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
1822 ]
1823 );
1824
1825 assert_eq!(linked_chunk.num_items(), 9);
1826 }
1827
1828 #[test]
1829 fn test_identifiers_and_positions() {
1830 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
1831 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
1832 linked_chunk.push_gap_back(());
1833 linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
1834 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
1835
1836 assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
1837 assert_eq!(
1838 linked_chunk.item_position(|item| *item == 'e'),
1839 Some(Position(ChunkIdentifier(1), 1))
1840 );
1841 }
1842
1843 #[test]
1844 fn test_rchunks() {
1845 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1846 linked_chunk.push_items_back(['a', 'b']);
1847 linked_chunk.push_gap_back(());
1848 linked_chunk.push_items_back(['c', 'd', 'e']);
1849
1850 let mut iterator = linked_chunk.rchunks();
1851
1852 assert_matches!(
1853 iterator.next(),
1854 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
1855 assert_eq!(items, &['e']);
1856 }
1857 );
1858 assert_matches!(
1859 iterator.next(),
1860 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1861 assert_eq!(items, &['c', 'd']);
1862 }
1863 );
1864 assert_matches!(
1865 iterator.next(),
1866 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
1867 );
1868 assert_matches!(
1869 iterator.next(),
1870 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
1871 assert_eq!(items, &['a', 'b']);
1872 }
1873 );
1874 assert_matches!(iterator.next(), None);
1875 }
1876
1877 #[test]
1878 fn test_chunks() {
1879 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1880 linked_chunk.push_items_back(['a', 'b']);
1881 linked_chunk.push_gap_back(());
1882 linked_chunk.push_items_back(['c', 'd', 'e']);
1883
1884 let mut iterator = linked_chunk.chunks();
1885
1886 assert_matches!(
1887 iterator.next(),
1888 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
1889 assert_eq!(items, &['a', 'b']);
1890 }
1891 );
1892 assert_matches!(
1893 iterator.next(),
1894 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
1895 );
1896 assert_matches!(
1897 iterator.next(),
1898 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1899 assert_eq!(items, &['c', 'd']);
1900 }
1901 );
1902 assert_matches!(
1903 iterator.next(),
1904 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
1905 assert_eq!(items, &['e']);
1906 }
1907 );
1908 assert_matches!(iterator.next(), None);
1909 }
1910
1911 #[test]
1912 fn test_rchunks_from() -> Result<(), Error> {
1913 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1914 linked_chunk.push_items_back(['a', 'b']);
1915 linked_chunk.push_gap_back(());
1916 linked_chunk.push_items_back(['c', 'd', 'e']);
1917
1918 let mut iterator = linked_chunk.rchunks_from(
1919 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
1920 )?;
1921
1922 assert_matches!(
1923 iterator.next(),
1924 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1925 assert_eq!(items, &['c', 'd']);
1926 }
1927 );
1928 assert_matches!(
1929 iterator.next(),
1930 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
1931 );
1932 assert_matches!(
1933 iterator.next(),
1934 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
1935 assert_eq!(items, &['a', 'b']);
1936 }
1937 );
1938 assert_matches!(iterator.next(), None);
1939
1940 Ok(())
1941 }
1942
1943 #[test]
1944 fn test_chunks_from() -> Result<(), Error> {
1945 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1946 linked_chunk.push_items_back(['a', 'b']);
1947 linked_chunk.push_gap_back(());
1948 linked_chunk.push_items_back(['c', 'd', 'e']);
1949
1950 let mut iterator = linked_chunk.chunks_from(
1951 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
1952 )?;
1953
1954 assert_matches!(
1955 iterator.next(),
1956 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1957 assert_eq!(items, &['c', 'd']);
1958 }
1959 );
1960 assert_matches!(
1961 iterator.next(),
1962 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
1963 assert_eq!(items, &['e']);
1964 }
1965 );
1966 assert_matches!(iterator.next(), None);
1967
1968 Ok(())
1969 }
1970
1971 #[test]
1972 fn test_ritems() {
1973 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1974 linked_chunk.push_items_back(['a', 'b']);
1975 linked_chunk.push_gap_back(());
1976 linked_chunk.push_items_back(['c', 'd', 'e']);
1977
1978 let mut iterator = linked_chunk.ritems();
1979
1980 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
1981 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
1982 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
1983 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
1984 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
1985 assert_matches!(iterator.next(), None);
1986 }
1987
1988 #[test]
1989 fn test_ritems_with_final_gap() -> Result<(), Error> {
1990 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
1991 linked_chunk.push_items_back(['a', 'b']);
1992 linked_chunk.push_gap_back(());
1993 linked_chunk.push_items_back(['c', 'd', 'e']);
1994 linked_chunk.push_gap_back(());
1995
1996 let mut iterator = linked_chunk.ritems();
1997
1998 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
1999 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2000 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2001 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2002 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2003 assert_matches!(iterator.next(), None);
2004
2005 Ok(())
2006 }
2007
2008 #[test]
2009 fn test_ritems_empty() {
2010 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2011 let mut iterator = linked_chunk.ritems();
2012
2013 assert_matches!(iterator.next(), None);
2014 }
2015
2016 #[test]
2017 fn test_items() {
2018 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2019 linked_chunk.push_items_back(['a', 'b']);
2020 linked_chunk.push_gap_back(());
2021 linked_chunk.push_items_back(['c', 'd', 'e']);
2022
2023 let mut iterator = linked_chunk.items();
2024
2025 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2026 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2027 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2028 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2029 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2030 assert_matches!(iterator.next(), None);
2031 }
2032
2033 #[test]
2034 fn test_items_empty() {
2035 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2036 let mut iterator = linked_chunk.items();
2037
2038 assert_matches!(iterator.next(), None);
2039 }
2040
2041 #[test]
2042 fn test_ritems_from() -> Result<(), Error> {
2043 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2044 linked_chunk.push_items_back(['a', 'b']);
2045 linked_chunk.push_gap_back(());
2046 linked_chunk.push_items_back(['c', 'd', 'e']);
2047
2048 let mut iterator =
2049 linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2050
2051 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2052 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2053 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2054 assert_matches!(iterator.next(), None);
2055
2056 Ok(())
2057 }
2058
2059 #[test]
2060 fn test_items_from() -> Result<(), Error> {
2061 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2062 linked_chunk.push_items_back(['a', 'b']);
2063 linked_chunk.push_gap_back(());
2064 linked_chunk.push_items_back(['c', 'd', 'e']);
2065
2066 let mut iterator =
2067 linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2068
2069 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2070 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2071 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2072 assert_matches!(iterator.next(), None);
2073
2074 Ok(())
2075 }
2076
2077 #[test]
2078 fn test_insert_items_at() -> Result<(), Error> {
2079 use super::Update::*;
2080
2081 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2082
2083 let _ = linked_chunk.updates().unwrap().take();
2085
2086 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2087 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2088 assert_eq!(
2089 linked_chunk.updates().unwrap().take(),
2090 &[
2091 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2092 NewItemsChunk {
2093 previous: Some(ChunkIdentifier(0)),
2094 new: ChunkIdentifier(1),
2095 next: None,
2096 },
2097 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2098 ]
2099 );
2100
2101 {
2103 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2104
2105 linked_chunk.insert_items_at(['w', 'x', 'y', 'z'], position_of_e)?;
2108
2109 assert_items_eq!(
2110 linked_chunk,
2111 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2112 );
2113 assert_eq!(linked_chunk.num_items(), 10);
2114 assert_eq!(
2115 linked_chunk.updates().unwrap().take(),
2116 &[
2117 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2118 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2119 NewItemsChunk {
2120 previous: Some(ChunkIdentifier(1)),
2121 new: ChunkIdentifier(2),
2122 next: None,
2123 },
2124 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2125 StartReattachItems,
2126 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2127 NewItemsChunk {
2128 previous: Some(ChunkIdentifier(2)),
2129 new: ChunkIdentifier(3),
2130 next: None,
2131 },
2132 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2133 EndReattachItems,
2134 ]
2135 );
2136 }
2137
2138 {
2140 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2141 linked_chunk.insert_items_at(['l', 'm', 'n', 'o'], position_of_a)?;
2142
2143 assert_items_eq!(
2144 linked_chunk,
2145 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2146 );
2147 assert_eq!(linked_chunk.num_items(), 14);
2148 assert_eq!(
2149 linked_chunk.updates().unwrap().take(),
2150 &[
2151 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2152 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2153 NewItemsChunk {
2154 previous: Some(ChunkIdentifier(0)),
2155 new: ChunkIdentifier(4),
2156 next: Some(ChunkIdentifier(1)),
2157 },
2158 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2159 StartReattachItems,
2160 PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2161 NewItemsChunk {
2162 previous: Some(ChunkIdentifier(4)),
2163 new: ChunkIdentifier(5),
2164 next: Some(ChunkIdentifier(1)),
2165 },
2166 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2167 EndReattachItems,
2168 ]
2169 );
2170 }
2171
2172 {
2174 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2175 linked_chunk.insert_items_at(['r', 's'], position_of_c)?;
2176
2177 assert_items_eq!(
2178 linked_chunk,
2179 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2180 );
2181 assert_eq!(linked_chunk.num_items(), 16);
2182 assert_eq!(
2183 linked_chunk.updates().unwrap().take(),
2184 &[
2185 DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2186 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2187 StartReattachItems,
2188 PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2189 EndReattachItems,
2190 ]
2191 );
2192 }
2193
2194 {
2196 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2197 let position_after_f =
2198 Position(position_of_f.chunk_identifier(), position_of_f.index() + 1);
2199
2200 linked_chunk.insert_items_at(['p', 'q'], position_after_f)?;
2201 assert_items_eq!(
2202 linked_chunk,
2203 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2204 );
2205 assert_eq!(
2206 linked_chunk.updates().unwrap().take(),
2207 &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2208 );
2209 assert_eq!(linked_chunk.num_items(), 18);
2210 }
2211
2212 {
2214 assert_matches!(
2215 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)),
2216 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2217 );
2218 assert!(linked_chunk.updates().unwrap().take().is_empty());
2219 }
2220
2221 {
2223 assert_matches!(
2224 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)),
2225 Err(Error::InvalidItemIndex { index: 128 })
2226 );
2227 assert!(linked_chunk.updates().unwrap().take().is_empty());
2228 }
2229
2230 {
2232 linked_chunk.push_gap_back(());
2234 assert_items_eq!(
2235 linked_chunk,
2236 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2237 );
2238 assert_eq!(
2239 linked_chunk.updates().unwrap().take(),
2240 &[NewGapChunk {
2241 previous: Some(ChunkIdentifier(3)),
2242 new: ChunkIdentifier(6),
2243 next: None,
2244 gap: ()
2245 }]
2246 );
2247
2248 assert_matches!(
2249 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(6), 0)),
2250 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2251 );
2252 }
2253
2254 assert_eq!(linked_chunk.num_items(), 18);
2255
2256 Ok(())
2257 }
2258
2259 #[test]
2260 fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2261 use super::Update::*;
2262
2263 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2264
2265 let _ = linked_chunk.updates().unwrap().take();
2267
2268 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2269 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2270 assert_eq!(
2271 linked_chunk.updates().unwrap().take(),
2272 &[
2273 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2274 NewItemsChunk {
2275 previous: Some(ChunkIdentifier(0)),
2276 new: ChunkIdentifier(1),
2277 next: None,
2278 },
2279 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2280 ]
2281 );
2282
2283 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2285
2286 linked_chunk.insert_items_at(['w', 'x', 'y', 'z'], position_of_e)?;
2289
2290 assert_items_eq!(
2291 linked_chunk,
2292 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2293 );
2294 assert_eq!(linked_chunk.num_items(), 10);
2295 assert_eq!(
2296 linked_chunk.updates().unwrap().take(),
2297 &[
2298 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2299 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2300 NewItemsChunk {
2301 previous: Some(ChunkIdentifier(1)),
2302 new: ChunkIdentifier(2),
2303 next: None,
2304 },
2305 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2306 StartReattachItems,
2307 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2308 NewItemsChunk {
2309 previous: Some(ChunkIdentifier(2)),
2310 new: ChunkIdentifier(3),
2311 next: None,
2312 },
2313 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2314 EndReattachItems,
2315 ]
2316 );
2317
2318 Ok(())
2319 }
2320
2321 #[test]
2322 fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2323 use super::Update::*;
2324
2325 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2326
2327 let _ = linked_chunk.updates().unwrap().take();
2329
2330 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2331 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2332 assert_eq!(
2333 linked_chunk.updates().unwrap().take(),
2334 &[
2335 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2336 NewItemsChunk {
2337 previous: Some(ChunkIdentifier(0)),
2338 new: ChunkIdentifier(1),
2339 next: None,
2340 },
2341 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2342 ]
2343 );
2344
2345 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2347 linked_chunk.insert_items_at(['l', 'm', 'n', 'o'], position_of_a)?;
2348
2349 assert_items_eq!(
2350 linked_chunk,
2351 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'e', 'f']
2352 );
2353 assert_eq!(linked_chunk.num_items(), 10);
2354 assert_eq!(
2355 linked_chunk.updates().unwrap().take(),
2356 &[
2357 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2358 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2359 NewItemsChunk {
2360 previous: Some(ChunkIdentifier(0)),
2361 new: ChunkIdentifier(2),
2362 next: Some(ChunkIdentifier(1)),
2363 },
2364 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2365 StartReattachItems,
2366 PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2367 NewItemsChunk {
2368 previous: Some(ChunkIdentifier(2)),
2369 new: ChunkIdentifier(3),
2370 next: Some(ChunkIdentifier(1)),
2371 },
2372 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2373 EndReattachItems,
2374 ]
2375 );
2376
2377 Ok(())
2378 }
2379
2380 #[test]
2381 fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2382 use super::Update::*;
2383
2384 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2385
2386 let _ = linked_chunk.updates().unwrap().take();
2388
2389 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2390 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2391 assert_eq!(
2392 linked_chunk.updates().unwrap().take(),
2393 &[
2394 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2395 NewItemsChunk {
2396 previous: Some(ChunkIdentifier(0)),
2397 new: ChunkIdentifier(1),
2398 next: None,
2399 },
2400 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2401 NewItemsChunk {
2402 previous: Some(ChunkIdentifier(1)),
2403 new: ChunkIdentifier(2),
2404 next: None,
2405 },
2406 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2407 ]
2408 );
2409
2410 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2411 linked_chunk.insert_items_at(['r', 's'], position_of_d)?;
2412
2413 assert_items_eq!(
2414 linked_chunk,
2415 ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2416 );
2417 assert_eq!(linked_chunk.num_items(), 10);
2418 assert_eq!(
2419 linked_chunk.updates().unwrap().take(),
2420 &[
2421 DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2422 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2423 StartReattachItems,
2424 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2425 NewItemsChunk {
2426 previous: Some(ChunkIdentifier(1)),
2427 new: ChunkIdentifier(3),
2428 next: Some(ChunkIdentifier(2)),
2429 },
2430 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2431 EndReattachItems,
2432 ]
2433 );
2434
2435 Ok(())
2436 }
2437
2438 #[test]
2439 fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2440 use super::Update::*;
2441
2442 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2443
2444 let _ = linked_chunk.updates().unwrap().take();
2446
2447 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2448 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2449 assert_eq!(
2450 linked_chunk.updates().unwrap().take(),
2451 &[
2452 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2453 NewItemsChunk {
2454 previous: Some(ChunkIdentifier(0)),
2455 new: ChunkIdentifier(1),
2456 next: None,
2457 },
2458 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2459 ]
2460 );
2461
2462 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2464 let position_after_e =
2465 Position(position_of_e.chunk_identifier(), position_of_e.index() + 1);
2466
2467 linked_chunk.insert_items_at(['p', 'q'], position_after_e)?;
2468 assert_items_eq!(
2469 linked_chunk,
2470 ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2471 );
2472 assert_eq!(
2473 linked_chunk.updates().unwrap().take(),
2474 &[
2475 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2476 NewItemsChunk {
2477 previous: Some(ChunkIdentifier(1)),
2478 new: ChunkIdentifier(2),
2479 next: None
2480 },
2481 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2482 ]
2483 );
2484 assert_eq!(linked_chunk.num_items(), 7);
2485
2486 Ok(())
2487 }
2488
2489 #[test]
2490 fn test_insert_items_at_errs() -> Result<(), Error> {
2491 use super::Update::*;
2492
2493 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2494
2495 let _ = linked_chunk.updates().unwrap().take();
2497
2498 linked_chunk.push_items_back(['a', 'b', 'c']);
2499 linked_chunk.push_gap_back(());
2500 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2501 assert_eq!(
2502 linked_chunk.updates().unwrap().take(),
2503 &[
2504 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2505 NewGapChunk {
2506 previous: Some(ChunkIdentifier(0)),
2507 new: ChunkIdentifier(1),
2508 next: None,
2509 gap: (),
2510 },
2511 ]
2512 );
2513
2514 {
2516 assert_matches!(
2517 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)),
2518 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2519 );
2520 assert!(linked_chunk.updates().unwrap().take().is_empty());
2521 }
2522
2523 {
2525 assert_matches!(
2526 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)),
2527 Err(Error::InvalidItemIndex { index: 128 })
2528 );
2529 assert!(linked_chunk.updates().unwrap().take().is_empty());
2530 }
2531
2532 {
2534 assert_matches!(
2535 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(1), 0)),
2536 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2537 );
2538 }
2539
2540 Ok(())
2541 }
2542
2543 #[test]
2544 fn test_remove_item_at() -> Result<(), Error> {
2545 use super::Update::*;
2546
2547 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2548
2549 let _ = linked_chunk.updates().unwrap().take();
2551
2552 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2553 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2554 assert_eq!(linked_chunk.num_items(), 11);
2555
2556 let _ = linked_chunk.updates().unwrap().take();
2558
2559 {
2562 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2563 let removed_item = linked_chunk.remove_item_at(position_of_f, EmptyChunk::Remove)?;
2564
2565 assert_eq!(removed_item, 'f');
2566 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2567 assert_eq!(linked_chunk.num_items(), 10);
2568
2569 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2570 let removed_item = linked_chunk.remove_item_at(position_of_e, EmptyChunk::Remove)?;
2571
2572 assert_eq!(removed_item, 'e');
2573 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2574 assert_eq!(linked_chunk.num_items(), 9);
2575
2576 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2577 let removed_item = linked_chunk.remove_item_at(position_of_d, EmptyChunk::Remove)?;
2578
2579 assert_eq!(removed_item, 'd');
2580 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2581 assert_eq!(linked_chunk.num_items(), 8);
2582
2583 assert_eq!(
2584 linked_chunk.updates().unwrap().take(),
2585 &[
2586 RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2587 RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2588 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2589 RemoveChunk(ChunkIdentifier(1)),
2590 ]
2591 );
2592 }
2593
2594 {
2597 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2598 let removed_item = linked_chunk.remove_item_at(first_position, EmptyChunk::Remove)?;
2599
2600 assert_eq!(removed_item, 'a');
2601 assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2602 assert_eq!(linked_chunk.num_items(), 7);
2603
2604 let removed_item = linked_chunk.remove_item_at(first_position, EmptyChunk::Remove)?;
2605
2606 assert_eq!(removed_item, 'b');
2607 assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2608 assert_eq!(linked_chunk.num_items(), 6);
2609
2610 let removed_item = linked_chunk.remove_item_at(first_position, EmptyChunk::Remove)?;
2611
2612 assert_eq!(removed_item, 'c');
2613 assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2614 assert_eq!(linked_chunk.num_items(), 5);
2615
2616 assert_eq!(
2617 linked_chunk.updates().unwrap().take(),
2618 &[
2619 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2620 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2621 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2622 ]
2623 );
2624 }
2625
2626 {
2629 let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2630 let removed_item = linked_chunk.remove_item_at(first_position, EmptyChunk::Remove)?;
2631
2632 assert_eq!(removed_item, 'g');
2633 assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2634 assert_eq!(linked_chunk.num_items(), 4);
2635
2636 let removed_item = linked_chunk.remove_item_at(first_position, EmptyChunk::Remove)?;
2637
2638 assert_eq!(removed_item, 'h');
2639 assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2640 assert_eq!(linked_chunk.num_items(), 3);
2641
2642 let removed_item = linked_chunk.remove_item_at(first_position, EmptyChunk::Remove)?;
2643
2644 assert_eq!(removed_item, 'i');
2645 assert_items_eq!(linked_chunk, [] ['j', 'k']);
2646 assert_eq!(linked_chunk.num_items(), 2);
2647
2648 assert_eq!(
2649 linked_chunk.updates().unwrap().take(),
2650 &[
2651 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2652 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2653 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2654 RemoveChunk(ChunkIdentifier(2)),
2655 ]
2656 );
2657 }
2658
2659 {
2662 let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2663 let removed_item = linked_chunk.remove_item_at(position_of_k, EmptyChunk::Remove)?;
2664
2665 assert_eq!(removed_item, 'k');
2666 #[rustfmt::skip]
2667 assert_items_eq!(linked_chunk, [] ['j']);
2668 assert_eq!(linked_chunk.num_items(), 1);
2669
2670 let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2671 let removed_item = linked_chunk.remove_item_at(position_of_j, EmptyChunk::Remove)?;
2672
2673 assert_eq!(removed_item, 'j');
2674 assert_items_eq!(linked_chunk, []);
2675 assert_eq!(linked_chunk.num_items(), 0);
2676
2677 assert_eq!(
2678 linked_chunk.updates().unwrap().take(),
2679 &[
2680 RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2681 RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2682 RemoveChunk(ChunkIdentifier(3)),
2683 ]
2684 );
2685 }
2686
2687 {
2689 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2690
2691 #[rustfmt::skip]
2692 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2693 assert_eq!(linked_chunk.num_items(), 4);
2694
2695 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2696 linked_chunk.insert_gap_at((), position_of_c)?;
2697
2698 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2699 assert_eq!(linked_chunk.num_items(), 4);
2700
2701 let _ = linked_chunk.updates().unwrap().take();
2703
2704 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2705 let removed_item = linked_chunk.remove_item_at(position_of_c, EmptyChunk::Remove)?;
2706
2707 assert_eq!(removed_item, 'c');
2708 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2709 assert_eq!(linked_chunk.num_items(), 3);
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, EmptyChunk::Remove)?;
2713
2714 assert_eq!(removed_item, 'd');
2715 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2716 assert_eq!(linked_chunk.num_items(), 2);
2717
2718 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2719 let removed_item = linked_chunk.remove_item_at(first_position, EmptyChunk::Remove)?;
2720
2721 assert_eq!(removed_item, 'a');
2722 assert_items_eq!(linked_chunk, ['b'] [-]);
2723 assert_eq!(linked_chunk.num_items(), 1);
2724
2725 let removed_item = linked_chunk.remove_item_at(first_position, EmptyChunk::Remove)?;
2726
2727 assert_eq!(removed_item, 'b');
2728 assert_items_eq!(linked_chunk, [] [-]);
2729 assert_eq!(linked_chunk.num_items(), 0);
2730
2731 assert_eq!(
2732 linked_chunk.updates().unwrap().take(),
2733 &[
2734 RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2735 RemoveChunk(ChunkIdentifier(6)),
2736 RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2737 RemoveChunk(ChunkIdentifier(4)),
2738 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2739 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2740 ]
2741 );
2742 }
2743
2744 Ok(())
2745 }
2746
2747 #[test]
2748 fn test_remove_item_at_and_keep_empty_chunks() -> Result<(), Error> {
2749 use super::Update::*;
2750
2751 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2752
2753 let _ = linked_chunk.updates().unwrap().take();
2755
2756 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2757 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2758 assert_eq!(linked_chunk.num_items(), 8);
2759
2760 let _ = linked_chunk.updates().unwrap().take();
2762
2763 {
2766 let position = linked_chunk.item_position(|item| *item == 'd').unwrap();
2767 let removed_item = linked_chunk.remove_item_at(position, EmptyChunk::Keep)?;
2768
2769 assert_eq!(removed_item, 'd');
2770 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['e', 'f'] ['g', 'h']);
2771 assert_eq!(linked_chunk.num_items(), 7);
2772
2773 let removed_item = linked_chunk.remove_item_at(position, EmptyChunk::Keep)?;
2774
2775 assert_eq!(removed_item, 'e');
2776 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['f'] ['g', 'h']);
2777 assert_eq!(linked_chunk.num_items(), 6);
2778
2779 let removed_item = linked_chunk.remove_item_at(position, EmptyChunk::Keep)?;
2780
2781 assert_eq!(removed_item, 'f');
2782 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [] ['g', 'h']);
2783 assert_eq!(linked_chunk.num_items(), 5);
2784
2785 assert_eq!(
2786 linked_chunk.updates().unwrap().take(),
2787 &[
2788 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2789 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2790 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2791 ]
2792 );
2793 }
2794
2795 {
2798 let position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2799 let removed_item = linked_chunk.remove_item_at(position, EmptyChunk::Keep)?;
2800
2801 assert_eq!(removed_item, 'g');
2802 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [] ['h']);
2803 assert_eq!(linked_chunk.num_items(), 4);
2804
2805 let removed_item = linked_chunk.remove_item_at(position, EmptyChunk::Keep)?;
2806
2807 assert_eq!(removed_item, 'h');
2808 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [] []);
2809 assert_eq!(linked_chunk.num_items(), 3);
2810
2811 assert_eq!(
2812 linked_chunk.updates().unwrap().take(),
2813 &[
2814 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2815 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2816 ]
2817 );
2818 }
2819
2820 {
2823 let position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2824 let removed_item = linked_chunk.remove_item_at(position, EmptyChunk::Keep)?;
2825
2826 assert_eq!(removed_item, 'a');
2827 assert_items_eq!(linked_chunk, ['b', 'c'] [] []);
2828 assert_eq!(linked_chunk.num_items(), 2);
2829
2830 let removed_item = linked_chunk.remove_item_at(position, EmptyChunk::Keep)?;
2831
2832 assert_eq!(removed_item, 'b');
2833 assert_items_eq!(linked_chunk, ['c'] [] []);
2834 assert_eq!(linked_chunk.num_items(), 1);
2835
2836 let removed_item = linked_chunk.remove_item_at(position, EmptyChunk::Keep)?;
2837
2838 assert_eq!(removed_item, 'c');
2839 assert_items_eq!(linked_chunk, [] [] []);
2840 assert_eq!(linked_chunk.num_items(), 0);
2841
2842 assert_eq!(
2843 linked_chunk.updates().unwrap().take(),
2844 &[
2845 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2846 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2847 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2848 ]
2849 );
2850 }
2851
2852 Ok(())
2853 }
2854
2855 #[test]
2856 fn test_insert_gap_at() -> Result<(), Error> {
2857 use super::Update::*;
2858
2859 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2860
2861 let _ = linked_chunk.updates().unwrap().take();
2863
2864 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2865 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2866 assert_eq!(
2867 linked_chunk.updates().unwrap().take(),
2868 &[
2869 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2870 NewItemsChunk {
2871 previous: Some(ChunkIdentifier(0)),
2872 new: ChunkIdentifier(1),
2873 next: None
2874 },
2875 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2876 ]
2877 );
2878
2879 {
2881 let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
2882 linked_chunk.insert_gap_at((), position_of_b)?;
2883
2884 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2885 assert_eq!(
2886 linked_chunk.updates().unwrap().take(),
2887 &[
2888 DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
2889 NewGapChunk {
2890 previous: Some(ChunkIdentifier(0)),
2891 new: ChunkIdentifier(2),
2892 next: Some(ChunkIdentifier(1)),
2893 gap: (),
2894 },
2895 StartReattachItems,
2896 NewItemsChunk {
2897 previous: Some(ChunkIdentifier(2)),
2898 new: ChunkIdentifier(3),
2899 next: Some(ChunkIdentifier(1)),
2900 },
2901 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
2902 EndReattachItems,
2903 ]
2904 );
2905 }
2906
2907 {
2910 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2911 linked_chunk.insert_gap_at((), position_of_a)?;
2912
2913 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2916 assert_eq!(
2917 linked_chunk.updates().unwrap().take(),
2918 &[NewGapChunk {
2919 previous: None,
2920 new: ChunkIdentifier(4),
2921 next: Some(ChunkIdentifier(0)),
2922 gap: (),
2923 },]
2924 );
2925 }
2926
2927 {
2930 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2931 linked_chunk.insert_gap_at((), position_of_d)?;
2932
2933 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
2937 assert_eq!(
2938 linked_chunk.updates().unwrap().take(),
2939 &[NewGapChunk {
2940 previous: Some(ChunkIdentifier(3)),
2941 new: ChunkIdentifier(5),
2942 next: Some(ChunkIdentifier(1)),
2943 gap: (),
2944 }]
2945 );
2946 }
2947
2948 {
2950 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
2952 let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
2953
2954 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
2955
2956 assert_eq!(
2957 linked_chunk.updates().unwrap().take(),
2958 &[
2959 NewItemsChunk {
2960 previous: Some(ChunkIdentifier(5)),
2961 new: ChunkIdentifier(6),
2962 next: Some(ChunkIdentifier(1)),
2963 },
2964 RemoveChunk(ChunkIdentifier(5)),
2965 ]
2966 );
2967
2968 linked_chunk.insert_gap_at((), position)?;
2969
2970 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
2971 assert_eq!(
2972 linked_chunk.updates().unwrap().take(),
2973 &[NewGapChunk {
2974 previous: Some(ChunkIdentifier(3)),
2975 new: ChunkIdentifier(7),
2976 next: Some(ChunkIdentifier(6)),
2977 gap: (),
2978 }]
2979 );
2980 }
2981
2982 {
2984 assert_matches!(
2985 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)),
2986 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2987 );
2988 assert!(linked_chunk.updates().unwrap().take().is_empty());
2989 }
2990
2991 {
2993 assert_matches!(
2994 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)),
2995 Err(Error::InvalidItemIndex { index: 128 })
2996 );
2997 assert!(linked_chunk.updates().unwrap().take().is_empty());
2998 }
2999
3000 {
3002 let position_of_a_gap = Position(ChunkIdentifier(2), 0);
3005 assert_matches!(
3006 linked_chunk.insert_gap_at((), position_of_a_gap),
3007 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
3008 );
3009 assert!(linked_chunk.updates().unwrap().take().is_empty());
3010 }
3011
3012 assert_eq!(linked_chunk.num_items(), 6);
3013
3014 Ok(())
3015 }
3016
3017 #[test]
3018 fn test_replace_gap_at_middle() -> Result<(), Error> {
3019 use super::Update::*;
3020
3021 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3022
3023 let _ = linked_chunk.updates().unwrap().take();
3025
3026 linked_chunk.push_items_back(['a', 'b']);
3027 linked_chunk.push_gap_back(());
3028 linked_chunk.push_items_back(['l', 'm']);
3029 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
3030 assert_eq!(
3031 linked_chunk.updates().unwrap().take(),
3032 &[
3033 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3034 NewGapChunk {
3035 previous: Some(ChunkIdentifier(0)),
3036 new: ChunkIdentifier(1),
3037 next: None,
3038 gap: (),
3039 },
3040 NewItemsChunk {
3041 previous: Some(ChunkIdentifier(1)),
3042 new: ChunkIdentifier(2),
3043 next: None,
3044 },
3045 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
3046 ]
3047 );
3048
3049 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3051 assert_eq!(gap_identifier, ChunkIdentifier(1));
3052
3053 let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
3054 assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
3055 assert_items_eq!(
3056 linked_chunk,
3057 ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
3058 );
3059 assert_eq!(
3060 linked_chunk.updates().unwrap().take(),
3061 &[
3062 NewItemsChunk {
3063 previous: Some(ChunkIdentifier(1)),
3064 new: ChunkIdentifier(3),
3065 next: Some(ChunkIdentifier(2)),
3066 },
3067 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
3068 NewItemsChunk {
3069 previous: Some(ChunkIdentifier(3)),
3070 new: ChunkIdentifier(4),
3071 next: Some(ChunkIdentifier(2)),
3072 },
3073 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
3074 RemoveChunk(ChunkIdentifier(1)),
3075 ]
3076 );
3077
3078 assert_eq!(linked_chunk.num_items(), 9);
3079
3080 Ok(())
3081 }
3082
3083 #[test]
3084 fn test_replace_gap_at_end() -> Result<(), Error> {
3085 use super::Update::*;
3086
3087 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3088
3089 let _ = linked_chunk.updates().unwrap().take();
3091
3092 linked_chunk.push_items_back(['a', 'b']);
3093 linked_chunk.push_gap_back(());
3094 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3095 assert_eq!(
3096 linked_chunk.updates().unwrap().take(),
3097 &[
3098 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3099 NewGapChunk {
3100 previous: Some(ChunkIdentifier(0)),
3101 new: ChunkIdentifier(1),
3102 next: None,
3103 gap: (),
3104 },
3105 ]
3106 );
3107
3108 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3110 assert_eq!(gap_identifier, ChunkIdentifier(1));
3111
3112 let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3113 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3114 assert_items_eq!(
3115 linked_chunk,
3116 ['a', 'b'] ['w', 'x', 'y'] ['z']
3117 );
3118 assert_eq!(
3119 linked_chunk.updates().unwrap().take(),
3120 &[
3121 NewItemsChunk {
3122 previous: Some(ChunkIdentifier(1)),
3123 new: ChunkIdentifier(2),
3124 next: None,
3125 },
3126 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3127 NewItemsChunk {
3128 previous: Some(ChunkIdentifier(2)),
3129 new: ChunkIdentifier(3),
3130 next: None,
3131 },
3132 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3133 RemoveChunk(ChunkIdentifier(1)),
3134 ]
3135 );
3136
3137 assert_eq!(linked_chunk.num_items(), 6);
3138
3139 Ok(())
3140 }
3141
3142 #[test]
3143 fn test_replace_gap_at_beginning() -> Result<(), Error> {
3144 use super::Update::*;
3145
3146 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3147
3148 let _ = linked_chunk.updates().unwrap().take();
3150
3151 linked_chunk.push_items_back(['a', 'b']);
3152 assert_items_eq!(linked_chunk, ['a', 'b']);
3153 assert_eq!(
3154 linked_chunk.updates().unwrap().take(),
3155 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },]
3156 );
3157
3158 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3160 linked_chunk.insert_gap_at((), position_of_a).unwrap();
3161 assert_items_eq!(
3162 linked_chunk,
3163 [-] ['a', 'b']
3164 );
3165 assert_eq!(
3166 linked_chunk.updates().unwrap().take(),
3167 &[NewGapChunk {
3168 previous: None,
3169 new: ChunkIdentifier(1),
3170 next: Some(ChunkIdentifier(0)),
3171 gap: (),
3172 }]
3173 );
3174
3175 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3176 assert_eq!(gap_identifier, ChunkIdentifier(1));
3177
3178 let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3179 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3180 assert_items_eq!(
3181 linked_chunk,
3182 ['x'] ['a', 'b']
3183 );
3184 assert_eq!(
3185 linked_chunk.updates().unwrap().take(),
3186 &[
3187 NewItemsChunk {
3188 previous: Some(ChunkIdentifier(1)),
3189 new: ChunkIdentifier(2),
3190 next: Some(ChunkIdentifier(0)),
3191 },
3192 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3193 RemoveChunk(ChunkIdentifier(1)),
3194 ]
3195 );
3196
3197 assert_eq!(linked_chunk.num_items(), 3);
3198
3199 Ok(())
3200 }
3201
3202 #[test]
3203 fn test_remove_gap_at() -> Result<(), Error> {
3204 use super::Update::*;
3205
3206 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3207
3208 let _ = linked_chunk.updates().unwrap().take();
3210
3211 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3212 linked_chunk.push_items_back(['a', 'b']);
3213 linked_chunk.push_gap_back(());
3214 linked_chunk.push_items_back(['l', 'm']);
3215 linked_chunk.push_gap_back(());
3216 assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3217 assert_eq!(
3218 linked_chunk.updates().unwrap().take(),
3219 &[
3220 NewGapChunk {
3221 previous: None,
3222 new: ChunkIdentifier(1),
3223 next: Some(ChunkIdentifier(0)),
3224 gap: (),
3225 },
3226 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3227 NewGapChunk {
3228 previous: Some(ChunkIdentifier(0)),
3229 new: ChunkIdentifier(2),
3230 next: None,
3231 gap: (),
3232 },
3233 NewItemsChunk {
3234 previous: Some(ChunkIdentifier(2)),
3235 new: ChunkIdentifier(3),
3236 next: None,
3237 },
3238 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3239 NewGapChunk {
3240 previous: Some(ChunkIdentifier(3)),
3241 new: ChunkIdentifier(4),
3242 next: None,
3243 gap: (),
3244 },
3245 ]
3246 );
3247
3248 let err = linked_chunk.remove_gap_at(ChunkIdentifier(0)).unwrap_err();
3250 assert_matches!(err, Error::ChunkIsItems { .. });
3251
3252 let err = linked_chunk.remove_gap_at(ChunkIdentifier(42)).unwrap_err();
3254 assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3255
3256 let maybe_next = linked_chunk.remove_gap_at(ChunkIdentifier(2)).unwrap();
3258 let next = maybe_next.unwrap();
3259 assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3261 assert_eq!(next.index(), 0);
3262 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3263 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3264
3265 let next = linked_chunk.remove_gap_at(ChunkIdentifier(4)).unwrap();
3267 assert!(next.is_none());
3269 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3270 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3271
3272 let maybe_next = linked_chunk.remove_gap_at(ChunkIdentifier(1)).unwrap();
3274 let next = maybe_next.unwrap();
3275 assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3276 assert_eq!(next.index(), 0);
3277 assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3278 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3279
3280 Ok(())
3281 }
3282
3283 #[test]
3284 fn test_chunk_item_positions() {
3285 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3286 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3287 linked_chunk.push_gap_back(());
3288 linked_chunk.push_items_back(['f']);
3289
3290 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3291
3292 let mut iterator = linked_chunk.chunks();
3293
3294 {
3296 let chunk = iterator.next().unwrap();
3297 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3298 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3299 }
3300
3301 {
3303 let chunk = iterator.next().unwrap();
3304 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3305 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3306 }
3307
3308 {
3310 let chunk = iterator.next().unwrap();
3311 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3312 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3313 }
3314
3315 {
3317 let chunk = iterator.next().unwrap();
3318 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3319 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3320 }
3321 }
3322
3323 #[test]
3324 fn test_is_first_and_last_chunk() {
3325 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3326
3327 let mut chunks = linked_chunk.chunks().peekable();
3328 assert!(chunks.peek().unwrap().is_first_chunk());
3329 assert!(chunks.next().unwrap().is_last_chunk());
3330 assert!(chunks.next().is_none());
3331
3332 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3333
3334 let mut chunks = linked_chunk.chunks().peekable();
3335 assert!(chunks.next().unwrap().is_first_chunk());
3336 assert!(chunks.peek().unwrap().is_first_chunk().not());
3337 assert!(chunks.next().unwrap().is_last_chunk().not());
3338 assert!(chunks.next().unwrap().is_last_chunk());
3339 assert!(chunks.next().is_none());
3340 }
3341
3342 #[test]
3347 fn test_clear() {
3348 let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3349
3350 let item = Arc::new('a');
3351 let gap = Arc::new(());
3352
3353 linked_chunk.push_items_back([
3354 item.clone(),
3355 item.clone(),
3356 item.clone(),
3357 item.clone(),
3358 item.clone(),
3359 ]);
3360 linked_chunk.push_gap_back(gap.clone());
3361 linked_chunk.push_items_back([item.clone()]);
3362
3363 assert_eq!(Arc::strong_count(&item), 7);
3364 assert_eq!(Arc::strong_count(&gap), 2);
3365 assert_eq!(linked_chunk.num_items(), 6);
3366 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3367
3368 linked_chunk.clear();
3370
3371 assert_eq!(Arc::strong_count(&item), 1);
3372 assert_eq!(Arc::strong_count(&gap), 1);
3373 assert_eq!(linked_chunk.num_items(), 0);
3374 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3375 }
3376
3377 #[test]
3378 fn test_clear_emit_an_update_clear() {
3379 use super::Update::*;
3380
3381 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3382
3383 assert_eq!(
3384 linked_chunk.updates().unwrap().take(),
3385 &[NewItemsChunk {
3386 previous: None,
3387 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3388 next: None
3389 }]
3390 );
3391
3392 linked_chunk.clear();
3393
3394 assert_eq!(
3395 linked_chunk.updates().unwrap().take(),
3396 &[
3397 Clear,
3398 NewItemsChunk {
3399 previous: None,
3400 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3401 next: None
3402 }
3403 ]
3404 );
3405 }
3406
3407 #[test]
3408 fn test_replace_item() {
3409 use super::Update::*;
3410
3411 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3412
3413 linked_chunk.push_items_back(['a', 'b', 'c']);
3414 linked_chunk.push_gap_back(());
3415 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3417
3418 let _ = linked_chunk.updates().unwrap().take();
3420
3421 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3423 assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3424
3425 assert_eq!(
3426 linked_chunk.updates().unwrap().take(),
3427 &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3428 );
3429
3430 assert_matches!(
3432 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3433 Err(Error::InvalidItemIndex { index: 3 })
3434 );
3435
3436 assert_matches!(
3438 linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3439 Err(Error::ChunkIsAGap { .. })
3440 );
3441 }
3442
3443 #[test]
3444 fn test_lazy_previous() {
3445 use std::marker::PhantomData;
3446
3447 use super::{Ends, ObservableUpdates, Update::*};
3448
3449 let first_chunk_identifier = ChunkIdentifier(0);
3451 let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3452 unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3453
3454 let mut linked_chunk = LinkedChunk::<3, char, ()> {
3455 links: Ends { first: first_loaded_chunk, last: None },
3456 chunk_identifier_generator:
3457 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3458 updates: Some(ObservableUpdates::new()),
3459 marker: PhantomData,
3460 };
3461
3462 {
3465 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3466
3467 assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3468
3469 {
3471 let mut chunks = linked_chunk.chunks();
3472
3473 assert_matches!(chunks.next(), Some(chunk) => {
3474 assert_eq!(chunk.identifier(), 1);
3475 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3476 });
3477 assert_matches!(chunks.next(), Some(chunk) => {
3478 assert_eq!(chunk.identifier(), 2);
3479 assert!(chunk.lazy_previous.is_none());
3480 });
3481 assert!(chunks.next().is_none());
3482 }
3483
3484 assert_eq!(
3486 linked_chunk.updates().unwrap().take(),
3487 &[
3488 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3489 NewItemsChunk {
3490 previous: Some(ChunkIdentifier(1)),
3491 new: ChunkIdentifier(2),
3492 next: None,
3493 },
3494 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3495 ]
3496 );
3497 }
3498
3499 {
3501 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3502
3503 assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3504
3505 {
3507 let mut chunks = linked_chunk.chunks();
3508
3509 assert_matches!(chunks.next(), Some(chunk) => {
3510 assert_eq!(chunk.identifier(), 3);
3511 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3513 });
3514 assert_matches!(chunks.next(), Some(chunk) => {
3515 assert_eq!(chunk.identifier(), 1);
3516 assert!(chunk.lazy_previous.is_none());
3518 });
3519 assert_matches!(chunks.next(), Some(chunk) => {
3520 assert_eq!(chunk.identifier(), 2);
3521 assert!(chunk.lazy_previous.is_none());
3522 });
3523 assert!(chunks.next().is_none());
3524 }
3525
3526 assert_eq!(
3528 linked_chunk.updates().unwrap().take(),
3529 &[NewGapChunk {
3530 previous: Some(ChunkIdentifier(0)),
3532 new: ChunkIdentifier(3),
3533 next: Some(ChunkIdentifier(1)),
3534 gap: ()
3535 }]
3536 );
3537 }
3538
3539 {
3541 linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3542
3543 assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3544
3545 {
3547 let mut chunks = linked_chunk.chunks();
3548
3549 assert_matches!(chunks.next(), Some(chunk) => {
3550 assert_eq!(chunk.identifier(), 4);
3551 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3553 });
3554 assert_matches!(chunks.next(), Some(chunk) => {
3555 assert_eq!(chunk.identifier(), 5);
3556 assert!(chunk.lazy_previous.is_none());
3557 });
3558 assert_matches!(chunks.next(), Some(chunk) => {
3559 assert_eq!(chunk.identifier(), 1);
3560 assert!(chunk.lazy_previous.is_none());
3561 });
3562 assert_matches!(chunks.next(), Some(chunk) => {
3563 assert_eq!(chunk.identifier(), 2);
3564 assert!(chunk.lazy_previous.is_none());
3565 });
3566 assert!(chunks.next().is_none());
3567 }
3568
3569 assert_eq!(
3571 linked_chunk.updates().unwrap().take(),
3572 &[
3573 NewItemsChunk {
3575 previous: Some(ChunkIdentifier(3)),
3576 new: ChunkIdentifier(4),
3577 next: Some(ChunkIdentifier(1)),
3578 },
3579 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3581 NewItemsChunk {
3583 previous: Some(ChunkIdentifier(4)),
3584 new: ChunkIdentifier(5),
3585 next: Some(ChunkIdentifier(1)),
3586 },
3587 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3589 RemoveChunk(ChunkIdentifier(3)),
3591 ]
3592 );
3593 }
3594
3595 {
3599 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3600
3601 assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3602
3603 {
3605 let mut chunks = linked_chunk.chunks();
3606
3607 assert_matches!(chunks.next(), Some(chunk) => {
3608 assert_eq!(chunk.identifier(), 6);
3609 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3611 });
3612 assert_matches!(chunks.next(), Some(chunk) => {
3613 assert_eq!(chunk.identifier(), 4);
3614 assert!(chunk.lazy_previous.is_none());
3616 });
3617 assert_matches!(chunks.next(), Some(chunk) => {
3618 assert_eq!(chunk.identifier(), 5);
3619 assert!(chunk.lazy_previous.is_none());
3620 });
3621 assert_matches!(chunks.next(), Some(chunk) => {
3622 assert_eq!(chunk.identifier(), 1);
3623 assert!(chunk.lazy_previous.is_none());
3624 });
3625 assert_matches!(chunks.next(), Some(chunk) => {
3626 assert_eq!(chunk.identifier(), 2);
3627 assert!(chunk.lazy_previous.is_none());
3628 });
3629 assert!(chunks.next().is_none());
3630 }
3631
3632 assert_eq!(
3634 linked_chunk.updates().unwrap().take(),
3635 &[NewGapChunk {
3636 previous: Some(ChunkIdentifier(0)),
3638 new: ChunkIdentifier(6),
3639 next: Some(ChunkIdentifier(4)),
3640 gap: ()
3641 }]
3642 );
3643 }
3644 }
3645}