@@ -273,35 +273,46 @@ void block_reader_start(struct block_reader *br, struct block_iter *it)
273
273
it -> next_off = br -> header_off + 4 ;
274
274
}
275
275
276
- struct restart_find_args {
276
+ struct restart_needle_less_args {
277
277
int error ;
278
- struct strbuf key ;
279
- struct block_reader * r ;
278
+ struct strbuf needle ;
279
+ struct block_reader * reader ;
280
280
};
281
281
282
- static int restart_key_less (size_t idx , void * args )
282
+ static int restart_needle_less (size_t idx , void * _args )
283
283
{
284
- struct restart_find_args * a = args ;
285
- uint32_t off = block_reader_restart_offset (a -> r , idx );
284
+ struct restart_needle_less_args * args = _args ;
285
+ uint32_t off = block_reader_restart_offset (args -> reader , idx );
286
286
struct string_view in = {
287
- .buf = a -> r -> block .data + off ,
288
- .len = a -> r -> block_len - off ,
287
+ .buf = args -> reader -> block .data + off ,
288
+ .len = args -> reader -> block_len - off ,
289
289
};
290
+ uint64_t prefix_len , suffix_len ;
291
+ uint8_t extra ;
292
+ int n ;
293
+
294
+ /*
295
+ * Records at restart points are stored without prefix compression, so
296
+ * there is no need to fully decode the record key here. This removes
297
+ * the need for allocating memory.
298
+ */
299
+ n = reftable_decode_keylen (in , & prefix_len , & suffix_len , & extra );
300
+ if (n < 0 || prefix_len ) {
301
+ args -> error = 1 ;
302
+ return -1 ;
303
+ }
290
304
291
- /* the restart key is verbatim in the block, so this could avoid the
292
- alloc for decoding the key */
293
- struct strbuf rkey = STRBUF_INIT ;
294
- uint8_t unused_extra ;
295
- int n = reftable_decode_key (& rkey , & unused_extra , in );
296
- int result ;
297
- if (n < 0 ) {
298
- a -> error = 1 ;
305
+ string_view_consume (& in , n );
306
+ if (suffix_len > in .len ) {
307
+ args -> error = 1 ;
299
308
return -1 ;
300
309
}
301
310
302
- result = strbuf_cmp (& a -> key , & rkey );
303
- strbuf_release (& rkey );
304
- return result < 0 ;
311
+ n = memcmp (args -> needle .buf , in .buf ,
312
+ args -> needle .len < suffix_len ? args -> needle .len : suffix_len );
313
+ if (n )
314
+ return n < 0 ;
315
+ return args -> needle .len < suffix_len ;
305
316
}
306
317
307
318
void block_iter_copy_from (struct block_iter * dest , struct block_iter * src )
@@ -376,20 +387,51 @@ void block_iter_close(struct block_iter *it)
376
387
int block_reader_seek (struct block_reader * br , struct block_iter * it ,
377
388
struct strbuf * want )
378
389
{
379
- struct restart_find_args args = {
380
- .key = * want ,
381
- .r = br ,
390
+ struct restart_needle_less_args args = {
391
+ .needle = * want ,
392
+ .reader = br ,
382
393
};
383
394
struct block_iter next = BLOCK_ITER_INIT ;
384
395
struct reftable_record rec ;
385
- int err = 0 , i ;
386
-
396
+ int err = 0 ;
397
+ size_t i ;
398
+
399
+ /*
400
+ * Perform a binary search over the block's restart points, which
401
+ * avoids doing a linear scan over the whole block. Like this, we
402
+ * identify the section of the block that should contain our key.
403
+ *
404
+ * Note that we explicitly search for the first restart point _greater_
405
+ * than the sought-after record, not _greater or equal_ to it. In case
406
+ * the sought-after record is located directly at the restart point we
407
+ * would otherwise start doing the linear search at the preceding
408
+ * restart point. While that works alright, we would end up scanning
409
+ * too many record.
410
+ */
411
+ i = binsearch (br -> restart_count , & restart_needle_less , & args );
387
412
if (args .error ) {
388
413
err = REFTABLE_FORMAT_ERROR ;
389
414
goto done ;
390
415
}
391
416
392
- i = binsearch (br -> restart_count , & restart_key_less , & args );
417
+ /*
418
+ * Now there are multiple cases:
419
+ *
420
+ * - `i == 0`: The wanted record is smaller than the record found at
421
+ * the first restart point. As the first restart point is the first
422
+ * record in the block, our wanted record cannot be located in this
423
+ * block at all. We still need to position the iterator so that the
424
+ * next call to `block_iter_next()` will yield an end-of-iterator
425
+ * signal.
426
+ *
427
+ * - `i == restart_count`: The wanted record was not found at any of
428
+ * the restart points. As there is no restart point at the end of
429
+ * the section the record may thus be contained in the last block.
430
+ *
431
+ * - `i > 0`: The wanted record must be contained in the section
432
+ * before the found restart point. We thus do a linear search
433
+ * starting from the preceding restart point.
434
+ */
393
435
if (i > 0 )
394
436
it -> next_off = block_reader_restart_offset (br , i - 1 );
395
437
else
@@ -398,21 +440,34 @@ int block_reader_seek(struct block_reader *br, struct block_iter *it,
398
440
399
441
reftable_record_init (& rec , block_reader_type (br ));
400
442
401
- /* We're looking for the last entry less/equal than the wanted key, so
402
- we have to go one entry too far and then back up.
403
- */
443
+ /*
444
+ * We're looking for the last entry less than the wanted key so that
445
+ * the next call to `block_reader_next()` would yield the wanted
446
+ * record. We thus don't want to position our reader at the sought
447
+ * after record, but one before. To do so, we have to go one entry too
448
+ * far and then back up.
449
+ */
404
450
while (1 ) {
405
451
block_iter_copy_from (& next , it );
406
452
err = block_iter_next (& next , & rec );
407
453
if (err < 0 )
408
454
goto done ;
409
-
410
- reftable_record_key (& rec , & it -> last_key );
411
- if (err > 0 || strbuf_cmp (& it -> last_key , want ) >= 0 ) {
455
+ if (err > 0 ) {
412
456
err = 0 ;
413
457
goto done ;
414
458
}
415
459
460
+ /*
461
+ * Check whether the current key is greater or equal to the
462
+ * sought-after key. In case it is greater we know that the
463
+ * record does not exist in the block and can thus abort early.
464
+ * In case it is equal to the sought-after key we have found
465
+ * the desired record.
466
+ */
467
+ reftable_record_key (& rec , & it -> last_key );
468
+ if (strbuf_cmp (& it -> last_key , want ) >= 0 )
469
+ goto done ;
470
+
416
471
block_iter_copy_from (it , & next );
417
472
}
418
473
0 commit comments