diff --git a/crates/matrix-sdk/src/event_cache/linked_chunk.rs b/crates/matrix-sdk/src/event_cache/linked_chunk.rs index c13a2a7d293..49702fc0b16 100644 --- a/crates/matrix-sdk/src/event_cache/linked_chunk.rs +++ b/crates/matrix-sdk/src/event_cache/linked_chunk.rs @@ -98,7 +98,7 @@ impl LinkedChunk { if last_chunk.is_first_chunk().not() { // Maybe `last_chunk` is the same as the previous `self.last` chunk, but it's // OK. - self.last = Some(NonNull::from(last_chunk)); + self.last = Some(last_chunk.as_ptr()); } self.length += number_of_items; @@ -122,14 +122,14 @@ impl LinkedChunk { pub fn insert_items_at( &mut self, items: I, - position: ItemPosition, + position: Position, ) -> Result<(), LinkedChunkError> where I: IntoIterator, I::IntoIter: ExactSizeIterator, { let chunk_identifier = position.chunk_identifier(); - let item_index = position.item_index(); + let item_index = position.index(); let chunk_identifier_generator = self.chunk_identifier_generator.clone(); @@ -148,11 +148,6 @@ impl LinkedChunk { return Err(LinkedChunkError::InvalidItemIndex { index: item_index }); } - // The `ItemPosition` is computed from the latest items. Here, we manipulate the - // items in their original order: the last item comes last. Let's adjust - // `item_index`. - let item_index = current_items_length - 1 - item_index; - // Split the items. let detached_items = current_items.split_off(item_index); @@ -176,7 +171,7 @@ impl LinkedChunk { if chunk.is_first_chunk().not() && chunk.is_last_chunk() { // Maybe `chunk` is the same as the previous `self.last` chunk, but it's // OK. - self.last = Some(NonNull::from(chunk)); + self.last = Some(chunk.as_ptr()); } self.length += number_of_items; @@ -190,11 +185,11 @@ impl LinkedChunk { /// `Result`. pub fn insert_gap_at( &mut self, - position: ItemPosition, content: Gap, + position: Position, ) -> Result<(), LinkedChunkError> { let chunk_identifier = position.chunk_identifier(); - let item_index = position.item_index(); + let item_index = position.index(); let chunk_identifier_generator = self.chunk_identifier_generator.clone(); @@ -213,11 +208,6 @@ impl LinkedChunk { return Err(LinkedChunkError::InvalidItemIndex { index: item_index }); } - // The `ItemPosition` is computed from the latest items. Here, we manipulate the - // items in their original order: the last item comes last. Let's adjust - // `item_index`. - let item_index = current_items_length - 1 - item_index; - // Split the items. let detached_items = current_items.split_off(item_index); @@ -241,7 +231,7 @@ impl LinkedChunk { if chunk.is_first_chunk().not() && chunk.is_last_chunk() { // Maybe `chunk` is the same as the previous `self.last` chunk, but it's // OK. - self.last = Some(NonNull::from(chunk)); + self.last = Some(chunk.as_ptr()); } Ok(()) @@ -270,40 +260,38 @@ impl LinkedChunk { debug_assert!(chunk.is_first_chunk().not(), "A gap cannot be the first chunk"); - let (previous, number_of_items) = match &mut chunk.content { + let (maybe_last_chunk_ptr, number_of_items) = match &mut chunk.content { ChunkContent::Gap(..) => { let items = items.into_iter(); let number_of_items = items.len(); - // Find the previous chunk… - // - // SAFETY: `unwrap` is safe because we are ensured `chunk` is not the first - // chunk, so a previous chunk always exists. - let previous = chunk.previous_mut().unwrap(); + let last_inserted_chunk = chunk + // Insert a new items chunk… + .insert_next(Chunk::new_items_leaked( + chunk_identifier_generator.generate_next().unwrap(), + )) + // … and insert the items. + .push_items(items, &chunk_identifier_generator); - // … and insert the items on it. - (previous.push_items(items, &chunk_identifier_generator), number_of_items) + ( + last_inserted_chunk.is_last_chunk().then(|| last_inserted_chunk.as_ptr()), + number_of_items, + ) } ChunkContent::Items(..) => { return Err(LinkedChunkError::ChunkIsItems { identifier: chunk_identifier }) } }; - // Get the pointer to `chunk` via `previous`. - // - // SAFETY: `unwrap` is safe because we are ensured the next of the previous - // chunk is `chunk` itself. - chunk_ptr = previous.next.unwrap(); - - // Get the pointer to the `previous` via `chunk`. - let previous_ptr = chunk.previous; - // Now that new items have been pushed, we can unlink the gap chunk. chunk.unlink(); + // Get the pointer to `chunk`. + chunk_ptr = chunk.as_ptr(); + // Update `self.last` if the gap chunk was the last chunk. - if chunk.is_last_chunk() { - self.last = previous_ptr; + if let Some(last_chunk_ptr) = maybe_last_chunk_ptr { + self.last = Some(last_chunk_ptr); } self.length += number_of_items; @@ -351,11 +339,11 @@ impl LinkedChunk { where P: FnMut(&'a Chunk) -> bool, { - self.rchunks().find_map(|chunk| predicate(chunk).then_some(chunk.identifier())) + self.rchunks().find_map(|chunk| predicate(chunk).then(|| chunk.identifier())) } /// Search for an item, and return its position. - pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option + pub fn item_position<'a, P>(&'a self, mut predicate: P) -> Option where P: FnMut(&'a Item) -> bool, { @@ -366,8 +354,14 @@ impl LinkedChunk { /// /// It iterates from the last to the first chunk. pub fn rchunks(&self) -> LinkedChunkIterBackward<'_, Item, Gap, CAP> { - self.rchunks_from(self.latest_chunk().identifier()) - .expect("`iter_chunks_from` cannot fail because at least one empty chunk must exist") + LinkedChunkIterBackward::new(self.latest_chunk()) + } + + /// Iterate over the chunks, forward. + /// + /// It iterates from the first to the last chunk. + pub fn chunks(&self) -> LinkedChunkIter<'_, Item, Gap, CAP> { + LinkedChunkIter::new(self.first_chunk()) } /// Iterate over the chunks, starting from `identifier`, backward. @@ -401,9 +395,19 @@ impl LinkedChunk { /// Iterate over the items, backward. /// /// It iterates from the last to the first item. - pub fn ritems(&self) -> impl Iterator { - self.ritems_from(ItemPosition(self.latest_chunk().identifier(), 0)) - .expect("`iter_items_from` cannot fail because at least one empty chunk must exist") + pub fn ritems(&self) -> impl Iterator { + self.ritems_from(self.latest_chunk().last_position()) + .expect("`ritems_from` cannot fail because at least one empty chunk must exist") + } + + /// Iterate over the items, forward. + /// + /// It iterates from the first to the last item. + pub fn items(&self) -> impl Iterator { + let first_chunk = self.first_chunk(); + + self.items_from(first_chunk.first_position()) + .expect("`items` cannot fail because at least one empty chunk must exist") } /// Iterate over the items, starting from `position`, backward. @@ -411,20 +415,30 @@ impl LinkedChunk { /// It iterates from the item at `position` to the first item. pub fn ritems_from( &self, - position: ItemPosition, - ) -> Result, LinkedChunkError> { + position: Position, + ) -> Result, LinkedChunkError> { Ok(self .rchunks_from(position.chunk_identifier())? .filter_map(|chunk| match &chunk.content { ChunkContent::Gap(..) => None, ChunkContent::Items(items) => { - Some(items.iter().rev().enumerate().map(move |(item_index, item)| { - (ItemPosition(chunk.identifier(), item_index), item) - })) + let identifier = chunk.identifier(); + + Some( + items.iter().enumerate().rev().map(move |(item_index, item)| { + (Position(identifier, item_index), item) + }), + ) } }) .flatten() - .skip(position.item_index())) + .skip_while({ + let expected_index = position.index(); + + move |(Position(_chunk_identifier, item_index), _item)| { + *item_index != expected_index + } + })) } /// Iterate over the items, starting from `position`, forward. @@ -432,19 +446,29 @@ impl LinkedChunk { /// It iterates from the item at `position` to the last item. pub fn items_from( &self, - position: ItemPosition, - ) -> Result, LinkedChunkError> { + position: Position, + ) -> Result, LinkedChunkError> { Ok(self .chunks_from(position.chunk_identifier())? .filter_map(|chunk| match &chunk.content { ChunkContent::Gap(..) => None, ChunkContent::Items(items) => { - Some(items.iter().rev().enumerate().rev().map(move |(item_index, item)| { - (ItemPosition(chunk.identifier(), item_index), item) - })) + let identifier = chunk.identifier(); + + Some( + items.iter().enumerate().map(move |(item_index, item)| { + (Position(identifier, item_index), item) + }), + ) } }) - .flatten()) + .flatten() + .skip(position.index())) + } + + /// Get the first chunk, as an immutable reference. + fn first_chunk(&self) -> &Chunk { + unsafe { self.first.as_ref() } } /// Get the latest chunk, as an immutable reference. @@ -553,21 +577,20 @@ impl ChunkIdentifierGenerator { #[repr(transparent)] pub struct ChunkIdentifier(u64); -/// The position of an item in a [`LinkedChunk`]. +/// The position of something inside a [`Chunk`]. /// -/// It's a pair of a chunk position and an item index. `(…, 0)` represents -/// the last item in the chunk. -#[derive(Debug, PartialEq)] -pub struct ItemPosition(ChunkIdentifier, usize); +/// It's a pair of a chunk position and an item index. +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Position(ChunkIdentifier, usize); -impl ItemPosition { +impl Position { /// Get the chunk identifier of the item. pub fn chunk_identifier(&self) -> ChunkIdentifier { self.0 } - /// Get the item index inside its chunk. - pub fn item_index(&self) -> usize { + /// Get the index inside the chunk. + pub fn index(&self) -> usize { self.1 } } @@ -679,13 +702,18 @@ impl Chunk { NonNull::from(Box::leak(chunk_box)) } + /// Get the pointer to `Self`. + pub fn as_ptr(&self) -> NonNull { + NonNull::from(self) + } + /// Check whether this current chunk is a gap chunk. - fn is_gap(&self) -> bool { + pub fn is_gap(&self) -> bool { matches!(self.content, ChunkContent::Gap(..)) } /// Check whether this current chunk is an items chunk. - fn is_items(&self) -> bool { + pub fn is_items(&self) -> bool { !self.is_gap() } @@ -704,6 +732,30 @@ impl Chunk { self.identifier } + /// Get the content of the chunk. + pub fn content(&self) -> &ChunkContent { + &self.content + } + + /// Get the [`Position`] of the first item if any. + /// + /// If the `Chunk` is a `Gap`, it returns `0` for the index. + pub fn first_position(&self) -> Position { + Position(self.identifier(), 0) + } + + /// Get the [`Position`] of the last item if any. + /// + /// If the `Chunk` is a `Gap`, it returns `0` for the index. + pub fn last_position(&self) -> Position { + let identifier = self.identifier(); + + match &self.content { + ChunkContent::Gap(..) => Position(identifier, 0), + ChunkContent::Items(items) => Position(identifier, items.len() - 1), + } + } + /// The length of the chunk, i.e. how many items are in it. /// /// It will always return 0 if it's a gap chunk. @@ -803,7 +855,7 @@ impl Chunk { // Link to the new chunk. self.next = Some(new_chunk_ptr); // Link the new chunk to this one. - new_chunk.previous = Some(NonNull::from(self)); + new_chunk.previous = Some(self.as_ptr()); new_chunk } @@ -884,8 +936,8 @@ mod tests { use assert_matches::assert_matches; use super::{ - Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, ItemPosition, LinkedChunk, - LinkedChunkError, + Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, LinkedChunk, + LinkedChunkError, Position, }; macro_rules! assert_items_eq { @@ -912,7 +964,7 @@ mod tests { $( assert_matches!( $iterator .next(), - Some((chunk_index, ItemPosition(chunk_identifier, item_index), & $item )) => { + Some((chunk_index, Position(chunk_identifier, item_index), & $item )) => { // Ensure the chunk index (from the enumeration) is correct. assert_eq!(chunk_index, $chunk_index); // Ensure the chunk identifier is the same for all items in this chunk. @@ -944,14 +996,15 @@ mod tests { { $( $all )* } { let mut iterator = $linked_chunk - .chunks_from(ChunkIdentifierGenerator::FIRST_IDENTIFIER) - .unwrap() + .chunks() .enumerate() .filter_map(|(chunk_index, chunk)| match &chunk.content { ChunkContent::Gap(..) => None, ChunkContent::Items(items) => { + let identifier = chunk.identifier(); + Some(items.iter().enumerate().map(move |(item_index, item)| { - (chunk_index, ItemPosition(chunk.identifier(), item_index), item) + (chunk_index, Position(identifier, item_index), item) })) } }) @@ -1042,7 +1095,7 @@ mod tests { assert_eq!(linked_chunk.chunk_identifier(Chunk::is_gap), Some(ChunkIdentifier(2))); assert_eq!( linked_chunk.item_position(|item| *item == 'e'), - Some(ItemPosition(ChunkIdentifier(1), 1)) + Some(Position(ChunkIdentifier(1), 1)) ); } @@ -1080,6 +1133,40 @@ mod tests { assert_matches!(iterator.next(), None); } + #[test] + fn test_chunks() { + let mut linked_chunk = LinkedChunk::::new(); + linked_chunk.push_items_back(['a', 'b']); + linked_chunk.push_gap_back(()); + linked_chunk.push_items_back(['c', 'd', 'e']); + + let mut iterator = linked_chunk.chunks(); + + assert_matches!( + iterator.next(), + Some(Chunk { identifier: ChunkIdentifier(0), content: ChunkContent::Items(items), .. }) => { + assert_eq!(items, &['a', 'b']); + } + ); + assert_matches!( + iterator.next(), + Some(Chunk { identifier: ChunkIdentifier(1), content: ChunkContent::Gap(..), .. }) + ); + assert_matches!( + iterator.next(), + Some(Chunk { identifier: ChunkIdentifier(2), content: ChunkContent::Items(items), .. }) => { + assert_eq!(items, &['c', 'd']); + } + ); + assert_matches!( + iterator.next(), + Some(Chunk { identifier: ChunkIdentifier(3), content: ChunkContent::Items(items), .. }) => { + assert_eq!(items, &['e']); + } + ); + assert_matches!(iterator.next(), None); + } + #[test] fn test_rchunks_from() -> Result<(), LinkedChunkError> { let mut linked_chunk = LinkedChunk::::new(); @@ -1149,11 +1236,28 @@ mod tests { let mut iterator = linked_chunk.ritems(); - assert_matches!(iterator.next(), Some((ItemPosition(ChunkIdentifier(3), 0), 'e'))); - assert_matches!(iterator.next(), Some((ItemPosition(ChunkIdentifier(2), 0), 'd'))); - assert_matches!(iterator.next(), Some((ItemPosition(ChunkIdentifier(2), 1), 'c'))); - assert_matches!(iterator.next(), Some((ItemPosition(ChunkIdentifier(0), 0), 'b'))); - assert_matches!(iterator.next(), Some((ItemPosition(ChunkIdentifier(0), 1), 'a'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a'))); + assert_matches!(iterator.next(), None); + } + + #[test] + fn test_items() { + let mut linked_chunk = LinkedChunk::::new(); + linked_chunk.push_items_back(['a', 'b']); + linked_chunk.push_gap_back(()); + linked_chunk.push_items_back(['c', 'd', 'e']); + + let mut iterator = linked_chunk.items(); + + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e'))); assert_matches!(iterator.next(), None); } @@ -1167,9 +1271,9 @@ mod tests { let mut iterator = linked_chunk.ritems_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?; - assert_matches!(iterator.next(), Some((ItemPosition(ChunkIdentifier(2), 1), 'c'))); - assert_matches!(iterator.next(), Some((ItemPosition(ChunkIdentifier(0), 0), 'b'))); - assert_matches!(iterator.next(), Some((ItemPosition(ChunkIdentifier(0), 1), 'a'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 1), 'b'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(0), 0), 'a'))); assert_matches!(iterator.next(), None); Ok(()) @@ -1185,9 +1289,9 @@ mod tests { let mut iterator = linked_chunk.items_from(linked_chunk.item_position(|item| *item == 'c').unwrap())?; - assert_matches!(iterator.next(), Some((ItemPosition(ChunkIdentifier(2), 1), 'c'))); - assert_matches!(iterator.next(), Some((ItemPosition(ChunkIdentifier(2), 0), 'd'))); - assert_matches!(iterator.next(), Some((ItemPosition(ChunkIdentifier(3), 0), 'e'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 0), 'c'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(2), 1), 'd'))); + assert_matches!(iterator.next(), Some((Position(ChunkIdentifier(3), 0), 'e'))); assert_matches!(iterator.next(), None); Ok(()) @@ -1241,7 +1345,7 @@ mod tests { // Insert in a chunk that does not exist. { assert_matches!( - linked_chunk.insert_items_at(['u', 'v'], ItemPosition(ChunkIdentifier(128), 0)), + linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)), Err(LinkedChunkError::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) }) ); } @@ -1249,7 +1353,7 @@ mod tests { // Insert in a chunk that exists, but at an item that does not exist. { assert_matches!( - linked_chunk.insert_items_at(['u', 'v'], ItemPosition(ChunkIdentifier(0), 128)), + linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)), Err(LinkedChunkError::InvalidItemIndex { index: 128 }) ); } @@ -1264,7 +1368,7 @@ mod tests { ); assert_matches!( - linked_chunk.insert_items_at(['u', 'v'], ItemPosition(ChunkIdentifier(6), 0),), + linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(6), 0)), Err(LinkedChunkError::ChunkIsAGap { identifier: ChunkIdentifier(6) }) ); } @@ -1283,7 +1387,7 @@ mod tests { // Insert in the middle of a chunk. { let position_of_b = linked_chunk.item_position(|item| *item == 'b').unwrap(); - linked_chunk.insert_gap_at(position_of_b, ())?; + linked_chunk.insert_gap_at((), position_of_b)?; assert_items_eq!(linked_chunk, ['a'] [-] ['b', 'c'] ['d', 'e', 'f']); } @@ -1291,7 +1395,7 @@ mod tests { // Insert at the beginning of a chunk. { let position_of_a = linked_chunk.item_position(|item| *item == 'a').unwrap(); - linked_chunk.insert_gap_at(position_of_a, ())?; + linked_chunk.insert_gap_at((), position_of_a)?; assert_items_eq!(linked_chunk, [] [-] ['a'] [-] ['b', 'c'] ['d', 'e', 'f']); } @@ -1299,7 +1403,7 @@ mod tests { // Insert in a chunk that does not exist. { assert_matches!( - linked_chunk.insert_items_at(['u', 'v'], ItemPosition(ChunkIdentifier(128), 0)), + linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(128), 0)), Err(LinkedChunkError::InvalidChunkIdentifier { identifier: ChunkIdentifier(128) }) ); } @@ -1307,7 +1411,7 @@ mod tests { // Insert in a chunk that exists, but at an item that does not exist. { assert_matches!( - linked_chunk.insert_items_at(['u', 'v'], ItemPosition(ChunkIdentifier(0), 128)), + linked_chunk.insert_items_at(['u', 'v'], Position(ChunkIdentifier(0), 128)), Err(LinkedChunkError::InvalidItemIndex { index: 128 }) ); } @@ -1316,9 +1420,9 @@ mod tests { { // It is impossible to get the item position inside a gap. It's only possible if // the item position is crafted by hand or is outdated. - let position_of_a_gap = ItemPosition(ChunkIdentifier(4), 0); + let position_of_a_gap = Position(ChunkIdentifier(4), 0); assert_matches!( - linked_chunk.insert_gap_at(position_of_a_gap, ()), + linked_chunk.insert_gap_at((), position_of_a_gap), Err(LinkedChunkError::ChunkIsAGap { identifier: ChunkIdentifier(4) }) ); } @@ -1331,10 +1435,10 @@ mod tests { #[test] fn test_replace_gap_at() -> Result<(), LinkedChunkError> { let mut linked_chunk = LinkedChunk::::new(); - linked_chunk.push_items_back(['a', 'b', 'c']); + linked_chunk.push_items_back(['a', 'b']); linked_chunk.push_gap_back(()); - linked_chunk.push_items_back(['l', 'm', 'n']); - assert_items_eq!(linked_chunk, ['a', 'b', 'c'] [-] ['l', 'm', 'n']); + linked_chunk.push_items_back(['l', 'm']); + assert_items_eq!(linked_chunk, ['a', 'b'] [-] ['l', 'm']); // Replace a gap in the middle of the linked chunk. { @@ -1344,7 +1448,7 @@ mod tests { linked_chunk.replace_gap_at(['d', 'e', 'f', 'g', 'h'], gap_identifier)?; assert_items_eq!( linked_chunk, - ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm', 'n'] + ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm'] ); } @@ -1353,7 +1457,7 @@ mod tests { linked_chunk.push_gap_back(()); assert_items_eq!( linked_chunk, - ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm', 'n'] [-] + ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm'] [-] ); let gap_identifier = linked_chunk.chunk_identifier(Chunk::is_gap).unwrap(); @@ -1362,12 +1466,52 @@ mod tests { linked_chunk.replace_gap_at(['w', 'x', 'y', 'z'], gap_identifier)?; assert_items_eq!( linked_chunk, - ['a', 'b', 'c'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm', 'n'] ['w', 'x', 'y'] ['z'] + ['a', 'b'] ['d', 'e', 'f'] ['g', 'h'] ['l', 'm'] ['w', 'x', 'y'] ['z'] ); } - assert_eq!(linked_chunk.len(), 15); + assert_eq!(linked_chunk.len(), 13); Ok(()) } + + #[test] + fn test_chunk_item_positions() { + let mut linked_chunk = LinkedChunk::::new(); + linked_chunk.push_items_back(['a', 'b', 'c', 'd', 'e']); + linked_chunk.push_gap_back(()); + linked_chunk.push_items_back(['f']); + + assert_items_eq!(linked_chunk, ['a', 'b', 'c'] ['d', 'e'] [-] ['f']); + + let mut iterator = linked_chunk.chunks(); + + // First chunk. + { + let chunk = iterator.next().unwrap(); + assert_eq!(chunk.first_position(), Position(ChunkIdentifier(0), 0)); + assert_eq!(chunk.last_position(), Position(ChunkIdentifier(0), 2)); + } + + // Second chunk. + { + let chunk = iterator.next().unwrap(); + assert_eq!(chunk.first_position(), Position(ChunkIdentifier(1), 0)); + assert_eq!(chunk.last_position(), Position(ChunkIdentifier(1), 1)); + } + + // Gap. + { + let chunk = iterator.next().unwrap(); + assert_eq!(chunk.first_position(), Position(ChunkIdentifier(2), 0)); + assert_eq!(chunk.last_position(), Position(ChunkIdentifier(2), 0)); + } + + // Last chunk. + { + let chunk = iterator.next().unwrap(); + assert_eq!(chunk.first_position(), Position(ChunkIdentifier(3), 0)); + assert_eq!(chunk.last_position(), Position(ChunkIdentifier(3), 0)); + } + } } diff --git a/crates/matrix-sdk/src/event_cache/store.rs b/crates/matrix-sdk/src/event_cache/store.rs index eede3c3c4d3..f5d12ae97e8 100644 --- a/crates/matrix-sdk/src/event_cache/store.rs +++ b/crates/matrix-sdk/src/event_cache/store.rs @@ -21,8 +21,8 @@ use tokio::sync::RwLock; use super::{ linked_chunk::{ - Chunk, ChunkIdentifier, ItemPosition, LinkedChunk, LinkedChunkError, LinkedChunkIter, - LinkedChunkIterBackward, + Chunk, ChunkIdentifier, LinkedChunk, LinkedChunkError, LinkedChunkIter, + LinkedChunkIterBackward, Position, }, Result, }; @@ -255,7 +255,7 @@ impl RoomEvents { pub fn insert_events_at( &mut self, events: I, - position: ItemPosition, + position: Position, ) -> StdResult<(), LinkedChunkError> where I: IntoIterator, @@ -267,10 +267,10 @@ impl RoomEvents { /// Insert a gap at a specified position. pub fn insert_gap_at( &mut self, - position: ItemPosition, gap: Gap, + position: Position, ) -> StdResult<(), LinkedChunkError> { - self.chunks.insert_gap_at(position, gap) + self.chunks.insert_gap_at(gap, position) } /// Search for a chunk, and return its identifier. @@ -282,7 +282,7 @@ impl RoomEvents { } /// Search for an item, and return its position. - pub fn event_position<'a, P>(&'a self, predicate: P) -> Option + pub fn event_position<'a, P>(&'a self, predicate: P) -> Option where P: FnMut(&'a SyncTimelineEvent) -> bool, { @@ -324,15 +324,15 @@ impl RoomEvents { /// Iterate over the events, backward. /// /// The most recent event comes first. - pub fn revents(&self) -> impl Iterator { + pub fn revents(&self) -> impl Iterator { self.chunks.ritems() } /// Iterate over the events, starting from `position`, backward. pub fn revents_from( &self, - position: ItemPosition, - ) -> StdResult, LinkedChunkError> { + position: Position, + ) -> StdResult, LinkedChunkError> { self.chunks.ritems_from(position) } @@ -340,8 +340,8 @@ impl RoomEvents { /// to the latest event. pub fn events_from( &self, - position: ItemPosition, - ) -> StdResult, LinkedChunkError> { + position: Position, + ) -> StdResult, LinkedChunkError> { self.chunks.items_from(position) } }