Skip to content

Commit

Permalink
temp!
Browse files Browse the repository at this point in the history
  • Loading branch information
Hywan committed Nov 20, 2024
1 parent 2e77be2 commit 74b346f
Show file tree
Hide file tree
Showing 2 changed files with 240 additions and 8 deletions.
22 changes: 22 additions & 0 deletions crates/matrix-sdk-common/src/linked_chunk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,13 @@ impl ChunkIdentifierGenerator {
#[repr(transparent)]
pub struct ChunkIdentifier(u64);

impl ChunkIdentifier {
/// Create a new chunk identifier.
pub fn new(identifier: u64) -> Self {
Self(identifier)
}
}

impl PartialEq<u64> for ChunkIdentifier {
fn eq(&self, other: &u64) -> bool {
self.0 == *other
Expand All @@ -948,6 +955,11 @@ impl PartialEq<u64> for ChunkIdentifier {
pub struct Position(ChunkIdentifier, usize);

impl Position {
/// Create a new position.
pub fn new(chunk_identifier: ChunkIdentifier, index: usize) -> Self {
Self(chunk_identifier, index)
}

/// Get the chunk identifier of the item.
pub fn chunk_identifier(&self) -> ChunkIdentifier {
self.0
Expand All @@ -966,6 +978,16 @@ impl Position {
pub fn decrement_index(&mut self) {
self.1 = self.1.checked_sub(1).expect("Cannot decrement the index because it's already 0");
}

/// Increment the index part (see [`Self::index`]), i.e. add 1.
///
/// # Panic
///
/// This method will panic if it will overflow, i.e. if the index is larger
/// than `usize::MAX`.
pub fn increment_index(&mut self) {
self.1 = self.1.checked_add(1).expect("Cannot increment the index because it's too large");
}
}

/// An iterator over a [`LinkedChunk`] that traverses the chunk in backward
Expand Down
226 changes: 218 additions & 8 deletions crates/matrix-sdk-common/src/store_locks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,15 +552,225 @@ pub mod memory_store_helper {
}
}

pub fn handle_linked_chunk_updates<const CHUNK_CAPACITY: usize, Event, Gap>(
linked_chunk: &RwLock<LinkedChunk<CHUNK_CAPACITY, Event, Gap>>,
updates: &[Update<Event, Gap>],
) {
for update in updates {
match update {
Update::NewItemsChunk
pub mod relational_linked_chunk {
use crate::linked_chunk::{ChunkIdentifier, Position, Update};

type Chunks = Vec<(Option<ChunkIdentifier>, ChunkIdentifier, Option<ChunkIdentifier>)>;

#[derive(Debug)]
enum Content<Item, Gap> {
Item(Item),
Gap(Gap),
}

/// A [`LinkedChunk`] but with a relational layout, similar to what we would have in a database.
///
/// This is used by memory stores.
#[derive(Debug)]
pub struct RelationalLinkedChunk<Item, Gap> {
chunks: Chunks,
items: Vec<(Position, Content<Item, Gap>)>,
}

impl<Item, Gap> RelationalLinkedChunk<Item, Gap> {
pub fn new() -> Self {
Self { chunks: Vec::new(), items: Vec::new() }
}
}

pub fn handle_linked_chunk_updates<Item, Gap>(
linked_chunk: &mut RelationalLinkedChunk<Item, Gap>,
updates: &[Update<Item, Gap>],
) where
Item: Clone,
Gap: Clone,
{
for update in updates {
match update {
Update::NewItemsChunk { previous, new, next } => {
insert_chunk(&mut linked_chunk.chunks, previous, new, next);
}

Update::NewGapChunk { previous, new, next, gap } => {
insert_chunk(&mut linked_chunk.chunks, previous, new, next);
linked_chunk
.items
.push((Position::new(*new, 0), Content::Gap(gap.clone())));
}

Update::RemoveChunk(chunk_identifier) => {
remove_chunk(&mut linked_chunk.chunks, chunk_identifier);

let indices_to_remove = linked_chunk
.items
.iter()
.enumerate()
.filter_map(|(nth, (position, _))| {
(position.chunk_identifier() == *chunk_identifier).then_some(nth)
})
.collect::<Vec<_>>();

for index_to_remove in indices_to_remove.into_iter().rev() {
linked_chunk.items.remove(index_to_remove);
}
}

Update::PushItems { at, items } => {
let mut at = at.clone();

for item in items {
linked_chunk.items.push((at.clone(), Content::Item(item.clone())));
at.increment_index();
}
}

Update::RemoveItem { at } => {
let entry_to_remove = linked_chunk
.items
.iter()
.position(|(position, _)| position == at)
.expect("Remove an unkwnown item");

linked_chunk.items.remove(entry_to_remove);
}

Update::DetachLastItems { at } => {
let indices_to_remove = linked_chunk
.items
.iter()
.enumerate()
.filter_map(|(nth, (position, _))| {
(position.chunk_identifier() == at.chunk_identifier()
&& position.index() >= at.index())
.then_some(nth)
})
.collect::<Vec<_>>();

for index_to_remove in indices_to_remove.into_iter().rev() {
linked_chunk.items.remove(index_to_remove);
}
}

Update::StartReattachItems | Update::EndReattachItems => { /* nothing */ }
}
}

fn insert_chunk(
chunks: &mut Chunks,
previous: &Option<ChunkIdentifier>,
new: &ChunkIdentifier,
next: &Option<ChunkIdentifier>,
) {
// Find the previous chunk, and update its next chunk.
if let Some(previous) = previous {
let entry_for_previous_chunk = chunks
.iter_mut()
.find(|(_, chunk_candidate, _)| chunk_candidate == previous)
.expect("Previous chunk should be present");

// Insert the chunk.
entry_for_previous_chunk.2 = Some(*new);
}

// Find the next chunk, and update its previous chunk.
if let Some(next) = next {
let entry_for_next_chunk = chunks
.iter_mut()
.find(|(_, chunk_candidate, _)| chunk_candidate == next)
.expect("Next chunk should be present");

// Insert the chunk.
entry_for_next_chunk.0 = Some(*new);
}

// Insert the chunk.
chunks.push((*previous, *new, *next));
}

fn remove_chunk(chunks: &mut Chunks, chunk: &ChunkIdentifier) {
let entry_nth_to_remove = chunks
.iter()
.enumerate()
.find_map(|(nth, (_, chunk_candidate, _))| {
(chunk_candidate == chunk).then_some(nth)
})
.expect("Remove an unknown chunk");

let (previous, _, next) = chunks.remove(entry_nth_to_remove);

// Find the previous chunk, and update its next chunk.
if let Some(previous) = previous {
let entry_for_previous_chunk = chunks
.iter_mut()
.find(|(_, chunk_candidate, _)| *chunk_candidate == previous)
.expect("Previous chunk should be present");

// Insert the chunk.
entry_for_previous_chunk.2 = next;
}

// Find the next chunk, and update its previous chunk.
if let Some(next) = next {
let entry_for_next_chunk = chunks
.iter_mut()
.find(|(_, chunk_candidate, _)| *chunk_candidate == next)
.expect("Next chunk should be present");

// Insert the chunk.
entry_for_next_chunk.0 = previous;
}
}
}

#[cfg(test)]
mod tests {
use super::*;

macro_rules! cid {
($id:literal) => {
ChunkIdentifier::new($id)
};
}

#[test]
fn test_new_items_chunk() {
let mut relational_linked_chunk = RelationalLinkedChunk::<char, ()>::new();

handle_linked_chunk_updates(
&mut relational_linked_chunk,
&[
// 0
Update::NewItemsChunk { previous: None, new: cid!(0), next: None },
// 1 after 0
Update::NewItemsChunk { previous: Some(cid!(0)), new: cid!(1), next: None },
// 2 before 0
Update::NewItemsChunk { previous: None, new: cid!(2), next: Some(cid!(0)) },
// 3 between 2 and 0
Update::NewItemsChunk {
previous: Some(cid!(2)),
new: cid!(3),
next: Some(cid!(0)),
},
],
);

assert_eq!(
relational_linked_chunk.chunks,
&[
(Some(cid!(3)), cid!(0), Some(cid!(1))),
(Some(cid!(0)), cid!(1), None),
(None, cid!(2), Some(cid!(3))),
(Some(cid!(2)), cid!(3), Some(cid!(0))),
]
)
}

/*
#[test]
fn test_new_gap_chunk() {
let mut relational_linked_chunk = RelationalLinkedChunk::<char, ()>::new();
}
*/
}
todo!()
}
}

0 comments on commit 74b346f

Please sign in to comment.