@@ -196,6 +196,65 @@ where
196
196
Ok ( ( ) )
197
197
}
198
198
199
+ /// A pretty inefficient, test-only, function to rebuild a full `LinkedChunk`.
200
+ #[ doc( hidden) ]
201
+ pub fn from_all_chunks < const CAP : usize , Item , Gap > (
202
+ mut chunks : Vec < RawChunk < Item , Gap > > ,
203
+ ) -> Result < Option < LinkedChunk < CAP , Item , Gap > > , LazyLoaderError >
204
+ where
205
+ Item : Clone ,
206
+ Gap : Clone ,
207
+ {
208
+ if chunks. is_empty ( ) {
209
+ return Ok ( None ) ;
210
+ }
211
+
212
+ // Sort by `next` so that the search for the next chunk is faster (it should
213
+ // come first). The chunk with the biggest next chunk identifier comes first.
214
+ // Chunk with no next chunk comes last.
215
+ chunks. sort_by ( |a, b| b. next . cmp ( & a. next ) ) ;
216
+
217
+ let last_chunk = chunks
218
+ . pop ( )
219
+ // SAFETY: `chunks` is guaranteed to not be empty, `pop` cannot fail.
220
+ . expect ( "`chunks` is supposed to not be empty, we must be able to `pop` an item" ) ;
221
+ let last_chunk_identifier = last_chunk. identifier ;
222
+ let chunk_identifier_generator =
223
+ ChunkIdentifierGenerator :: new_from_previous_chunk_identifier ( last_chunk_identifier) ;
224
+
225
+ let Some ( mut linked_chunk) = from_last_chunk ( Some ( last_chunk) , chunk_identifier_generator) ?
226
+ else {
227
+ return Ok ( None ) ;
228
+ } ;
229
+
230
+ let mut next_chunk = last_chunk_identifier;
231
+
232
+ while let Some ( chunk) = chunks
233
+ . iter ( )
234
+ . position ( |chunk| chunk. next == Some ( next_chunk) )
235
+ . map ( |index| chunks. remove ( index) )
236
+ {
237
+ next_chunk = chunk. identifier ;
238
+ insert_new_first_chunk ( & mut linked_chunk, chunk) ?;
239
+ }
240
+
241
+ let first_chunk = linked_chunk. links . first_chunk ( ) ;
242
+
243
+ // It is expected that **all chunks** are passed to this function. If there was
244
+ // a previous chunk, `insert_new_first_chunk` has erased it and moved it to
245
+ // `lazy_previous`. Hence, let's check both (the former condition isn't
246
+ // necessary, but better be robust).
247
+ if first_chunk. previous ( ) . is_some ( ) || first_chunk. lazy_previous . is_some ( ) {
248
+ return Err ( LazyLoaderError :: ChunkIsNotFirst { id : first_chunk. identifier ( ) } ) ;
249
+ }
250
+
251
+ if !chunks. is_empty ( ) {
252
+ return Err ( LazyLoaderError :: MultipleConnectedComponents ) ;
253
+ }
254
+
255
+ Ok ( Some ( linked_chunk) )
256
+ }
257
+
199
258
#[ derive( thiserror:: Error , Debug ) ]
200
259
pub enum LazyLoaderError {
201
260
#[ error( "chunk with id {} has a next chunk, it is supposed to be the last chunk" , id. index( ) ) ]
@@ -216,6 +275,18 @@ pub enum LazyLoaderError {
216
275
217
276
#[ error( "chunk with id {} is too large" , id. index( ) ) ]
218
277
ChunkTooLarge { id : ChunkIdentifier } ,
278
+
279
+ #[ doc( hidden) ]
280
+ #[ error( "the last chunk is missing" ) ]
281
+ MissingLastChunk ,
282
+
283
+ #[ doc( hidden) ]
284
+ #[ error( "chunk with id {} has a previous chunk, it is supposed to be the first chunk" , id. index( ) ) ]
285
+ ChunkIsNotFirst { id : ChunkIdentifier } ,
286
+
287
+ #[ doc( hidden) ]
288
+ #[ error( "multiple connected components" ) ]
289
+ MultipleConnectedComponents ,
219
290
}
220
291
221
292
#[ cfg( test) ]
0 commit comments