@@ -19,7 +19,7 @@ use matrix_sdk_base::{
19
19
event_cache:: store:: DEFAULT_CHUNK_CAPACITY ,
20
20
linked_chunk:: {
21
21
lazy_loader:: { self , LazyLoaderError } ,
22
- ChunkContent , ChunkIdentifierGenerator , RawChunk ,
22
+ ChunkContent , ChunkIdentifierGenerator , OrderTracker , RawChunk ,
23
23
} ,
24
24
} ;
25
25
use matrix_sdk_common:: linked_chunk:: {
@@ -37,6 +37,9 @@ pub struct RoomEvents {
37
37
///
38
38
/// [`Update`]: matrix_sdk_base::linked_chunk::Update
39
39
chunks_updates_as_vectordiffs : AsVector < Event , Gap > ,
40
+
41
+ /// Tracker of the events ordering in this room.
42
+ pub order_tracker : OrderTracker < Event , Gap > ,
40
43
}
41
44
42
45
impl Default for RoomEvents {
@@ -48,24 +51,27 @@ impl Default for RoomEvents {
48
51
impl RoomEvents {
49
52
/// Build a new [`RoomEvents`] struct with zero events.
50
53
pub fn new ( ) -> Self {
51
- Self :: with_initial_linked_chunk ( None )
54
+ Self :: with_initial_linked_chunk ( None , None )
52
55
}
53
56
54
57
/// Build a new [`RoomEvents`] struct with prior chunks knowledge.
55
58
///
56
59
/// The provided [`LinkedChunk`] must have been built with update history.
57
60
pub fn with_initial_linked_chunk (
58
61
linked_chunk : Option < LinkedChunk < DEFAULT_CHUNK_CAPACITY , Event , Gap > > ,
62
+ fully_loaded_linked_chunk : Option < LinkedChunk < DEFAULT_CHUNK_CAPACITY , Event , Gap > > ,
59
63
) -> Self {
60
64
let mut linked_chunk = linked_chunk. unwrap_or_else ( LinkedChunk :: new_with_update_history) ;
61
65
62
66
let chunks_updates_as_vectordiffs = linked_chunk
63
67
. as_vector ( )
64
- // SAFETY: The `LinkedChunk` has been built with `new_with_update_history`, so
65
- // `as_vector` must return `Some(…)`.
66
68
. expect ( "`LinkedChunk` must have been built with `new_with_update_history`" ) ;
67
69
68
- Self { chunks : linked_chunk, chunks_updates_as_vectordiffs }
70
+ let order_tracker = linked_chunk
71
+ . order_tracker ( fully_loaded_linked_chunk. as_ref ( ) . map ( |lc| lc. chunks ( ) ) )
72
+ . expect ( "`LinkedChunk` must have been built with `new_with_update_history`" ) ;
73
+
74
+ Self { chunks : linked_chunk, chunks_updates_as_vectordiffs, order_tracker }
69
75
}
70
76
71
77
/// Clear all events.
@@ -76,6 +82,20 @@ impl RoomEvents {
76
82
self . chunks . clear ( ) ;
77
83
}
78
84
85
+ fn inhibit_updates_to_ordering_tracker < F : FnOnce ( & mut Self ) -> R , R > ( & mut self , f : F ) -> R {
86
+ // Start by flushing previous pending updates to the chunk ordering, if any.
87
+ self . order_tracker . flush_updates ( false ) ;
88
+
89
+ // Call the function.
90
+ let r = f ( self ) ;
91
+
92
+ // Now, flush other pending updates which have been caused by the function, and
93
+ // ignore them.
94
+ self . order_tracker . flush_updates ( true ) ;
95
+
96
+ r
97
+ }
98
+
79
99
/// Replace the events with the given last chunk of events and generator.
80
100
///
81
101
/// This clears all the chunks in memory before resetting to the new chunk,
@@ -85,7 +105,11 @@ impl RoomEvents {
85
105
last_chunk : Option < RawChunk < Event , Gap > > ,
86
106
chunk_identifier_generator : ChunkIdentifierGenerator ,
87
107
) -> Result < ( ) , LazyLoaderError > {
88
- lazy_loader:: replace_with ( & mut self . chunks , last_chunk, chunk_identifier_generator)
108
+ // Since `replace_with` is used only to unload some chunks, we don't want it to
109
+ // affect the chunk ordering.
110
+ self . inhibit_updates_to_ordering_tracker ( move |this| {
111
+ lazy_loader:: replace_with ( & mut this. chunks , last_chunk, chunk_identifier_generator)
112
+ } )
89
113
}
90
114
91
115
/// Push events after all events or gaps.
@@ -231,6 +255,35 @@ impl RoomEvents {
231
255
self . chunks . items ( )
232
256
}
233
257
258
+ /// Return the order of an event in the room (main) linked chunk.
259
+ ///
260
+ /// Can return `None` if the event can't be found in the linked chunk.
261
+ pub fn event_order ( & self , event_pos : Position ) -> Option < usize > {
262
+ self . order_tracker . ordering ( event_pos)
263
+ }
264
+
265
+ #[ cfg( any( test, debug_assertions) ) ]
266
+ fn assert_event_ordering ( & self ) {
267
+ let mut iter = self . chunks . items ( ) . enumerate ( ) ;
268
+ let Some ( ( i, ( first_event_pos, _) ) ) = iter. next ( ) else {
269
+ return ;
270
+ } ;
271
+
272
+ // Sanity check.
273
+ assert_eq ! ( i, 0 ) ;
274
+
275
+ // That's the offset in the full linked chunk. Will be 0 if the linked chunk is
276
+ // entirely loaded, may be non-zero otherwise.
277
+ let offset =
278
+ self . event_order ( first_event_pos) . expect ( "first event's ordering must be known" ) ;
279
+
280
+ for ( i, ( next_pos, _) ) in iter {
281
+ let next_index =
282
+ self . event_order ( next_pos) . expect ( "next event's ordering must be known" ) ;
283
+ assert_eq ! ( offset + i, next_index, "event ordering must be continuous" ) ;
284
+ }
285
+ }
286
+
234
287
/// Get all updates from the room events as [`VectorDiff`].
235
288
///
236
289
/// Be careful that each `VectorDiff` is returned only once!
@@ -239,7 +292,17 @@ impl RoomEvents {
239
292
///
240
293
/// [`Update`]: matrix_sdk_base::linked_chunk::Update
241
294
pub fn updates_as_vector_diffs ( & mut self ) -> Vec < VectorDiff < Event > > {
242
- self . chunks_updates_as_vectordiffs . take ( )
295
+ let updates = self . chunks_updates_as_vectordiffs . take ( ) ;
296
+
297
+ self . order_tracker . flush_updates ( false ) ;
298
+
299
+ if cfg ! ( any( test, debug_assertions) ) {
300
+ // Assert that the orderings are fully correct for all the events present in the
301
+ // in-memory linked chunk.
302
+ self . assert_event_ordering ( ) ;
303
+ }
304
+
305
+ updates
243
306
}
244
307
245
308
/// Get a mutable reference to the [`LinkedChunk`] updates, aka
@@ -258,8 +321,9 @@ impl RoomEvents {
258
321
pub fn debug_string ( & self ) -> Vec < String > {
259
322
let mut result = Vec :: new ( ) ;
260
323
261
- for chunk in self . chunks ( ) {
262
- let content = chunk_debug_string ( chunk. content ( ) ) ;
324
+ for chunk in self . chunks . chunks ( ) {
325
+ let content =
326
+ chunk_debug_string ( chunk. identifier ( ) , chunk. content ( ) , & self . order_tracker ) ;
263
327
let lazy_previous = if let Some ( cid) = chunk. lazy_previous ( ) {
264
328
format ! ( " (lazy previous = {})" , cid. index( ) )
265
329
} else {
@@ -289,24 +353,40 @@ impl RoomEvents {
289
353
& mut self ,
290
354
raw_new_first_chunk : RawChunk < Event , Gap > ,
291
355
) -> Result < ( ) , LazyLoaderError > {
292
- lazy_loader:: insert_new_first_chunk ( & mut self . chunks , raw_new_first_chunk)
356
+ // This is only used when reinserting a chunk that was in persisted storage, so
357
+ // we don't need to touch the chunk ordering for this.
358
+ self . inhibit_updates_to_ordering_tracker ( move |this| {
359
+ lazy_loader:: insert_new_first_chunk ( & mut this. chunks , raw_new_first_chunk)
360
+ } )
293
361
}
294
362
}
295
363
296
364
/// Create a debug string for a [`ChunkContent`] for an event/gap pair.
297
- fn chunk_debug_string ( content : & ChunkContent < Event , Gap > ) -> String {
365
+ fn chunk_debug_string (
366
+ chunk_id : ChunkIdentifier ,
367
+ content : & ChunkContent < Event , Gap > ,
368
+ order_tracker : & OrderTracker < Event , Gap > ,
369
+ ) -> String {
298
370
match content {
299
371
ChunkContent :: Gap ( Gap { prev_token } ) => {
300
372
format ! ( "gap['{prev_token}']" )
301
373
}
302
374
ChunkContent :: Items ( vec) => {
303
375
let items = vec
304
376
. iter ( )
305
- . map ( |event| {
306
- // Limit event ids to 8 chars *after* the $.
377
+ . enumerate ( )
378
+ . map ( | ( i , event) | {
307
379
event. event_id ( ) . map_or_else (
308
380
|| "<no event id>" . to_owned ( ) ,
309
- |id| id. as_str ( ) . chars ( ) . take ( 1 + 8 ) . collect ( ) ,
381
+ |id| {
382
+ let pos = Position :: new ( chunk_id, i) ;
383
+ let order = format ! ( "#{}: " , order_tracker. ordering( pos) . unwrap( ) ) ;
384
+
385
+ // Limit event ids to 8 chars *after* the $.
386
+ let event_id = id. as_str ( ) . chars ( ) . take ( 1 + 8 ) . collect :: < String > ( ) ;
387
+
388
+ format ! ( "{order}{event_id}" )
389
+ } ,
310
390
)
311
391
} )
312
392
. collect :: < Vec < _ > > ( )
@@ -719,10 +799,13 @@ mod tests {
719
799
] ) ;
720
800
room_events. push_gap ( Gap { prev_token : "raclette" . to_owned ( ) } ) ;
721
801
802
+ // Flush updates to the order tracker.
803
+ let _ = room_events. updates_as_vector_diffs ( ) ;
804
+
722
805
let output = room_events. debug_string ( ) ;
723
806
724
807
assert_eq ! ( output. len( ) , 2 ) ;
725
- assert_eq ! ( & output[ 0 ] , "chunk #0: events[$12345678, $2]" ) ;
808
+ assert_eq ! ( & output[ 0 ] , "chunk #0: events[#0: $12345678, #1: $2]" ) ;
726
809
assert_eq ! ( & output[ 1 ] , "chunk #1: gap['raclette']" ) ;
727
810
}
728
811
0 commit comments