Skip to content

Commit 4d3cc0d

Browse files
committed
doc(sdk): Add moooooar documentation.
This patch adds documentation that explains how `AsVectorSubscriber` works.
1 parent 77918af commit 4d3cc0d

File tree

2 files changed

+78
-7
lines changed

2 files changed

+78
-7
lines changed

crates/matrix-sdk/src/event_cache/linked_chunk/as_vector.rs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,61 @@ use super::{
3232
type ChunkLength = usize;
3333

3434
pin_project! {
35-
/// A type used to transform a `Stream<Item = Vec<Update<Item, Gap>>>` into
36-
/// a `Stream<Item = Vec<VectorDiff<Item>>>`. Basically, it helps to consume
37-
// a [`LinkedChunk<CAP, Item, Gap>`] as if it was an [`ObservableVector<Item>`].
35+
/// A type that transforms a `Stream<Item = Vec<Update<Item, Gap>>>` —given by
36+
/// [`UpdateSubscriber`]— into a `Stream<Item = Vec<VectorDiff<Item>>>` —this
37+
/// type—. Basically, it helps to consume a [`LinkedChunk<CAP, Item, Gap>`] as
38+
/// if it was an [`eyeball::ObservableVector<Item>`].
39+
///
40+
/// How this type transforms `Update` into `VectorDiff`? There is no internal
41+
/// buffer of kind [`eyeball_im::ObservableVector<Item>`], which could have been
42+
/// used to generate the `VectorDiff`s. They are computed manually.
43+
///
44+
/// The only buffered data is pairs of [`ChunkIdentifier`] and [`ChunkLength`].
45+
/// The following rules must be respected:
46+
///
47+
/// * A chunk of kind [`ChunkContent::Gap`] has a length of 0,
48+
/// * A chunk of kind [`ChunkContent::Items`] has a length equals to its number
49+
/// of items,
50+
/// * The pairs must be ordered exactly like the chunks in [`LinkedChunk`], i.e.
51+
/// the first pair must represent the first chunk, the last pair must
52+
/// represent the last chunk.
53+
///
54+
/// The only thing this algorithm does is maintaining the pairs:
55+
///
56+
/// * [`Update::NewItemsChunk`] and [`Update::NewGapChunk`] are inserting a new
57+
/// pair with a chunk length of 0 at the appropriate index,
58+
/// * [`Update::RemoveChunk`] is removing a pair,
59+
/// * [`Update::PushItems`] is increasing the length of the appropriate pair by
60+
/// the number of new items, and is potentially emitting [`VectorDiff`],
61+
/// * [`Update::DetachLastItems`] is decreasing the length of the appropriate pair
62+
/// by the number of items to be detached; no [`VectorDiff`] is emitted,
63+
/// * [`Update::ReattachItems`] and [`Update::ReattachItemsDone`] are
64+
/// respectively muting or unmuting the emission of [`VectorDiff`] by
65+
/// [`Update::PushItems`].
66+
///
67+
/// The only `VectorDiff` that are emitted are [`VectorDiff::Insert`] or
68+
/// [`VectorDiff::Append`] because a [`LinkedChunk`] is append-only.
69+
///
70+
/// `VectorDiff::Append` is an optimisation when numerous `VectorDiff::Insert`s
71+
/// have to be emitted at the last position.
72+
///
73+
/// `VectorDiff::Insert` need an index. To compute this index, the algorithm
74+
/// will iterate over all pairs to accumulate each chunk length until it finds
75+
/// the appropriate pair (given by [`Update::PushItems::position_hint`]). This
76+
/// is _the offset_. To this offset, the algorithm adds the position's index of
77+
/// the new items (still given by [`Update::PushItems::position_hint`]). This is
78+
/// _the index_. This logic works for all cases as long as pairs are maintained
79+
/// according to the rules hereinabove.
80+
///
81+
/// That's a pretty memory compact and computation efficient way to map a
82+
/// `Stream<Item = Vec<Update<Item, Gap>>>` into a `Stream<Item =
83+
/// Vec<VectorDiff<Item>>>`. The larger the `LinkedChunk` capacity is, the fewer
84+
/// pairs the algorithm will have to handle, e.g. for 1'000 items and a
85+
/// `LinkedChunk` capacity of 128, it's only 8 pairs, be 256 bytes.
86+
///
87+
/// [`LinkedChunk`]: super::LinkedChunk
88+
/// [`ChunkContent::Gap`]: super::ChunkContent::Gap
89+
/// [`ChunkContent::Content`]: super::ChunkContent::Content
3890
pub struct AsVectorSubscriber<Item, Gap> {
3991
// The inner `UpdatesSubscriber`.
4092
#[pin]

crates/matrix-sdk/src/event_cache/linked_chunk/mod.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
#![allow(dead_code)]
1616

17+
//! A linked chunk is the underlying data structure that holds all events.
18+
1719
/// A macro to test the items and the gap of a `LinkedChunk`.
1820
/// A chunk is delimited by `[` and `]`. An item chunk has the form `[a, b,
1921
/// c]` where `a`, `b` and `c` are items. A gap chunk has the form `[-]`.
@@ -107,17 +109,33 @@ use updates::*;
107109
/// Errors of [`LinkedChunk`].
108110
#[derive(thiserror::Error, Debug)]
109111
pub enum Error {
112+
/// A chunk identifier is invalid.
110113
#[error("The chunk identifier is invalid: `{identifier:?}`")]
111-
InvalidChunkIdentifier { identifier: ChunkIdentifier },
114+
InvalidChunkIdentifier {
115+
/// The chunk identifier.
116+
identifier: ChunkIdentifier,
117+
},
112118

119+
/// A chunk is a gap, and it was expected to be an items.
113120
#[error("The chunk is a gap: `{identifier:?}`")]
114-
ChunkIsAGap { identifier: ChunkIdentifier },
121+
ChunkIsAGap {
122+
/// The chunk identifier.
123+
identifier: ChunkIdentifier,
124+
},
115125

126+
/// A chunk is an items, and it was expected to be a gap.
116127
#[error("The chunk is an item: `{identifier:?}`")]
117-
ChunkIsItems { identifier: ChunkIdentifier },
128+
ChunkIsItems {
129+
/// The chunk identifier.
130+
identifier: ChunkIdentifier,
131+
},
118132

133+
/// An item index is invalid.
119134
#[error("The item index is invalid: `{index}`")]
120-
InvalidItemIndex { index: usize },
135+
InvalidItemIndex {
136+
/// The index.
137+
index: usize,
138+
},
121139
}
122140

123141
/// Links of a `LinkedChunk`, i.e. the first and last [`Chunk`].
@@ -719,6 +737,7 @@ impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
719737
self.updates.as_mut()
720738
}
721739

740+
/// Get a `Stream<Item = Vec<VectorDiff<Item>>>` of this `LinkedChunk`.
722741
pub fn subscribe_as_vector(&mut self) -> Option<AsVectorSubscriber<Item, Gap>> {
723742
let mut initial_chunk_lengths = VecDeque::new();
724743

0 commit comments

Comments
 (0)