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 ptr::NonNull,
103 sync::atomic::{AtomicU64, Ordering},
104};
105
106pub use as_vector::*;
107pub use updates::*;
108
109#[derive(thiserror::Error, Debug)]
111pub enum Error {
112 #[error("The chunk identifier is invalid: `{identifier:?}`")]
114 InvalidChunkIdentifier {
115 identifier: ChunkIdentifier,
117 },
118
119 #[error("The chunk is a gap: `{identifier:?}`")]
121 ChunkIsAGap {
122 identifier: ChunkIdentifier,
124 },
125
126 #[error("The chunk is an item: `{identifier:?}`")]
128 ChunkIsItems {
129 identifier: ChunkIdentifier,
131 },
132
133 #[error("The chunk is a non-empty item chunk: `{identifier:?}`")]
135 RemovingNonEmptyItemsChunk {
136 identifier: ChunkIdentifier,
138 },
139
140 #[error("Trying to remove the only chunk, but a linked chunk can't be empty")]
143 RemovingLastChunk,
144
145 #[error("The item index is invalid: `{index}`")]
147 InvalidItemIndex {
148 index: usize,
150 },
151}
152
153struct Ends<const CHUNK_CAPACITY: usize, Item, Gap> {
158 first: NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>,
160 last: Option<NonNull<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
162}
163
164impl<const CAP: usize, Item, Gap> Ends<CAP, Item, Gap> {
165 fn first_chunk(&self) -> &Chunk<CAP, Item, Gap> {
167 unsafe { self.first.as_ref() }
168 }
169
170 fn first_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
172 unsafe { self.first.as_mut() }
173 }
174
175 fn latest_chunk(&self) -> &Chunk<CAP, Item, Gap> {
177 unsafe { self.last.unwrap_or(self.first).as_ref() }
178 }
179
180 fn latest_chunk_mut(&mut self) -> &mut Chunk<CAP, Item, Gap> {
182 unsafe { self.last.as_mut().unwrap_or(&mut self.first).as_mut() }
183 }
184
185 fn chunk(&self, identifier: ChunkIdentifier) -> Option<&Chunk<CAP, Item, Gap>> {
187 let mut chunk = self.latest_chunk();
188
189 loop {
190 if chunk.identifier() == identifier {
191 return Some(chunk);
192 }
193
194 chunk = chunk.previous()?;
195 }
196 }
197
198 fn chunk_mut(&mut self, identifier: ChunkIdentifier) -> Option<&mut Chunk<CAP, Item, Gap>> {
200 let mut chunk = self.latest_chunk_mut();
201
202 loop {
203 if chunk.identifier() == identifier {
204 return Some(chunk);
205 }
206
207 chunk = chunk.previous_mut()?;
208 }
209 }
210
211 fn replace_with(&mut self, first_chunk: NonNull<Chunk<CAP, Item, Gap>>) {
214 let mut current_chunk_ptr = self.last.or(Some(self.first));
217
218 while let Some(chunk_ptr) = current_chunk_ptr {
220 let previous_ptr = unsafe { chunk_ptr.as_ref() }.previous;
222
223 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
225
226 current_chunk_ptr = previous_ptr;
228 }
229
230 self.first = first_chunk;
232 self.last = None;
233 }
234
235 fn clear(&mut self) {
240 self.replace_with(Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER));
241 }
242}
243
244pub struct LinkedChunk<const CHUNK_CAPACITY: usize, Item, Gap> {
251 links: Ends<CHUNK_CAPACITY, Item, Gap>,
253
254 chunk_identifier_generator: ChunkIdentifierGenerator,
256
257 updates: Option<ObservableUpdates<Item, Gap>>,
261
262 marker: PhantomData<Box<Chunk<CHUNK_CAPACITY, Item, Gap>>>,
264}
265
266impl<const CAP: usize, Item, Gap> Default for LinkedChunk<CAP, Item, Gap> {
267 fn default() -> Self {
268 Self::new()
269 }
270}
271
272impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
273 pub fn new() -> Self {
275 Self {
276 links: Ends {
277 first: Chunk::new_items_leaked(ChunkIdentifierGenerator::FIRST_IDENTIFIER),
278 last: None,
279 },
280 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
281 updates: None,
282 marker: PhantomData,
283 }
284 }
285
286 pub fn new_with_update_history() -> Self {
292 let first_chunk_identifier = ChunkIdentifierGenerator::FIRST_IDENTIFIER;
293
294 let mut updates = ObservableUpdates::new();
295 updates.push(Update::NewItemsChunk {
296 previous: None,
297 new: first_chunk_identifier,
298 next: None,
299 });
300
301 Self {
302 links: Ends { first: Chunk::new_items_leaked(first_chunk_identifier), last: None },
303 chunk_identifier_generator: ChunkIdentifierGenerator::new_from_scratch(),
304 updates: Some(updates),
305 marker: PhantomData,
306 }
307 }
308
309 pub fn clear(&mut self) {
311 self.links.clear();
313
314 self.chunk_identifier_generator = ChunkIdentifierGenerator::new_from_scratch();
316
317 if let Some(updates) = self.updates.as_mut() {
319 updates.clear_pending();
322 updates.push(Update::Clear);
323 updates.push(Update::NewItemsChunk {
324 previous: None,
325 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
326 next: None,
327 })
328 }
329 }
330
331 pub fn push_items_back<I>(&mut self, items: I)
337 where
338 Item: Clone,
339 Gap: Clone,
340 I: IntoIterator<Item = Item>,
341 I::IntoIter: ExactSizeIterator,
342 {
343 let items = items.into_iter();
344
345 let last_chunk = self.links.latest_chunk_mut();
346
347 let last_chunk =
349 last_chunk.push_items(items, &self.chunk_identifier_generator, &mut self.updates);
350
351 debug_assert!(last_chunk.is_last_chunk(), "`last_chunk` must be… the last chunk");
352
353 if !last_chunk.is_first_chunk() {
357 self.links.last = Some(last_chunk.as_ptr());
360 }
361 }
362
363 pub fn push_gap_back(&mut self, content: Gap)
366 where
367 Item: Clone,
368 Gap: Clone,
369 {
370 let last_chunk = self.links.latest_chunk_mut();
371 last_chunk.insert_next(
372 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
373 &mut self.updates,
374 );
375
376 self.links.last = last_chunk.next;
377 }
378
379 pub fn insert_items_at<I>(&mut self, items: I, position: Position) -> Result<(), Error>
384 where
385 Item: Clone,
386 Gap: Clone,
387 I: IntoIterator<Item = Item>,
388 I::IntoIter: ExactSizeIterator,
389 {
390 let chunk_identifier = position.chunk_identifier();
391 let item_index = position.index();
392
393 let chunk = self
394 .links
395 .chunk_mut(chunk_identifier)
396 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
397
398 let chunk = match &mut chunk.content {
399 ChunkContent::Gap(..) => {
400 return Err(Error::ChunkIsAGap { identifier: chunk_identifier })
401 }
402
403 ChunkContent::Items(current_items) => {
404 let current_items_length = current_items.len();
405
406 if item_index > current_items_length {
407 return Err(Error::InvalidItemIndex { index: item_index });
408 }
409
410 let items = items.into_iter();
412
413 if item_index == current_items_length {
415 chunk
416 .push_items(items, &self.chunk_identifier_generator, &mut self.updates)
418 }
419 else {
421 if let Some(updates) = self.updates.as_mut() {
422 updates.push(Update::DetachLastItems {
423 at: Position(chunk_identifier, item_index),
424 });
425 }
426
427 let detached_items = current_items.split_off(item_index);
429
430 let chunk = chunk
431 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
433
434 if let Some(updates) = self.updates.as_mut() {
435 updates.push(Update::StartReattachItems);
436 }
437
438 let chunk = chunk
439 .push_items(
441 detached_items.into_iter(),
442 &self.chunk_identifier_generator,
443 &mut self.updates,
444 );
445
446 if let Some(updates) = self.updates.as_mut() {
447 updates.push(Update::EndReattachItems);
448 }
449
450 chunk
451 }
452 }
453 };
454
455 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
458 self.links.last = Some(chunk.as_ptr());
461 }
462
463 Ok(())
464 }
465
466 pub fn remove_item_at(&mut self, position: Position) -> Result<Item, Error> {
474 let chunk_identifier = position.chunk_identifier();
475 let item_index = position.index();
476
477 let mut chunk_ptr = None;
478 let removed_item;
479
480 {
481 let chunk = self
482 .links
483 .chunk_mut(chunk_identifier)
484 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
485
486 let can_unlink_chunk = match &mut chunk.content {
487 ChunkContent::Gap(..) => {
488 return Err(Error::ChunkIsAGap { identifier: chunk_identifier })
489 }
490
491 ChunkContent::Items(current_items) => {
492 let current_items_length = current_items.len();
493
494 if item_index > current_items_length {
495 return Err(Error::InvalidItemIndex { index: item_index });
496 }
497
498 removed_item = current_items.remove(item_index);
499
500 if let Some(updates) = self.updates.as_mut() {
501 updates
502 .push(Update::RemoveItem { at: Position(chunk_identifier, item_index) })
503 }
504
505 current_items.is_empty()
506 }
507 };
508
509 if can_unlink_chunk && !chunk.is_first_chunk() {
512 chunk.unlink(self.updates.as_mut());
514
515 chunk_ptr = Some(chunk.as_ptr());
516
517 if chunk.is_last_chunk() {
520 self.links.last = chunk.previous;
521 }
522 }
523
524 }
526
527 if let Some(chunk_ptr) = chunk_ptr {
528 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
535 }
536
537 Ok(removed_item)
538 }
539
540 pub fn replace_item_at(&mut self, position: Position, item: Item) -> Result<(), Error>
545 where
546 Item: Clone,
547 {
548 let chunk_identifier = position.chunk_identifier();
549 let item_index = position.index();
550
551 let chunk = self
552 .links
553 .chunk_mut(chunk_identifier)
554 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
555
556 match &mut chunk.content {
557 ChunkContent::Gap(..) => {
558 return Err(Error::ChunkIsAGap { identifier: chunk_identifier })
559 }
560
561 ChunkContent::Items(current_items) => {
562 if item_index >= current_items.len() {
563 return Err(Error::InvalidItemIndex { index: item_index });
564 }
565
566 if let Some(updates) = self.updates.as_mut() {
568 updates.push(Update::ReplaceItem {
569 at: Position(chunk_identifier, item_index),
570 item: item.clone(),
571 });
572 }
573
574 current_items[item_index] = item;
575 }
576 }
577
578 Ok(())
579 }
580
581 pub fn insert_gap_at(&mut self, content: Gap, position: Position) -> Result<(), Error>
586 where
587 Item: Clone,
588 Gap: Clone,
589 {
590 let chunk_identifier = position.chunk_identifier();
591 let item_index = position.index();
592
593 let chunk = self
594 .links
595 .chunk_mut(chunk_identifier)
596 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
597
598 let chunk = match &mut chunk.content {
599 ChunkContent::Gap(..) => {
600 return Err(Error::ChunkIsAGap { identifier: chunk_identifier });
601 }
602
603 ChunkContent::Items(current_items) => {
604 if item_index == 0 {
608 let chunk_was_first = chunk.is_first_chunk();
609 let chunk_was_last = chunk.is_last_chunk();
610
611 let new_chunk = chunk.insert_before(
612 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
613 self.updates.as_mut(),
614 );
615
616 let new_chunk_ptr = new_chunk.as_ptr();
617 let chunk_ptr = chunk.as_ptr();
618
619 if chunk_was_first {
624 self.links.first = new_chunk_ptr;
625
626 if chunk_was_last {
628 self.links.last = Some(chunk_ptr);
629 }
630 }
631
632 return Ok(());
633 }
634
635 let current_items_length = current_items.len();
636
637 if item_index >= current_items_length {
638 return Err(Error::InvalidItemIndex { index: item_index });
639 }
640
641 if let Some(updates) = self.updates.as_mut() {
642 updates.push(Update::DetachLastItems {
643 at: Position(chunk_identifier, item_index),
644 });
645 }
646
647 let detached_items = current_items.split_off(item_index);
649
650 let chunk = chunk
651 .insert_next(
653 Chunk::new_gap_leaked(self.chunk_identifier_generator.next(), content),
654 &mut self.updates,
655 );
656
657 if let Some(updates) = self.updates.as_mut() {
658 updates.push(Update::StartReattachItems);
659 }
660
661 let chunk = chunk
662 .insert_next(
664 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
665 &mut self.updates,
666 )
667 .push_items(
669 detached_items.into_iter(),
670 &self.chunk_identifier_generator,
671 &mut self.updates,
672 );
673
674 if let Some(updates) = self.updates.as_mut() {
675 updates.push(Update::EndReattachItems);
676 }
677
678 chunk
679 }
680 };
681
682 if !chunk.is_first_chunk() && chunk.is_last_chunk() {
685 self.links.last = Some(chunk.as_ptr());
688 }
689
690 Ok(())
691 }
692
693 pub fn remove_empty_chunk_at(
702 &mut self,
703 chunk_identifier: ChunkIdentifier,
704 ) -> Result<Option<Position>, Error> {
705 if self.links.first_chunk().is_last_chunk() {
707 return Err(Error::RemovingLastChunk);
708 }
709
710 let chunk = self
711 .links
712 .chunk_mut(chunk_identifier)
713 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
714
715 if chunk.num_items() > 0 {
716 return Err(Error::RemovingNonEmptyItemsChunk { identifier: chunk_identifier });
717 }
718
719 let chunk_was_first = chunk.is_first_chunk();
720 let chunk_was_last = chunk.is_last_chunk();
721 let next_ptr = chunk.next;
722 let previous_ptr = chunk.previous;
723 let position_of_next = chunk.next().map(|next| next.first_position());
724
725 chunk.unlink(self.updates.as_mut());
726
727 let chunk_ptr = chunk.as_ptr();
728
729 if chunk_was_first {
731 if let Some(next_ptr) = next_ptr {
733 self.links.first = next_ptr;
734 }
735 }
736
737 if chunk_was_last {
738 self.links.last = previous_ptr;
739 }
740
741 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
744
745 Ok(position_of_next)
747 }
748
749 pub fn replace_gap_at<I>(
757 &mut self,
758 items: I,
759 chunk_identifier: ChunkIdentifier,
760 ) -> Result<&Chunk<CAP, Item, Gap>, Error>
761 where
762 Item: Clone,
763 Gap: Clone,
764 I: IntoIterator<Item = Item>,
765 I::IntoIter: ExactSizeIterator,
766 {
767 let chunk_ptr;
768 let new_chunk_ptr;
769
770 {
771 let chunk = self
772 .links
773 .chunk_mut(chunk_identifier)
774 .ok_or(Error::InvalidChunkIdentifier { identifier: chunk_identifier })?;
775
776 if chunk.is_items() {
777 return Err(Error::ChunkIsItems { identifier: chunk_identifier });
778 };
779
780 let chunk_was_first = chunk.is_first_chunk();
781
782 let maybe_last_chunk_ptr = {
783 let items = items.into_iter();
784
785 let last_inserted_chunk = chunk
786 .insert_next(
788 Chunk::new_items_leaked(self.chunk_identifier_generator.next()),
789 &mut self.updates,
790 )
791 .push_items(items, &self.chunk_identifier_generator, &mut self.updates);
793
794 last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr())
795 };
796
797 new_chunk_ptr = chunk
798 .next
799 .unwrap();
801
802 chunk.unlink(self.updates.as_mut());
804
805 chunk_ptr = chunk.as_ptr();
807
808 if chunk_was_first {
810 self.links.first = new_chunk_ptr;
811 }
812
813 if let Some(last_chunk_ptr) = maybe_last_chunk_ptr {
816 self.links.last = Some(last_chunk_ptr);
817 }
818
819 }
821
822 let _chunk_boxed = unsafe { Box::from_raw(chunk_ptr.as_ptr()) };
827
828 Ok(
829 unsafe { new_chunk_ptr.as_ref() },
833 )
834 }
835
836 pub fn chunk_identifier<'a, P>(&'a self, mut predicate: P) -> Option<ChunkIdentifier>
838 where
839 P: FnMut(&'a Chunk<CAP, Item, Gap>) -> bool,
840 {
841 self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier()))
842 }
843
844 pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option<Position>
846 where
847 P: FnMut(&'a Item) -> bool,
848 {
849 self.ritems().find_map(|(item_position, item)| predicate(item).then_some(item_position))
850 }
851
852 pub fn rchunks(&self) -> IterBackward<'_, CAP, Item, Gap> {
856 IterBackward::new(self.links.latest_chunk())
857 }
858
859 pub fn chunks(&self) -> Iter<'_, CAP, Item, Gap> {
863 Iter::new(self.links.first_chunk())
864 }
865
866 pub fn rchunks_from(
871 &self,
872 identifier: ChunkIdentifier,
873 ) -> Result<IterBackward<'_, CAP, Item, Gap>, Error> {
874 Ok(IterBackward::new(
875 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
876 ))
877 }
878
879 pub fn chunks_from(
884 &self,
885 identifier: ChunkIdentifier,
886 ) -> Result<Iter<'_, CAP, Item, Gap>, Error> {
887 Ok(Iter::new(
888 self.links.chunk(identifier).ok_or(Error::InvalidChunkIdentifier { identifier })?,
889 ))
890 }
891
892 pub fn ritems(&self) -> impl Iterator<Item = (Position, &Item)> {
896 self.ritems_from(self.links.latest_chunk().last_position())
897 .expect("`ritems_from` cannot fail because at least one empty chunk must exist")
898 }
899
900 pub fn items(&self) -> impl Iterator<Item = (Position, &Item)> {
904 let first_chunk = self.links.first_chunk();
905
906 self.items_from(first_chunk.first_position())
907 .expect("`items` cannot fail because at least one empty chunk must exist")
908 }
909
910 pub fn ritems_from(
914 &self,
915 position: Position,
916 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
917 Ok(self
918 .rchunks_from(position.chunk_identifier())?
919 .filter_map(|chunk| match &chunk.content {
920 ChunkContent::Gap(..) => None,
921 ChunkContent::Items(items) => {
922 let identifier = chunk.identifier();
923
924 Some(
925 items.iter().enumerate().rev().map(move |(item_index, item)| {
926 (Position(identifier, item_index), item)
927 }),
928 )
929 }
930 })
931 .flatten()
932 .skip_while({
933 let expected_index = position.index();
934
935 move |(Position(chunk_identifier, item_index), _item)| {
936 *chunk_identifier == position.chunk_identifier()
937 && *item_index != expected_index
938 }
939 }))
940 }
941
942 pub fn items_from(
946 &self,
947 position: Position,
948 ) -> Result<impl Iterator<Item = (Position, &Item)>, Error> {
949 Ok(self
950 .chunks_from(position.chunk_identifier())?
951 .filter_map(|chunk| match &chunk.content {
952 ChunkContent::Gap(..) => None,
953 ChunkContent::Items(items) => {
954 let identifier = chunk.identifier();
955
956 Some(
957 items.iter().enumerate().map(move |(item_index, item)| {
958 (Position(identifier, item_index), item)
959 }),
960 )
961 }
962 })
963 .flatten()
964 .skip(position.index()))
965 }
966
967 #[must_use]
979 pub fn updates(&mut self) -> Option<&mut ObservableUpdates<Item, Gap>> {
980 self.updates.as_mut()
981 }
982
983 pub fn as_vector(&mut self) -> Option<AsVector<Item, Gap>> {
990 let (updates, token) = self
991 .updates
992 .as_mut()
993 .map(|updates| (updates.inner.clone(), updates.new_reader_token()))?;
994 let chunk_iterator = self.chunks();
995
996 Some(AsVector::new(updates, token, chunk_iterator))
997 }
998
999 pub fn num_items(&self) -> usize {
1001 self.items().count()
1002 }
1003}
1004
1005impl<const CAP: usize, Item, Gap> Drop for LinkedChunk<CAP, Item, Gap> {
1006 fn drop(&mut self) {
1007 self.links.clear();
1012 }
1013}
1014
1015unsafe impl<const CAP: usize, Item: Send, Gap: Send> Send for LinkedChunk<CAP, Item, Gap> {}
1019
1020unsafe impl<const CAP: usize, Item: Sync, Gap: Sync> Sync for LinkedChunk<CAP, Item, Gap> {}
1024
1025#[derive(Debug)]
1035pub struct ChunkIdentifierGenerator {
1036 next: AtomicU64,
1037}
1038
1039impl ChunkIdentifierGenerator {
1040 const FIRST_IDENTIFIER: ChunkIdentifier = ChunkIdentifier(0);
1042
1043 pub fn new_from_scratch() -> Self {
1046 Self { next: AtomicU64::new(Self::FIRST_IDENTIFIER.0) }
1047 }
1048
1049 pub fn new_from_previous_chunk_identifier(last_chunk_identifier: ChunkIdentifier) -> Self {
1052 Self { next: AtomicU64::new(last_chunk_identifier.0) }
1053 }
1054
1055 fn next(&self) -> ChunkIdentifier {
1060 let previous = self.next.fetch_add(1, Ordering::Relaxed);
1061
1062 if previous == u64::MAX {
1065 panic!("No more chunk identifiers available. Congrats, you did it. 2^64 identifiers have been consumed.")
1066 }
1067
1068 ChunkIdentifier(previous + 1)
1069 }
1070
1071 #[doc(hidden)]
1075 pub fn current(&self) -> ChunkIdentifier {
1076 ChunkIdentifier(self.next.load(Ordering::Relaxed))
1077 }
1078}
1079
1080#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
1086#[repr(transparent)]
1087pub struct ChunkIdentifier(u64);
1088
1089impl ChunkIdentifier {
1090 pub fn new(identifier: u64) -> Self {
1092 Self(identifier)
1093 }
1094
1095 pub fn index(&self) -> u64 {
1097 self.0
1098 }
1099}
1100
1101impl PartialEq<u64> for ChunkIdentifier {
1102 fn eq(&self, other: &u64) -> bool {
1103 self.0 == *other
1104 }
1105}
1106
1107#[derive(Copy, Clone, Debug, PartialEq)]
1111pub struct Position(ChunkIdentifier, usize);
1112
1113impl Position {
1114 pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
1116 Self(chunk_identifier, index)
1117 }
1118
1119 pub fn chunk_identifier(&self) -> ChunkIdentifier {
1121 self.0
1122 }
1123
1124 pub fn index(&self) -> usize {
1126 self.1
1127 }
1128
1129 pub fn decrement_index(&mut self) {
1135 self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
1136 }
1137
1138 pub fn increment_index(&mut self) {
1145 self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
1146 }
1147}
1148
1149#[derive(Debug)]
1152pub struct IterBackward<'a, const CAP: usize, Item, Gap> {
1153 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1154}
1155
1156impl<'a, const CAP: usize, Item, Gap> IterBackward<'a, CAP, Item, Gap> {
1157 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1159 Self { chunk: Some(from_chunk) }
1160 }
1161}
1162
1163impl<'a, const CAP: usize, Item, Gap> Iterator for IterBackward<'a, CAP, Item, Gap> {
1164 type Item = &'a Chunk<CAP, Item, Gap>;
1165
1166 fn next(&mut self) -> Option<Self::Item> {
1167 self.chunk.inspect(|chunk| self.chunk = chunk.previous())
1168 }
1169}
1170
1171#[derive(Debug)]
1174pub struct Iter<'a, const CAP: usize, Item, Gap> {
1175 chunk: Option<&'a Chunk<CAP, Item, Gap>>,
1176}
1177
1178impl<'a, const CAP: usize, Item, Gap> Iter<'a, CAP, Item, Gap> {
1179 fn new(from_chunk: &'a Chunk<CAP, Item, Gap>) -> Self {
1181 Self { chunk: Some(from_chunk) }
1182 }
1183}
1184
1185impl<'a, const CAP: usize, Item, Gap> Iterator for Iter<'a, CAP, Item, Gap> {
1186 type Item = &'a Chunk<CAP, Item, Gap>;
1187
1188 fn next(&mut self) -> Option<Self::Item> {
1189 self.chunk.inspect(|chunk| self.chunk = chunk.next())
1190 }
1191}
1192
1193#[derive(Clone, Debug)]
1195pub enum ChunkContent<Item, Gap> {
1196 Gap(Gap),
1199
1200 Items(Vec<Item>),
1202}
1203
1204pub struct Chunk<const CAPACITY: usize, Item, Gap> {
1206 previous: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1208
1209 lazy_previous: Option<ChunkIdentifier>,
1214
1215 next: Option<NonNull<Chunk<CAPACITY, Item, Gap>>>,
1217
1218 identifier: ChunkIdentifier,
1220
1221 content: ChunkContent<Item, Gap>,
1223}
1224
1225impl<const CAPACITY: usize, Item, Gap> Chunk<CAPACITY, Item, Gap> {
1226 fn new_gap(identifier: ChunkIdentifier, content: Gap) -> Self {
1228 Self::new(identifier, ChunkContent::Gap(content))
1229 }
1230
1231 fn new_items(identifier: ChunkIdentifier) -> Self {
1233 Self::new(identifier, ChunkContent::Items(Vec::with_capacity(CAPACITY)))
1234 }
1235
1236 fn new(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> Self {
1237 Self { previous: None, lazy_previous: None, next: None, identifier, content }
1238 }
1239
1240 fn new_leaked(identifier: ChunkIdentifier, content: ChunkContent<Item, Gap>) -> NonNull<Self> {
1242 let chunk = Self::new(identifier, content);
1243 let chunk_box = Box::new(chunk);
1244
1245 NonNull::from(Box::leak(chunk_box))
1246 }
1247
1248 fn new_gap_leaked(identifier: ChunkIdentifier, content: Gap) -> NonNull<Self> {
1250 let chunk = Self::new_gap(identifier, content);
1251 let chunk_box = Box::new(chunk);
1252
1253 NonNull::from(Box::leak(chunk_box))
1254 }
1255
1256 fn new_items_leaked(identifier: ChunkIdentifier) -> NonNull<Self> {
1258 let chunk = Self::new_items(identifier);
1259 let chunk_box = Box::new(chunk);
1260
1261 NonNull::from(Box::leak(chunk_box))
1262 }
1263
1264 pub fn as_ptr(&self) -> NonNull<Self> {
1266 NonNull::from(self)
1267 }
1268
1269 pub fn is_gap(&self) -> bool {
1271 matches!(self.content, ChunkContent::Gap(..))
1272 }
1273
1274 pub fn is_items(&self) -> bool {
1276 !self.is_gap()
1277 }
1278
1279 pub fn is_definitive_head(&self) -> bool {
1282 self.previous.is_none() && self.lazy_previous.is_none()
1283 }
1284
1285 fn is_first_chunk(&self) -> bool {
1287 self.previous.is_none()
1288 }
1289
1290 fn is_last_chunk(&self) -> bool {
1292 self.next.is_none()
1293 }
1294
1295 #[doc(hidden)]
1299 pub fn lazy_previous(&self) -> Option<ChunkIdentifier> {
1300 self.lazy_previous
1301 }
1302
1303 pub fn identifier(&self) -> ChunkIdentifier {
1305 self.identifier
1306 }
1307
1308 pub fn content(&self) -> &ChunkContent<Item, Gap> {
1310 &self.content
1311 }
1312
1313 pub fn first_position(&self) -> Position {
1317 Position(self.identifier(), 0)
1318 }
1319
1320 pub fn last_position(&self) -> Position {
1324 let identifier = self.identifier();
1325
1326 match &self.content {
1327 ChunkContent::Gap(..) => Position(identifier, 0),
1328 ChunkContent::Items(items) => Position(identifier, items.len().saturating_sub(1)),
1329 }
1330 }
1331
1332 pub fn num_items(&self) -> usize {
1336 match &self.content {
1337 ChunkContent::Gap(..) => 0,
1338 ChunkContent::Items(items) => items.len(),
1339 }
1340 }
1341
1342 fn push_items<I>(
1354 &mut self,
1355 mut new_items: I,
1356 chunk_identifier_generator: &ChunkIdentifierGenerator,
1357 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1358 ) -> &mut Self
1359 where
1360 I: Iterator<Item = Item> + ExactSizeIterator,
1361 Item: Clone,
1362 Gap: Clone,
1363 {
1364 if new_items.len() == 0 {
1366 return self;
1367 }
1368
1369 let identifier = self.identifier();
1370 let prev_num_items = self.num_items();
1371
1372 match &mut self.content {
1373 ChunkContent::Gap(..) => {
1376 self
1377 .insert_next(Self::new_items_leaked(chunk_identifier_generator.next()), updates)
1379 .push_items(new_items, chunk_identifier_generator, updates)
1382 }
1383
1384 ChunkContent::Items(items) => {
1385 let free_space = CAPACITY.saturating_sub(prev_num_items);
1387
1388 if new_items.len() <= free_space {
1390 let start = items.len();
1391 items.extend(new_items);
1392
1393 if let Some(updates) = updates.as_mut() {
1394 updates.push(Update::PushItems {
1395 at: Position(identifier, start),
1396 items: items[start..].to_vec(),
1397 });
1398 }
1399
1400 self
1402 } else {
1403 if free_space > 0 {
1404 let start = items.len();
1406 items.extend(new_items.by_ref().take(free_space));
1407
1408 if let Some(updates) = updates.as_mut() {
1409 updates.push(Update::PushItems {
1410 at: Position(identifier, start),
1411 items: items[start..].to_vec(),
1412 });
1413 }
1414 }
1415
1416 self
1417 .insert_next(
1419 Self::new_items_leaked(chunk_identifier_generator.next()),
1420 updates,
1421 )
1422 .push_items(new_items, chunk_identifier_generator, updates)
1425 }
1426 }
1427 }
1428 }
1429
1430 fn insert_next(
1435 &mut self,
1436 mut new_chunk_ptr: NonNull<Self>,
1437 updates: &mut Option<ObservableUpdates<Item, Gap>>,
1438 ) -> &mut Self
1439 where
1440 Gap: Clone,
1441 {
1442 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1443
1444 if let Some(next_chunk) = self.next_mut() {
1446 next_chunk.previous = Some(new_chunk_ptr);
1448
1449 new_chunk.next = self.next;
1451 }
1452
1453 self.next = Some(new_chunk_ptr);
1455 new_chunk.previous = Some(self.as_ptr());
1457
1458 if let Some(updates) = updates.as_mut() {
1459 let previous = new_chunk.previous().map(Chunk::identifier);
1460 let new = new_chunk.identifier();
1461 let next = new_chunk.next().map(Chunk::identifier);
1462
1463 match new_chunk.content() {
1464 ChunkContent::Gap(gap) => {
1465 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1466 }
1467
1468 ChunkContent::Items(..) => {
1469 updates.push(Update::NewItemsChunk { previous, new, next })
1470 }
1471 }
1472 }
1473
1474 new_chunk
1475 }
1476
1477 fn insert_before(
1482 &mut self,
1483 mut new_chunk_ptr: NonNull<Self>,
1484 updates: Option<&mut ObservableUpdates<Item, Gap>>,
1485 ) -> &mut Self
1486 where
1487 Gap: Clone,
1488 {
1489 let new_chunk = unsafe { new_chunk_ptr.as_mut() };
1490
1491 if let Some(previous_chunk) = self.previous_mut() {
1493 previous_chunk.next = Some(new_chunk_ptr);
1495
1496 new_chunk.previous = self.previous;
1498 }
1499 else {
1502 new_chunk.lazy_previous = self.lazy_previous.take();
1503 }
1504
1505 self.previous = Some(new_chunk_ptr);
1507 new_chunk.next = Some(self.as_ptr());
1509
1510 if let Some(updates) = updates {
1511 let previous = new_chunk.previous().map(Chunk::identifier).or(new_chunk.lazy_previous);
1512 let new = new_chunk.identifier();
1513 let next = new_chunk.next().map(Chunk::identifier);
1514
1515 match new_chunk.content() {
1516 ChunkContent::Gap(gap) => {
1517 updates.push(Update::NewGapChunk { previous, new, next, gap: gap.clone() })
1518 }
1519
1520 ChunkContent::Items(..) => {
1521 updates.push(Update::NewItemsChunk { previous, new, next })
1522 }
1523 }
1524 }
1525
1526 new_chunk
1527 }
1528
1529 fn unlink(&mut self, updates: Option<&mut ObservableUpdates<Item, Gap>>) {
1534 let previous_ptr = self.previous;
1535 let next_ptr = self.next;
1536 let lazy_previous = self.lazy_previous.take();
1540
1541 if let Some(previous) = self.previous_mut() {
1542 previous.next = next_ptr;
1543 }
1544
1545 if let Some(next) = self.next_mut() {
1546 next.previous = previous_ptr;
1547 next.lazy_previous = lazy_previous;
1548 }
1549
1550 if let Some(updates) = updates {
1551 updates.push(Update::RemoveChunk(self.identifier()));
1552 }
1553 }
1554
1555 fn previous(&self) -> Option<&Self> {
1557 self.previous.map(|non_null| unsafe { non_null.as_ref() })
1558 }
1559
1560 fn previous_mut(&mut self) -> Option<&mut Self> {
1562 self.previous.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1563 }
1564
1565 fn next(&self) -> Option<&Self> {
1567 self.next.map(|non_null| unsafe { non_null.as_ref() })
1568 }
1569
1570 fn next_mut(&mut self) -> Option<&mut Self> {
1572 self.next.as_mut().map(|non_null| unsafe { non_null.as_mut() })
1573 }
1574}
1575
1576impl<const CAP: usize, Item, Gap> fmt::Debug for LinkedChunk<CAP, Item, Gap>
1577where
1578 Item: fmt::Debug,
1579 Gap: fmt::Debug,
1580{
1581 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1582 formatter
1583 .debug_struct("LinkedChunk")
1584 .field("first (deref)", unsafe { self.links.first.as_ref() })
1585 .field("last", &self.links.last)
1586 .finish_non_exhaustive()
1587 }
1588}
1589
1590impl<const CAP: usize, Item, Gap> fmt::Debug for Chunk<CAP, Item, Gap>
1591where
1592 Item: fmt::Debug,
1593 Gap: fmt::Debug,
1594{
1595 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1596 formatter
1597 .debug_struct("Chunk")
1598 .field("identifier", &self.identifier)
1599 .field("content", &self.content)
1600 .field("previous", &self.previous)
1601 .field("ptr", &std::ptr::from_ref(self))
1602 .field("next", &self.next)
1603 .field("next (deref)", &self.next.as_ref().map(|non_null| unsafe { non_null.as_ref() }))
1604 .finish()
1605 }
1606}
1607
1608#[derive(Clone, Debug)]
1614pub struct RawChunk<Item, Gap> {
1615 pub content: ChunkContent<Item, Gap>,
1617
1618 pub previous: Option<ChunkIdentifier>,
1620
1621 pub identifier: ChunkIdentifier,
1623
1624 pub next: Option<ChunkIdentifier>,
1626}
1627
1628#[cfg(test)]
1629mod tests {
1630 use std::{
1631 ops::Not,
1632 sync::{atomic::Ordering, Arc},
1633 };
1634
1635 use assert_matches::assert_matches;
1636
1637 use super::{
1638 Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Error, LinkedChunk,
1639 Position,
1640 };
1641
1642 #[test]
1643 fn test_chunk_identifier_generator() {
1644 let generator = ChunkIdentifierGenerator::new_from_scratch();
1645
1646 assert_eq!(generator.next(), ChunkIdentifier(1));
1647 assert_eq!(generator.next(), ChunkIdentifier(2));
1648 assert_eq!(generator.next(), ChunkIdentifier(3));
1649 assert_eq!(generator.next(), ChunkIdentifier(4));
1650
1651 let generator =
1652 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(42));
1653
1654 assert_eq!(generator.next(), ChunkIdentifier(43));
1655 assert_eq!(generator.next(), ChunkIdentifier(44));
1656 assert_eq!(generator.next(), ChunkIdentifier(45));
1657 assert_eq!(generator.next(), ChunkIdentifier(46));
1658 }
1659
1660 #[test]
1661 fn test_empty() {
1662 let items = LinkedChunk::<3, char, ()>::new();
1663
1664 assert_eq!(items.num_items(), 0);
1665
1666 }
1669
1670 #[test]
1671 fn test_updates() {
1672 assert!(LinkedChunk::<3, char, ()>::new().updates().is_none());
1673 assert!(LinkedChunk::<3, char, ()>::new_with_update_history().updates().is_some());
1674 }
1675
1676 #[test]
1677 fn test_new_with_initial_update() {
1678 use super::Update::*;
1679
1680 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1681
1682 assert_eq!(
1683 linked_chunk.updates().unwrap().take(),
1684 &[NewItemsChunk { previous: None, new: ChunkIdentifier(0), next: None }]
1685 );
1686 }
1687
1688 #[test]
1689 fn test_push_items() {
1690 use super::Update::*;
1691
1692 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1693
1694 let _ = linked_chunk.updates().unwrap().take();
1696
1697 linked_chunk.push_items_back(['a']);
1698
1699 assert_items_eq!(linked_chunk, ['a']);
1700 assert_eq!(
1701 linked_chunk.updates().unwrap().take(),
1702 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1703 );
1704
1705 linked_chunk.push_items_back(['b', 'c']);
1706 assert_items_eq!(linked_chunk, ['a', 'b', 'c']);
1707 assert_eq!(
1708 linked_chunk.updates().unwrap().take(),
1709 &[PushItems { at: Position(ChunkIdentifier(0), 1), items: vec!['b', 'c'] }]
1710 );
1711
1712 linked_chunk.push_items_back(['d', 'e']);
1713 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
1714 assert_eq!(
1715 linked_chunk.updates().unwrap().take(),
1716 &[
1717 NewItemsChunk {
1718 previous: Some(ChunkIdentifier(0)),
1719 new: ChunkIdentifier(1),
1720 next: None
1721 },
1722 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] }
1723 ]
1724 );
1725
1726 linked_chunk.push_items_back(['f', 'g', 'h', 'i', 'j']);
1727 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j']);
1728 assert_eq!(
1729 linked_chunk.updates().unwrap().take(),
1730 &[
1731 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['f'] },
1732 NewItemsChunk {
1733 previous: Some(ChunkIdentifier(1)),
1734 new: ChunkIdentifier(2),
1735 next: None,
1736 },
1737 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h', 'i'] },
1738 NewItemsChunk {
1739 previous: Some(ChunkIdentifier(2)),
1740 new: ChunkIdentifier(3),
1741 next: None,
1742 },
1743 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['j'] },
1744 ]
1745 );
1746
1747 assert_eq!(linked_chunk.num_items(), 10);
1748 }
1749
1750 #[test]
1751 fn test_push_gap() {
1752 use super::Update::*;
1753
1754 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
1755
1756 let _ = linked_chunk.updates().unwrap().take();
1758
1759 linked_chunk.push_items_back(['a']);
1760 assert_items_eq!(linked_chunk, ['a']);
1761 assert_eq!(
1762 linked_chunk.updates().unwrap().take(),
1763 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a'] }]
1764 );
1765
1766 linked_chunk.push_gap_back(());
1767 assert_items_eq!(linked_chunk, ['a'] [-]);
1768 assert_eq!(
1769 linked_chunk.updates().unwrap().take(),
1770 &[NewGapChunk {
1771 previous: Some(ChunkIdentifier(0)),
1772 new: ChunkIdentifier(1),
1773 next: None,
1774 gap: (),
1775 }]
1776 );
1777
1778 linked_chunk.push_items_back(['b', 'c', 'd', 'e']);
1779 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e']);
1780 assert_eq!(
1781 linked_chunk.updates().unwrap().take(),
1782 &[
1783 NewItemsChunk {
1784 previous: Some(ChunkIdentifier(1)),
1785 new: ChunkIdentifier(2),
1786 next: None,
1787 },
1788 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['b', 'c', 'd'] },
1789 NewItemsChunk {
1790 previous: Some(ChunkIdentifier(2)),
1791 new: ChunkIdentifier(3),
1792 next: None,
1793 },
1794 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e'] },
1795 ]
1796 );
1797
1798 linked_chunk.push_gap_back(());
1799 linked_chunk.push_gap_back(()); assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-]);
1801 assert_eq!(
1802 linked_chunk.updates().unwrap().take(),
1803 &[
1804 NewGapChunk {
1805 previous: Some(ChunkIdentifier(3)),
1806 new: ChunkIdentifier(4),
1807 next: None,
1808 gap: (),
1809 },
1810 NewGapChunk {
1811 previous: Some(ChunkIdentifier(4)),
1812 new: ChunkIdentifier(5),
1813 next: None,
1814 gap: (),
1815 }
1816 ]
1817 );
1818
1819 linked_chunk.push_items_back(['f', 'g', 'h', 'i']);
1820 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c', 'd'] ['e'] [-] [-] ['f', 'g', 'h'] ['i']);
1821 assert_eq!(
1822 linked_chunk.updates().unwrap().take(),
1823 &[
1824 NewItemsChunk {
1825 previous: Some(ChunkIdentifier(5)),
1826 new: ChunkIdentifier(6),
1827 next: None,
1828 },
1829 PushItems { at: Position(ChunkIdentifier(6), 0), items: vec!['f', 'g', 'h'] },
1830 NewItemsChunk {
1831 previous: Some(ChunkIdentifier(6)),
1832 new: ChunkIdentifier(7),
1833 next: None,
1834 },
1835 PushItems { at: Position(ChunkIdentifier(7), 0), items: vec!['i'] },
1836 ]
1837 );
1838
1839 assert_eq!(linked_chunk.num_items(), 9);
1840 }
1841
1842 #[test]
1843 fn test_identifiers_and_positions() {
1844 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
1845 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
1846 linked_chunk.push_gap_back(());
1847 linked_chunk.push_items_back(['g', 'h', 'i', 'j']);
1848 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] [-] ['g', 'h', 'i'] ['j']);
1849
1850 assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2)));
1851 assert_eq!(
1852 linked_chunk.item_position(|item| *item == 'e'),
1853 Some(Position(ChunkIdentifier(1), 1))
1854 );
1855 }
1856
1857 #[test]
1858 fn test_rchunks() {
1859 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1860 linked_chunk.push_items_back(['a', 'b']);
1861 linked_chunk.push_gap_back(());
1862 linked_chunk.push_items_back(['c', 'd', 'e']);
1863
1864 let mut iterator = linked_chunk.rchunks();
1865
1866 assert_matches!(
1867 iterator.next(),
1868 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
1869 assert_eq!(items, &['e']);
1870 }
1871 );
1872 assert_matches!(
1873 iterator.next(),
1874 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1875 assert_eq!(items, &['c', 'd']);
1876 }
1877 );
1878 assert_matches!(
1879 iterator.next(),
1880 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
1881 );
1882 assert_matches!(
1883 iterator.next(),
1884 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
1885 assert_eq!(items, &['a', 'b']);
1886 }
1887 );
1888 assert_matches!(iterator.next(), None);
1889 }
1890
1891 #[test]
1892 fn test_chunks() {
1893 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1894 linked_chunk.push_items_back(['a', 'b']);
1895 linked_chunk.push_gap_back(());
1896 linked_chunk.push_items_back(['c', 'd', 'e']);
1897
1898 let mut iterator = linked_chunk.chunks();
1899
1900 assert_matches!(
1901 iterator.next(),
1902 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
1903 assert_eq!(items, &['a', 'b']);
1904 }
1905 );
1906 assert_matches!(
1907 iterator.next(),
1908 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
1909 );
1910 assert_matches!(
1911 iterator.next(),
1912 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1913 assert_eq!(items, &['c', 'd']);
1914 }
1915 );
1916 assert_matches!(
1917 iterator.next(),
1918 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
1919 assert_eq!(items, &['e']);
1920 }
1921 );
1922 assert_matches!(iterator.next(), None);
1923 }
1924
1925 #[test]
1926 fn test_rchunks_from() -> Result<(), Error> {
1927 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1928 linked_chunk.push_items_back(['a', 'b']);
1929 linked_chunk.push_gap_back(());
1930 linked_chunk.push_items_back(['c', 'd', 'e']);
1931
1932 let mut iterator = linked_chunk.rchunks_from(
1933 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
1934 )?;
1935
1936 assert_matches!(
1937 iterator.next(),
1938 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1939 assert_eq!(items, &['c', 'd']);
1940 }
1941 );
1942 assert_matches!(
1943 iterator.next(),
1944 Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. })
1945 );
1946 assert_matches!(
1947 iterator.next(),
1948 Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => {
1949 assert_eq!(items, &['a', 'b']);
1950 }
1951 );
1952 assert_matches!(iterator.next(), None);
1953
1954 Ok(())
1955 }
1956
1957 #[test]
1958 fn test_chunks_from() -> Result<(), Error> {
1959 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1960 linked_chunk.push_items_back(['a', 'b']);
1961 linked_chunk.push_gap_back(());
1962 linked_chunk.push_items_back(['c', 'd', 'e']);
1963
1964 let mut iterator = linked_chunk.chunks_from(
1965 linked_chunk.item_position(|item| *item == 'c').unwrap().chunk_identifier(),
1966 )?;
1967
1968 assert_matches!(
1969 iterator.next(),
1970 Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => {
1971 assert_eq!(items, &['c', 'd']);
1972 }
1973 );
1974 assert_matches!(
1975 iterator.next(),
1976 Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => {
1977 assert_eq!(items, &['e']);
1978 }
1979 );
1980 assert_matches!(iterator.next(), None);
1981
1982 Ok(())
1983 }
1984
1985 #[test]
1986 fn test_ritems() {
1987 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
1988 linked_chunk.push_items_back(['a', 'b']);
1989 linked_chunk.push_gap_back(());
1990 linked_chunk.push_items_back(['c', 'd', 'e']);
1991
1992 let mut iterator = linked_chunk.ritems();
1993
1994 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
1995 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
1996 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
1997 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
1998 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
1999 assert_matches!(iterator.next(), None);
2000 }
2001
2002 #[test]
2003 fn test_ritems_with_final_gap() -> Result<(), Error> {
2004 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
2005 linked_chunk.push_items_back(['a', 'b']);
2006 linked_chunk.push_gap_back(());
2007 linked_chunk.push_items_back(['c', 'd', 'e']);
2008 linked_chunk.push_gap_back(());
2009
2010 let mut iterator = linked_chunk.ritems();
2011
2012 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 2), 'e')));
2013 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2014 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2015 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2016 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2017 assert_matches!(iterator.next(), None);
2018
2019 Ok(())
2020 }
2021
2022 #[test]
2023 fn test_ritems_empty() {
2024 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2025 let mut iterator = linked_chunk.ritems();
2026
2027 assert_matches!(iterator.next(), None);
2028 }
2029
2030 #[test]
2031 fn test_items() {
2032 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2033 linked_chunk.push_items_back(['a', 'b']);
2034 linked_chunk.push_gap_back(());
2035 linked_chunk.push_items_back(['c', 'd', 'e']);
2036
2037 let mut iterator = linked_chunk.items();
2038
2039 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2040 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2041 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2042 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2043 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2044 assert_matches!(iterator.next(), None);
2045 }
2046
2047 #[test]
2048 fn test_items_empty() {
2049 let linked_chunk = LinkedChunk::<2, char, ()>::new();
2050 let mut iterator = linked_chunk.items();
2051
2052 assert_matches!(iterator.next(), None);
2053 }
2054
2055 #[test]
2056 fn test_ritems_from() -> Result<(), Error> {
2057 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2058 linked_chunk.push_items_back(['a', 'b']);
2059 linked_chunk.push_gap_back(());
2060 linked_chunk.push_items_back(['c', 'd', 'e']);
2061
2062 let mut iterator =
2063 linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2064
2065 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2066 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b')));
2067 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a')));
2068 assert_matches!(iterator.next(), None);
2069
2070 Ok(())
2071 }
2072
2073 #[test]
2074 fn test_items_from() -> Result<(), Error> {
2075 let mut linked_chunk = LinkedChunk::<2, char, ()>::new();
2076 linked_chunk.push_items_back(['a', 'b']);
2077 linked_chunk.push_gap_back(());
2078 linked_chunk.push_items_back(['c', 'd', 'e']);
2079
2080 let mut iterator =
2081 linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?;
2082
2083 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c')));
2084 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd')));
2085 assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e')));
2086 assert_matches!(iterator.next(), None);
2087
2088 Ok(())
2089 }
2090
2091 #[test]
2092 fn test_insert_items_at() -> Result<(), Error> {
2093 use super::Update::*;
2094
2095 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2096
2097 let _ = linked_chunk.updates().unwrap().take();
2099
2100 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2101 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2102 assert_eq!(
2103 linked_chunk.updates().unwrap().take(),
2104 &[
2105 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2106 NewItemsChunk {
2107 previous: Some(ChunkIdentifier(0)),
2108 new: ChunkIdentifier(1),
2109 next: None,
2110 },
2111 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2112 ]
2113 );
2114
2115 {
2117 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2118
2119 linked_chunk.insert_items_at(['w', 'x', 'y', 'z'], position_of_e)?;
2122
2123 assert_items_eq!(
2124 linked_chunk,
2125 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2126 );
2127 assert_eq!(linked_chunk.num_items(), 10);
2128 assert_eq!(
2129 linked_chunk.updates().unwrap().take(),
2130 &[
2131 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2132 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2133 NewItemsChunk {
2134 previous: Some(ChunkIdentifier(1)),
2135 new: ChunkIdentifier(2),
2136 next: None,
2137 },
2138 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2139 StartReattachItems,
2140 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2141 NewItemsChunk {
2142 previous: Some(ChunkIdentifier(2)),
2143 new: ChunkIdentifier(3),
2144 next: None,
2145 },
2146 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2147 EndReattachItems,
2148 ]
2149 );
2150 }
2151
2152 {
2154 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2155 linked_chunk.insert_items_at(['l', 'm', 'n', 'o'], position_of_a)?;
2156
2157 assert_items_eq!(
2158 linked_chunk,
2159 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2160 );
2161 assert_eq!(linked_chunk.num_items(), 14);
2162 assert_eq!(
2163 linked_chunk.updates().unwrap().take(),
2164 &[
2165 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2166 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2167 NewItemsChunk {
2168 previous: Some(ChunkIdentifier(0)),
2169 new: ChunkIdentifier(4),
2170 next: Some(ChunkIdentifier(1)),
2171 },
2172 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['o'] },
2173 StartReattachItems,
2174 PushItems { at: Position(ChunkIdentifier(4), 1), items: vec!['a', 'b'] },
2175 NewItemsChunk {
2176 previous: Some(ChunkIdentifier(4)),
2177 new: ChunkIdentifier(5),
2178 next: Some(ChunkIdentifier(1)),
2179 },
2180 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['c'] },
2181 EndReattachItems,
2182 ]
2183 );
2184 }
2185
2186 {
2188 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2189 linked_chunk.insert_items_at(['r', 's'], position_of_c)?;
2190
2191 assert_items_eq!(
2192 linked_chunk,
2193 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2194 );
2195 assert_eq!(linked_chunk.num_items(), 16);
2196 assert_eq!(
2197 linked_chunk.updates().unwrap().take(),
2198 &[
2199 DetachLastItems { at: Position(ChunkIdentifier(5), 0) },
2200 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['r', 's'] },
2201 StartReattachItems,
2202 PushItems { at: Position(ChunkIdentifier(5), 2), items: vec!['c'] },
2203 EndReattachItems,
2204 ]
2205 );
2206 }
2207
2208 {
2210 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2211 let position_after_f =
2212 Position(position_of_f.chunk_identifier(), position_of_f.index() + 1);
2213
2214 linked_chunk.insert_items_at(['p', 'q'], position_after_f)?;
2215 assert_items_eq!(
2216 linked_chunk,
2217 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q']
2218 );
2219 assert_eq!(
2220 linked_chunk.updates().unwrap().take(),
2221 &[PushItems { at: Position(ChunkIdentifier(3), 1), items: vec!['p', 'q'] }]
2222 );
2223 assert_eq!(linked_chunk.num_items(), 18);
2224 }
2225
2226 {
2228 assert_matches!(
2229 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)),
2230 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2231 );
2232 assert!(linked_chunk.updates().unwrap().take().is_empty());
2233 }
2234
2235 {
2237 assert_matches!(
2238 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)),
2239 Err(Error::InvalidItemIndex { index: 128 })
2240 );
2241 assert!(linked_chunk.updates().unwrap().take().is_empty());
2242 }
2243
2244 {
2246 linked_chunk.push_gap_back(());
2248 assert_items_eq!(
2249 linked_chunk,
2250 ['l', 'm', 'n'] ['o', 'a', 'b'] ['r', 's', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f', 'p', 'q'] [-]
2251 );
2252 assert_eq!(
2253 linked_chunk.updates().unwrap().take(),
2254 &[NewGapChunk {
2255 previous: Some(ChunkIdentifier(3)),
2256 new: ChunkIdentifier(6),
2257 next: None,
2258 gap: ()
2259 }]
2260 );
2261
2262 assert_matches!(
2263 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(6), 0)),
2264 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(6) })
2265 );
2266 }
2267
2268 assert_eq!(linked_chunk.num_items(), 18);
2269
2270 Ok(())
2271 }
2272
2273 #[test]
2274 fn test_insert_items_at_last_chunk() -> Result<(), Error> {
2275 use super::Update::*;
2276
2277 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2278
2279 let _ = linked_chunk.updates().unwrap().take();
2281
2282 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2283 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2284 assert_eq!(
2285 linked_chunk.updates().unwrap().take(),
2286 &[
2287 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2288 NewItemsChunk {
2289 previous: Some(ChunkIdentifier(0)),
2290 new: ChunkIdentifier(1),
2291 next: None,
2292 },
2293 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2294 ]
2295 );
2296
2297 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2299
2300 linked_chunk.insert_items_at(['w', 'x', 'y', 'z'], position_of_e)?;
2303
2304 assert_items_eq!(
2305 linked_chunk,
2306 ['a', 'b', 'c'] ['d', 'w', 'x'] ['y', 'z', 'e'] ['f']
2307 );
2308 assert_eq!(linked_chunk.num_items(), 10);
2309 assert_eq!(
2310 linked_chunk.updates().unwrap().take(),
2311 &[
2312 DetachLastItems { at: Position(ChunkIdentifier(1), 1) },
2313 PushItems { at: Position(ChunkIdentifier(1), 1), items: vec!['w', 'x'] },
2314 NewItemsChunk {
2315 previous: Some(ChunkIdentifier(1)),
2316 new: ChunkIdentifier(2),
2317 next: None,
2318 },
2319 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['y', 'z'] },
2320 StartReattachItems,
2321 PushItems { at: Position(ChunkIdentifier(2), 2), items: vec!['e'] },
2322 NewItemsChunk {
2323 previous: Some(ChunkIdentifier(2)),
2324 new: ChunkIdentifier(3),
2325 next: None,
2326 },
2327 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['f'] },
2328 EndReattachItems,
2329 ]
2330 );
2331
2332 Ok(())
2333 }
2334
2335 #[test]
2336 fn test_insert_items_at_first_chunk() -> Result<(), Error> {
2337 use super::Update::*;
2338
2339 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2340
2341 let _ = linked_chunk.updates().unwrap().take();
2343
2344 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2345 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2346 assert_eq!(
2347 linked_chunk.updates().unwrap().take(),
2348 &[
2349 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2350 NewItemsChunk {
2351 previous: Some(ChunkIdentifier(0)),
2352 new: ChunkIdentifier(1),
2353 next: None,
2354 },
2355 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2356 ]
2357 );
2358
2359 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2361 linked_chunk.insert_items_at(['l', 'm', 'n', 'o'], position_of_a)?;
2362
2363 assert_items_eq!(
2364 linked_chunk,
2365 ['l', 'm', 'n'] ['o', 'a', 'b'] ['c'] ['d', 'e', 'f']
2366 );
2367 assert_eq!(linked_chunk.num_items(), 10);
2368 assert_eq!(
2369 linked_chunk.updates().unwrap().take(),
2370 &[
2371 DetachLastItems { at: Position(ChunkIdentifier(0), 0) },
2372 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['l', 'm', 'n'] },
2373 NewItemsChunk {
2374 previous: Some(ChunkIdentifier(0)),
2375 new: ChunkIdentifier(2),
2376 next: Some(ChunkIdentifier(1)),
2377 },
2378 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['o'] },
2379 StartReattachItems,
2380 PushItems { at: Position(ChunkIdentifier(2), 1), items: vec!['a', 'b'] },
2381 NewItemsChunk {
2382 previous: Some(ChunkIdentifier(2)),
2383 new: ChunkIdentifier(3),
2384 next: Some(ChunkIdentifier(1)),
2385 },
2386 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['c'] },
2387 EndReattachItems,
2388 ]
2389 );
2390
2391 Ok(())
2392 }
2393
2394 #[test]
2395 fn test_insert_items_at_middle_chunk() -> Result<(), Error> {
2396 use super::Update::*;
2397
2398 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2399
2400 let _ = linked_chunk.updates().unwrap().take();
2402
2403 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
2404 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h']);
2405 assert_eq!(
2406 linked_chunk.updates().unwrap().take(),
2407 &[
2408 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2409 NewItemsChunk {
2410 previous: Some(ChunkIdentifier(0)),
2411 new: ChunkIdentifier(1),
2412 next: None,
2413 },
2414 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2415 NewItemsChunk {
2416 previous: Some(ChunkIdentifier(1)),
2417 new: ChunkIdentifier(2),
2418 next: None,
2419 },
2420 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['g', 'h'] },
2421 ]
2422 );
2423
2424 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2425 linked_chunk.insert_items_at(['r', 's'], position_of_d)?;
2426
2427 assert_items_eq!(
2428 linked_chunk,
2429 ['a', 'b', 'c'] ['r', 's', 'd'] ['e', 'f'] ['g', 'h']
2430 );
2431 assert_eq!(linked_chunk.num_items(), 10);
2432 assert_eq!(
2433 linked_chunk.updates().unwrap().take(),
2434 &[
2435 DetachLastItems { at: Position(ChunkIdentifier(1), 0) },
2436 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['r', 's'] },
2437 StartReattachItems,
2438 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['d'] },
2439 NewItemsChunk {
2440 previous: Some(ChunkIdentifier(1)),
2441 new: ChunkIdentifier(3),
2442 next: Some(ChunkIdentifier(2)),
2443 },
2444 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['e', 'f'] },
2445 EndReattachItems,
2446 ]
2447 );
2448
2449 Ok(())
2450 }
2451
2452 #[test]
2453 fn test_insert_items_at_end_of_chunk() -> Result<(), Error> {
2454 use super::Update::*;
2455
2456 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2457
2458 let _ = linked_chunk.updates().unwrap().take();
2460
2461 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
2462 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e']);
2463 assert_eq!(
2464 linked_chunk.updates().unwrap().take(),
2465 &[
2466 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2467 NewItemsChunk {
2468 previous: Some(ChunkIdentifier(0)),
2469 new: ChunkIdentifier(1),
2470 next: None,
2471 },
2472 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e'] },
2473 ]
2474 );
2475
2476 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2478 let position_after_e =
2479 Position(position_of_e.chunk_identifier(), position_of_e.index() + 1);
2480
2481 linked_chunk.insert_items_at(['p', 'q'], position_after_e)?;
2482 assert_items_eq!(
2483 linked_chunk,
2484 ['a', 'b', 'c'] ['d', 'e', 'p'] ['q']
2485 );
2486 assert_eq!(
2487 linked_chunk.updates().unwrap().take(),
2488 &[
2489 PushItems { at: Position(ChunkIdentifier(1), 2), items: vec!['p'] },
2490 NewItemsChunk {
2491 previous: Some(ChunkIdentifier(1)),
2492 new: ChunkIdentifier(2),
2493 next: None
2494 },
2495 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['q'] }
2496 ]
2497 );
2498 assert_eq!(linked_chunk.num_items(), 7);
2499
2500 Ok(())
2501 }
2502
2503 #[test]
2504 fn test_insert_items_at_errs() -> Result<(), Error> {
2505 use super::Update::*;
2506
2507 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2508
2509 let _ = linked_chunk.updates().unwrap().take();
2511
2512 linked_chunk.push_items_back(['a', 'b', 'c']);
2513 linked_chunk.push_gap_back(());
2514 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
2515 assert_eq!(
2516 linked_chunk.updates().unwrap().take(),
2517 &[
2518 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2519 NewGapChunk {
2520 previous: Some(ChunkIdentifier(0)),
2521 new: ChunkIdentifier(1),
2522 next: None,
2523 gap: (),
2524 },
2525 ]
2526 );
2527
2528 {
2530 assert_matches!(
2531 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)),
2532 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2533 );
2534 assert!(linked_chunk.updates().unwrap().take().is_empty());
2535 }
2536
2537 {
2539 assert_matches!(
2540 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)),
2541 Err(Error::InvalidItemIndex { index: 128 })
2542 );
2543 assert!(linked_chunk.updates().unwrap().take().is_empty());
2544 }
2545
2546 {
2548 assert_matches!(
2549 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(1), 0)),
2550 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(1) })
2551 );
2552 }
2553
2554 Ok(())
2555 }
2556
2557 #[test]
2558 fn test_remove_item_at() -> Result<(), Error> {
2559 use super::Update::*;
2560
2561 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2562
2563 let _ = linked_chunk.updates().unwrap().take();
2565
2566 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']);
2567 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h', 'i'] ['j', 'k']);
2568 assert_eq!(linked_chunk.num_items(), 11);
2569
2570 let _ = linked_chunk.updates().unwrap().take();
2572
2573 {
2576 let position_of_f = linked_chunk.item_position(|item| *item == 'f').unwrap();
2577 let removed_item = linked_chunk.remove_item_at(position_of_f)?;
2578
2579 assert_eq!(removed_item, 'f');
2580 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] ['g', 'h', 'i'] ['j', 'k']);
2581 assert_eq!(linked_chunk.num_items(), 10);
2582
2583 let position_of_e = linked_chunk.item_position(|item| *item == 'e').unwrap();
2584 let removed_item = linked_chunk.remove_item_at(position_of_e)?;
2585
2586 assert_eq!(removed_item, 'e');
2587 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d'] ['g', 'h', 'i'] ['j', 'k']);
2588 assert_eq!(linked_chunk.num_items(), 9);
2589
2590 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2591 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2592
2593 assert_eq!(removed_item, 'd');
2594 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2595 assert_eq!(linked_chunk.num_items(), 8);
2596
2597 assert_eq!(
2598 linked_chunk.updates().unwrap().take(),
2599 &[
2600 RemoveItem { at: Position(ChunkIdentifier(1), 2) },
2601 RemoveItem { at: Position(ChunkIdentifier(1), 1) },
2602 RemoveItem { at: Position(ChunkIdentifier(1), 0) },
2603 RemoveChunk(ChunkIdentifier(1)),
2604 ]
2605 );
2606 }
2607
2608 {
2611 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2612 let removed_item = linked_chunk.remove_item_at(first_position)?;
2613
2614 assert_eq!(removed_item, 'a');
2615 assert_items_eq!(linked_chunk, ['b', 'c'] ['g', 'h', 'i'] ['j', 'k']);
2616 assert_eq!(linked_chunk.num_items(), 7);
2617
2618 let removed_item = linked_chunk.remove_item_at(first_position)?;
2619
2620 assert_eq!(removed_item, 'b');
2621 assert_items_eq!(linked_chunk, ['c'] ['g', 'h', 'i'] ['j', 'k']);
2622 assert_eq!(linked_chunk.num_items(), 6);
2623
2624 let removed_item = linked_chunk.remove_item_at(first_position)?;
2625
2626 assert_eq!(removed_item, 'c');
2627 assert_items_eq!(linked_chunk, [] ['g', 'h', 'i'] ['j', 'k']);
2628 assert_eq!(linked_chunk.num_items(), 5);
2629
2630 assert_eq!(
2631 linked_chunk.updates().unwrap().take(),
2632 &[
2633 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2634 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2635 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2636 ]
2637 );
2638 }
2639
2640 {
2643 let first_position = linked_chunk.item_position(|item| *item == 'g').unwrap();
2644 let removed_item = linked_chunk.remove_item_at(first_position)?;
2645
2646 assert_eq!(removed_item, 'g');
2647 assert_items_eq!(linked_chunk, [] ['h', 'i'] ['j', 'k']);
2648 assert_eq!(linked_chunk.num_items(), 4);
2649
2650 let removed_item = linked_chunk.remove_item_at(first_position)?;
2651
2652 assert_eq!(removed_item, 'h');
2653 assert_items_eq!(linked_chunk, [] ['i'] ['j', 'k']);
2654 assert_eq!(linked_chunk.num_items(), 3);
2655
2656 let removed_item = linked_chunk.remove_item_at(first_position)?;
2657
2658 assert_eq!(removed_item, 'i');
2659 assert_items_eq!(linked_chunk, [] ['j', 'k']);
2660 assert_eq!(linked_chunk.num_items(), 2);
2661
2662 assert_eq!(
2663 linked_chunk.updates().unwrap().take(),
2664 &[
2665 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2666 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2667 RemoveItem { at: Position(ChunkIdentifier(2), 0) },
2668 RemoveChunk(ChunkIdentifier(2)),
2669 ]
2670 );
2671 }
2672
2673 {
2676 let position_of_k = linked_chunk.item_position(|item| *item == 'k').unwrap();
2677 let removed_item = linked_chunk.remove_item_at(position_of_k)?;
2678
2679 assert_eq!(removed_item, 'k');
2680 #[rustfmt::skip]
2681 assert_items_eq!(linked_chunk, [] ['j']);
2682 assert_eq!(linked_chunk.num_items(), 1);
2683
2684 let position_of_j = linked_chunk.item_position(|item| *item == 'j').unwrap();
2685 let removed_item = linked_chunk.remove_item_at(position_of_j)?;
2686
2687 assert_eq!(removed_item, 'j');
2688 assert_items_eq!(linked_chunk, []);
2689 assert_eq!(linked_chunk.num_items(), 0);
2690
2691 assert_eq!(
2692 linked_chunk.updates().unwrap().take(),
2693 &[
2694 RemoveItem { at: Position(ChunkIdentifier(3), 1) },
2695 RemoveItem { at: Position(ChunkIdentifier(3), 0) },
2696 RemoveChunk(ChunkIdentifier(3)),
2697 ]
2698 );
2699 }
2700
2701 {
2703 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
2704
2705 #[rustfmt::skip]
2706 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d']);
2707 assert_eq!(linked_chunk.num_items(), 4);
2708
2709 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2710 linked_chunk.insert_gap_at((), position_of_c)?;
2711
2712 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['c'] ['d']);
2713 assert_eq!(linked_chunk.num_items(), 4);
2714
2715 let _ = linked_chunk.updates().unwrap().take();
2717
2718 let position_of_c = linked_chunk.item_position(|item| *item == 'c').unwrap();
2719 let removed_item = linked_chunk.remove_item_at(position_of_c)?;
2720
2721 assert_eq!(removed_item, 'c');
2722 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['d']);
2723 assert_eq!(linked_chunk.num_items(), 3);
2724
2725 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2726 let removed_item = linked_chunk.remove_item_at(position_of_d)?;
2727
2728 assert_eq!(removed_item, 'd');
2729 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
2730 assert_eq!(linked_chunk.num_items(), 2);
2731
2732 let first_position = linked_chunk.item_position(|item| *item == 'a').unwrap();
2733 let removed_item = linked_chunk.remove_item_at(first_position)?;
2734
2735 assert_eq!(removed_item, 'a');
2736 assert_items_eq!(linked_chunk, ['b'] [-]);
2737 assert_eq!(linked_chunk.num_items(), 1);
2738
2739 let removed_item = linked_chunk.remove_item_at(first_position)?;
2740
2741 assert_eq!(removed_item, 'b');
2742 assert_items_eq!(linked_chunk, [] [-]);
2743 assert_eq!(linked_chunk.num_items(), 0);
2744
2745 assert_eq!(
2746 linked_chunk.updates().unwrap().take(),
2747 &[
2748 RemoveItem { at: Position(ChunkIdentifier(6), 0) },
2749 RemoveChunk(ChunkIdentifier(6)),
2750 RemoveItem { at: Position(ChunkIdentifier(4), 0) },
2751 RemoveChunk(ChunkIdentifier(4)),
2752 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2753 RemoveItem { at: Position(ChunkIdentifier(0), 0) },
2754 ]
2755 );
2756 }
2757
2758 Ok(())
2759 }
2760
2761 #[test]
2762 fn test_insert_gap_at() -> Result<(), Error> {
2763 use super::Update::*;
2764
2765 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2766
2767 let _ = linked_chunk.updates().unwrap().take();
2769
2770 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f']);
2771 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e', 'f']);
2772 assert_eq!(
2773 linked_chunk.updates().unwrap().take(),
2774 &[
2775 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b', 'c'] },
2776 NewItemsChunk {
2777 previous: Some(ChunkIdentifier(0)),
2778 new: ChunkIdentifier(1),
2779 next: None
2780 },
2781 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['d', 'e', 'f'] },
2782 ]
2783 );
2784
2785 {
2787 let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap();
2788 linked_chunk.insert_gap_at((), position_of_b)?;
2789
2790 assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2791 assert_eq!(
2792 linked_chunk.updates().unwrap().take(),
2793 &[
2794 DetachLastItems { at: Position(ChunkIdentifier(0), 1) },
2795 NewGapChunk {
2796 previous: Some(ChunkIdentifier(0)),
2797 new: ChunkIdentifier(2),
2798 next: Some(ChunkIdentifier(1)),
2799 gap: (),
2800 },
2801 StartReattachItems,
2802 NewItemsChunk {
2803 previous: Some(ChunkIdentifier(2)),
2804 new: ChunkIdentifier(3),
2805 next: Some(ChunkIdentifier(1)),
2806 },
2807 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['b', 'c'] },
2808 EndReattachItems,
2809 ]
2810 );
2811 }
2812
2813 {
2816 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
2817 linked_chunk.insert_gap_at((), position_of_a)?;
2818
2819 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']);
2822 assert_eq!(
2823 linked_chunk.updates().unwrap().take(),
2824 &[NewGapChunk {
2825 previous: None,
2826 new: ChunkIdentifier(4),
2827 next: Some(ChunkIdentifier(0)),
2828 gap: (),
2829 },]
2830 );
2831 }
2832
2833 {
2836 let position_of_d = linked_chunk.item_position(|item| *item == 'd').unwrap();
2837 linked_chunk.insert_gap_at((), position_of_d)?;
2838
2839 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] ['d', 'e', 'f']);
2843 assert_eq!(
2844 linked_chunk.updates().unwrap().take(),
2845 &[NewGapChunk {
2846 previous: Some(ChunkIdentifier(3)),
2847 new: ChunkIdentifier(5),
2848 next: Some(ChunkIdentifier(1)),
2849 gap: (),
2850 }]
2851 );
2852 }
2853
2854 {
2856 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
2858 let position = linked_chunk.replace_gap_at([], gap_identifier)?.first_position();
2859
2860 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [] ['d', 'e', 'f']);
2861
2862 assert_eq!(
2863 linked_chunk.updates().unwrap().take(),
2864 &[
2865 NewItemsChunk {
2866 previous: Some(ChunkIdentifier(5)),
2867 new: ChunkIdentifier(6),
2868 next: Some(ChunkIdentifier(1)),
2869 },
2870 RemoveChunk(ChunkIdentifier(5)),
2871 ]
2872 );
2873
2874 linked_chunk.insert_gap_at((), position)?;
2875
2876 assert_items_eq!(linked_chunk, [-] ['a'] [-] ['b', 'c'] [-] [] ['d', 'e', 'f']);
2877 assert_eq!(
2878 linked_chunk.updates().unwrap().take(),
2879 &[NewGapChunk {
2880 previous: Some(ChunkIdentifier(3)),
2881 new: ChunkIdentifier(7),
2882 next: Some(ChunkIdentifier(6)),
2883 gap: (),
2884 }]
2885 );
2886 }
2887
2888 {
2890 assert_matches!(
2891 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)),
2892 Err(Error::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) })
2893 );
2894 assert!(linked_chunk.updates().unwrap().take().is_empty());
2895 }
2896
2897 {
2899 assert_matches!(
2900 linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)),
2901 Err(Error::InvalidItemIndex { index: 128 })
2902 );
2903 assert!(linked_chunk.updates().unwrap().take().is_empty());
2904 }
2905
2906 {
2908 let position_of_a_gap = Position(ChunkIdentifier(2), 0);
2911 assert_matches!(
2912 linked_chunk.insert_gap_at((), position_of_a_gap),
2913 Err(Error::ChunkIsAGap { identifier: ChunkIdentifier(2) })
2914 );
2915 assert!(linked_chunk.updates().unwrap().take().is_empty());
2916 }
2917
2918 assert_eq!(linked_chunk.num_items(), 6);
2919
2920 Ok(())
2921 }
2922
2923 #[test]
2924 fn test_replace_gap_at_middle() -> Result<(), Error> {
2925 use super::Update::*;
2926
2927 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2928
2929 let _ = linked_chunk.updates().unwrap().take();
2931
2932 linked_chunk.push_items_back(['a', 'b']);
2933 linked_chunk.push_gap_back(());
2934 linked_chunk.push_items_back(['l', 'm']);
2935 assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']);
2936 assert_eq!(
2937 linked_chunk.updates().unwrap().take(),
2938 &[
2939 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
2940 NewGapChunk {
2941 previous: Some(ChunkIdentifier(0)),
2942 new: ChunkIdentifier(1),
2943 next: None,
2944 gap: (),
2945 },
2946 NewItemsChunk {
2947 previous: Some(ChunkIdentifier(1)),
2948 new: ChunkIdentifier(2),
2949 next: None,
2950 },
2951 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['l', 'm'] }
2952 ]
2953 );
2954
2955 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
2957 assert_eq!(gap_identifier, ChunkIdentifier(1));
2958
2959 let new_chunk = linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?;
2960 assert_eq!(new_chunk.identifier(), ChunkIdentifier(3));
2961 assert_items_eq!(
2962 linked_chunk,
2963 ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm']
2964 );
2965 assert_eq!(
2966 linked_chunk.updates().unwrap().take(),
2967 &[
2968 NewItemsChunk {
2969 previous: Some(ChunkIdentifier(1)),
2970 new: ChunkIdentifier(3),
2971 next: Some(ChunkIdentifier(2)),
2972 },
2973 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['d', 'e', 'f'] },
2974 NewItemsChunk {
2975 previous: Some(ChunkIdentifier(3)),
2976 new: ChunkIdentifier(4),
2977 next: Some(ChunkIdentifier(2)),
2978 },
2979 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['g', 'h'] },
2980 RemoveChunk(ChunkIdentifier(1)),
2981 ]
2982 );
2983
2984 assert_eq!(linked_chunk.num_items(), 9);
2985
2986 Ok(())
2987 }
2988
2989 #[test]
2990 fn test_replace_gap_at_end() -> Result<(), Error> {
2991 use super::Update::*;
2992
2993 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
2994
2995 let _ = linked_chunk.updates().unwrap().take();
2997
2998 linked_chunk.push_items_back(['a', 'b']);
2999 linked_chunk.push_gap_back(());
3000 assert_items_eq!(linked_chunk, ['a', 'b'] [-]);
3001 assert_eq!(
3002 linked_chunk.updates().unwrap().take(),
3003 &[
3004 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3005 NewGapChunk {
3006 previous: Some(ChunkIdentifier(0)),
3007 new: ChunkIdentifier(1),
3008 next: None,
3009 gap: (),
3010 },
3011 ]
3012 );
3013
3014 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3016 assert_eq!(gap_identifier, ChunkIdentifier(1));
3017
3018 let new_chunk = linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?;
3019 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3020 assert_items_eq!(
3021 linked_chunk,
3022 ['a', 'b'] ['w', 'x', 'y'] ['z']
3023 );
3024 assert_eq!(
3025 linked_chunk.updates().unwrap().take(),
3026 &[
3027 NewItemsChunk {
3028 previous: Some(ChunkIdentifier(1)),
3029 new: ChunkIdentifier(2),
3030 next: None,
3031 },
3032 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['w', 'x', 'y'] },
3033 NewItemsChunk {
3034 previous: Some(ChunkIdentifier(2)),
3035 new: ChunkIdentifier(3),
3036 next: None,
3037 },
3038 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['z'] },
3039 RemoveChunk(ChunkIdentifier(1)),
3040 ]
3041 );
3042
3043 assert_eq!(linked_chunk.num_items(), 6);
3044
3045 Ok(())
3046 }
3047
3048 #[test]
3049 fn test_replace_gap_at_beginning() -> Result<(), Error> {
3050 use super::Update::*;
3051
3052 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3053
3054 let _ = linked_chunk.updates().unwrap().take();
3056
3057 linked_chunk.push_items_back(['a', 'b']);
3058 assert_items_eq!(linked_chunk, ['a', 'b']);
3059 assert_eq!(
3060 linked_chunk.updates().unwrap().take(),
3061 &[PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },]
3062 );
3063
3064 let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap();
3066 linked_chunk.insert_gap_at((), position_of_a).unwrap();
3067 assert_items_eq!(
3068 linked_chunk,
3069 [-] ['a', 'b']
3070 );
3071 assert_eq!(
3072 linked_chunk.updates().unwrap().take(),
3073 &[NewGapChunk {
3074 previous: None,
3075 new: ChunkIdentifier(1),
3076 next: Some(ChunkIdentifier(0)),
3077 gap: (),
3078 }]
3079 );
3080
3081 let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap();
3082 assert_eq!(gap_identifier, ChunkIdentifier(1));
3083
3084 let new_chunk = linked_chunk.replace_gap_at(['x'], gap_identifier)?;
3085 assert_eq!(new_chunk.identifier(), ChunkIdentifier(2));
3086 assert_items_eq!(
3087 linked_chunk,
3088 ['x'] ['a', 'b']
3089 );
3090 assert_eq!(
3091 linked_chunk.updates().unwrap().take(),
3092 &[
3093 NewItemsChunk {
3094 previous: Some(ChunkIdentifier(1)),
3095 new: ChunkIdentifier(2),
3096 next: Some(ChunkIdentifier(0)),
3097 },
3098 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['x'] },
3099 RemoveChunk(ChunkIdentifier(1)),
3100 ]
3101 );
3102
3103 assert_eq!(linked_chunk.num_items(), 3);
3104
3105 Ok(())
3106 }
3107
3108 #[test]
3109 fn test_remove_empty_chunk_at() -> Result<(), Error> {
3110 use super::Update::*;
3111
3112 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3113
3114 let _ = linked_chunk.updates().unwrap().take();
3116
3117 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(0), 0)).unwrap();
3118 linked_chunk.push_items_back(['a', 'b']);
3119 linked_chunk.push_gap_back(());
3120 linked_chunk.push_items_back(['l', 'm']);
3121 linked_chunk.push_gap_back(());
3122 assert_items_eq!(linked_chunk, [-] ['a', 'b'] [-] ['l', 'm'] [-]);
3123 assert_eq!(
3124 linked_chunk.updates().unwrap().take(),
3125 &[
3126 NewGapChunk {
3127 previous: None,
3128 new: ChunkIdentifier(1),
3129 next: Some(ChunkIdentifier(0)),
3130 gap: (),
3131 },
3132 PushItems { at: Position(ChunkIdentifier(0), 0), items: vec!['a', 'b'] },
3133 NewGapChunk {
3134 previous: Some(ChunkIdentifier(0)),
3135 new: ChunkIdentifier(2),
3136 next: None,
3137 gap: (),
3138 },
3139 NewItemsChunk {
3140 previous: Some(ChunkIdentifier(2)),
3141 new: ChunkIdentifier(3),
3142 next: None,
3143 },
3144 PushItems { at: Position(ChunkIdentifier(3), 0), items: vec!['l', 'm'] },
3145 NewGapChunk {
3146 previous: Some(ChunkIdentifier(3)),
3147 new: ChunkIdentifier(4),
3148 next: None,
3149 gap: (),
3150 },
3151 ]
3152 );
3153
3154 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3156 assert_matches!(err, Error::RemovingNonEmptyItemsChunk { .. });
3157
3158 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(42)).unwrap_err();
3160 assert_matches!(err, Error::InvalidChunkIdentifier { .. });
3161
3162 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(2)).unwrap();
3164 let next = maybe_next.unwrap();
3165 assert_eq!(next.chunk_identifier(), ChunkIdentifier(3));
3167 assert_eq!(next.index(), 0);
3168 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm'] [-]);
3169 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(2))]);
3170
3171 let next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(4)).unwrap();
3173 assert!(next.is_none());
3175 assert_items_eq!(linked_chunk, [-] ['a', 'b'] ['l', 'm']);
3176 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(4))]);
3177
3178 let maybe_next = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(1)).unwrap();
3180 let next = maybe_next.unwrap();
3181 assert_eq!(next.chunk_identifier(), ChunkIdentifier(0));
3182 assert_eq!(next.index(), 0);
3183 assert_items_eq!(linked_chunk, ['a', 'b'] ['l', 'm']);
3184 assert_eq!(linked_chunk.updates().unwrap().take(), &[RemoveChunk(ChunkIdentifier(1))]);
3185
3186 Ok(())
3187 }
3188
3189 #[test]
3190 fn test_remove_empty_last_chunk() {
3191 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3192
3193 let _ = linked_chunk.updates().unwrap().take();
3195
3196 assert_items_eq!(linked_chunk, []);
3197 assert!(linked_chunk.updates().unwrap().take().is_empty());
3198
3199 let err = linked_chunk.remove_empty_chunk_at(ChunkIdentifier(0)).unwrap_err();
3201 assert_matches!(err, Error::RemovingLastChunk);
3202 }
3203
3204 #[test]
3205 fn test_chunk_item_positions() {
3206 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3207 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']);
3208 linked_chunk.push_gap_back(());
3209 linked_chunk.push_items_back(['f']);
3210
3211 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']);
3212
3213 let mut iterator = linked_chunk.chunks();
3214
3215 {
3217 let chunk = iterator.next().unwrap();
3218 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0));
3219 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2));
3220 }
3221
3222 {
3224 let chunk = iterator.next().unwrap();
3225 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0));
3226 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1));
3227 }
3228
3229 {
3231 let chunk = iterator.next().unwrap();
3232 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0));
3233 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0));
3234 }
3235
3236 {
3238 let chunk = iterator.next().unwrap();
3239 assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0));
3240 assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0));
3241 }
3242 }
3243
3244 #[test]
3245 fn test_is_first_and_last_chunk() {
3246 let mut linked_chunk = LinkedChunk::<3, char, ()>::new();
3247
3248 let mut chunks = linked_chunk.chunks().peekable();
3249 assert!(chunks.peek().unwrap().is_first_chunk());
3250 assert!(chunks.next().unwrap().is_last_chunk());
3251 assert!(chunks.next().is_none());
3252
3253 linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
3254
3255 let mut chunks = linked_chunk.chunks().peekable();
3256 assert!(chunks.next().unwrap().is_first_chunk());
3257 assert!(chunks.peek().unwrap().is_first_chunk().not());
3258 assert!(chunks.next().unwrap().is_last_chunk().not());
3259 assert!(chunks.next().unwrap().is_last_chunk());
3260 assert!(chunks.next().is_none());
3261 }
3262
3263 #[test]
3268 fn test_clear() {
3269 let mut linked_chunk = LinkedChunk::<3, Arc<char>, Arc<()>>::new();
3270
3271 let item = Arc::new('a');
3272 let gap = Arc::new(());
3273
3274 linked_chunk.push_items_back([
3275 item.clone(),
3276 item.clone(),
3277 item.clone(),
3278 item.clone(),
3279 item.clone(),
3280 ]);
3281 linked_chunk.push_gap_back(gap.clone());
3282 linked_chunk.push_items_back([item.clone()]);
3283
3284 assert_eq!(Arc::strong_count(&item), 7);
3285 assert_eq!(Arc::strong_count(&gap), 2);
3286 assert_eq!(linked_chunk.num_items(), 6);
3287 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 3);
3288
3289 linked_chunk.clear();
3291
3292 assert_eq!(Arc::strong_count(&item), 1);
3293 assert_eq!(Arc::strong_count(&gap), 1);
3294 assert_eq!(linked_chunk.num_items(), 0);
3295 assert_eq!(linked_chunk.chunk_identifier_generator.next.load(Ordering::SeqCst), 0);
3296 }
3297
3298 #[test]
3299 fn test_clear_emit_an_update_clear() {
3300 use super::Update::*;
3301
3302 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3303
3304 assert_eq!(
3305 linked_chunk.updates().unwrap().take(),
3306 &[NewItemsChunk {
3307 previous: None,
3308 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3309 next: None
3310 }]
3311 );
3312
3313 linked_chunk.clear();
3314
3315 assert_eq!(
3316 linked_chunk.updates().unwrap().take(),
3317 &[
3318 Clear,
3319 NewItemsChunk {
3320 previous: None,
3321 new: ChunkIdentifierGenerator::FIRST_IDENTIFIER,
3322 next: None
3323 }
3324 ]
3325 );
3326 }
3327
3328 #[test]
3329 fn test_replace_item() {
3330 use super::Update::*;
3331
3332 let mut linked_chunk = LinkedChunk::<3, char, ()>::new_with_update_history();
3333
3334 linked_chunk.push_items_back(['a', 'b', 'c']);
3335 linked_chunk.push_gap_back(());
3336 assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-]);
3338
3339 let _ = linked_chunk.updates().unwrap().take();
3341
3342 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 1), 'B').unwrap();
3344 assert_items_eq!(linked_chunk, ['a', 'B', 'c'] [-]);
3345
3346 assert_eq!(
3347 linked_chunk.updates().unwrap().take(),
3348 &[ReplaceItem { at: Position(ChunkIdentifier(0), 1), item: 'B' }]
3349 );
3350
3351 assert_matches!(
3353 linked_chunk.replace_item_at(Position(ChunkIdentifier(0), 3), 'Z'),
3354 Err(Error::InvalidItemIndex { index: 3 })
3355 );
3356
3357 assert_matches!(
3359 linked_chunk.replace_item_at(Position(ChunkIdentifier(1), 0), 'Z'),
3360 Err(Error::ChunkIsAGap { .. })
3361 );
3362 }
3363
3364 #[test]
3365 fn test_lazy_previous() {
3366 use std::marker::PhantomData;
3367
3368 use super::{Ends, ObservableUpdates, Update::*};
3369
3370 let first_chunk_identifier = ChunkIdentifier(0);
3372 let mut first_loaded_chunk = Chunk::new_items_leaked(ChunkIdentifier(1));
3373 unsafe { first_loaded_chunk.as_mut() }.lazy_previous = Some(first_chunk_identifier);
3374
3375 let mut linked_chunk = LinkedChunk::<3, char, ()> {
3376 links: Ends { first: first_loaded_chunk, last: None },
3377 chunk_identifier_generator:
3378 ChunkIdentifierGenerator::new_from_previous_chunk_identifier(ChunkIdentifier(1)),
3379 updates: Some(ObservableUpdates::new()),
3380 marker: PhantomData,
3381 };
3382
3383 {
3386 linked_chunk.push_items_back(['a', 'b', 'c', 'd']);
3387
3388 assert_items_eq!(linked_chunk, ['a', 'b', 'c']['d']);
3389
3390 {
3392 let mut chunks = linked_chunk.chunks();
3393
3394 assert_matches!(chunks.next(), Some(chunk) => {
3395 assert_eq!(chunk.identifier(), 1);
3396 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3397 });
3398 assert_matches!(chunks.next(), Some(chunk) => {
3399 assert_eq!(chunk.identifier(), 2);
3400 assert!(chunk.lazy_previous.is_none());
3401 });
3402 assert!(chunks.next().is_none());
3403 }
3404
3405 assert_eq!(
3407 linked_chunk.updates().unwrap().take(),
3408 &[
3409 PushItems { at: Position(ChunkIdentifier(1), 0), items: vec!['a', 'b', 'c'] },
3410 NewItemsChunk {
3411 previous: Some(ChunkIdentifier(1)),
3412 new: ChunkIdentifier(2),
3413 next: None,
3414 },
3415 PushItems { at: Position(ChunkIdentifier(2), 0), items: vec!['d'] }
3416 ]
3417 );
3418 }
3419
3420 {
3422 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(1), 0)).unwrap();
3423
3424 assert_items_eq!(linked_chunk, [-] ['a', 'b', 'c'] ['d']);
3425
3426 {
3428 let mut chunks = linked_chunk.chunks();
3429
3430 assert_matches!(chunks.next(), Some(chunk) => {
3431 assert_eq!(chunk.identifier(), 3);
3432 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3434 });
3435 assert_matches!(chunks.next(), Some(chunk) => {
3436 assert_eq!(chunk.identifier(), 1);
3437 assert!(chunk.lazy_previous.is_none());
3439 });
3440 assert_matches!(chunks.next(), Some(chunk) => {
3441 assert_eq!(chunk.identifier(), 2);
3442 assert!(chunk.lazy_previous.is_none());
3443 });
3444 assert!(chunks.next().is_none());
3445 }
3446
3447 assert_eq!(
3449 linked_chunk.updates().unwrap().take(),
3450 &[NewGapChunk {
3451 previous: Some(ChunkIdentifier(0)),
3453 new: ChunkIdentifier(3),
3454 next: Some(ChunkIdentifier(1)),
3455 gap: ()
3456 }]
3457 );
3458 }
3459
3460 {
3462 linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], ChunkIdentifier(3)).unwrap();
3463
3464 assert_items_eq!(linked_chunk, ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3465
3466 {
3468 let mut chunks = linked_chunk.chunks();
3469
3470 assert_matches!(chunks.next(), Some(chunk) => {
3471 assert_eq!(chunk.identifier(), 4);
3472 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3474 });
3475 assert_matches!(chunks.next(), Some(chunk) => {
3476 assert_eq!(chunk.identifier(), 5);
3477 assert!(chunk.lazy_previous.is_none());
3478 });
3479 assert_matches!(chunks.next(), Some(chunk) => {
3480 assert_eq!(chunk.identifier(), 1);
3481 assert!(chunk.lazy_previous.is_none());
3482 });
3483 assert_matches!(chunks.next(), Some(chunk) => {
3484 assert_eq!(chunk.identifier(), 2);
3485 assert!(chunk.lazy_previous.is_none());
3486 });
3487 assert!(chunks.next().is_none());
3488 }
3489
3490 assert_eq!(
3492 linked_chunk.updates().unwrap().take(),
3493 &[
3494 NewItemsChunk {
3496 previous: Some(ChunkIdentifier(3)),
3497 new: ChunkIdentifier(4),
3498 next: Some(ChunkIdentifier(1)),
3499 },
3500 PushItems { at: Position(ChunkIdentifier(4), 0), items: vec!['w', 'x', 'y'] },
3502 NewItemsChunk {
3504 previous: Some(ChunkIdentifier(4)),
3505 new: ChunkIdentifier(5),
3506 next: Some(ChunkIdentifier(1)),
3507 },
3508 PushItems { at: Position(ChunkIdentifier(5), 0), items: vec!['z'] },
3510 RemoveChunk(ChunkIdentifier(3)),
3512 ]
3513 );
3514 }
3515
3516 {
3520 linked_chunk.insert_gap_at((), Position(ChunkIdentifier(4), 0)).unwrap();
3521
3522 assert_items_eq!(linked_chunk, [-] ['w', 'x', 'y'] ['z'] ['a', 'b', 'c'] ['d']);
3523
3524 {
3526 let mut chunks = linked_chunk.chunks();
3527
3528 assert_matches!(chunks.next(), Some(chunk) => {
3529 assert_eq!(chunk.identifier(), 6);
3530 assert_eq!(chunk.lazy_previous, Some(ChunkIdentifier(0)));
3532 });
3533 assert_matches!(chunks.next(), Some(chunk) => {
3534 assert_eq!(chunk.identifier(), 4);
3535 assert!(chunk.lazy_previous.is_none());
3537 });
3538 assert_matches!(chunks.next(), Some(chunk) => {
3539 assert_eq!(chunk.identifier(), 5);
3540 assert!(chunk.lazy_previous.is_none());
3541 });
3542 assert_matches!(chunks.next(), Some(chunk) => {
3543 assert_eq!(chunk.identifier(), 1);
3544 assert!(chunk.lazy_previous.is_none());
3545 });
3546 assert_matches!(chunks.next(), Some(chunk) => {
3547 assert_eq!(chunk.identifier(), 2);
3548 assert!(chunk.lazy_previous.is_none());
3549 });
3550 assert!(chunks.next().is_none());
3551 }
3552
3553 assert_eq!(
3555 linked_chunk.updates().unwrap().take(),
3556 &[NewGapChunk {
3557 previous: Some(ChunkIdentifier(0)),
3559 new: ChunkIdentifier(6),
3560 next: Some(ChunkIdentifier(4)),
3561 gap: ()
3562 }]
3563 );
3564 }
3565 }
3566}