refs/iterator: implement seeking for ref-cache iterators
Implement seeking of ref-cache iterators. This is done by splitting most of the logic to seek iterators out of `cache_ref_iterator_begin()` and putting it into `cache_ref_iterator_seek()` so that we can reuse the logic. Note that we cannot use the optimization anymore where we return an empty ref iterator when there aren't any references, as otherwise it wouldn't be possible to reseek the iterator to a different prefix that may exist. This shouldn't be much of a performance concern though as we now start to bail out early in case `advance()` sees that there are no more directories to be searched. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Patrick Steinhardt committed
Mar 12, 2025 at 16:56 UTC
84e656919cb7237f1b11a948974d0591d9d3434f
1 file changed
+51
-28
refs/ref-cache.c
+51
-28
@@ -362,9 +362,7 @@ struct cache_ref_iterator {
362
struct ref_iterator base;
363
364
/*
365
- * The number of levels currently on the stack. This is always
366
- * at least 1, because when it becomes zero the iteration is
367
- * ended and this struct is freed.
365
+ * The number of levels currently on the stack.
366
*/
367
size_t levels_nr;
368
@@ -376,7 +374,7 @@ struct cache_ref_iterator {
374
* The prefix is matched textually, without regard for path
375
* component boundaries.
376
*/
379
- const char *prefix;
377
+ char *prefix;
378
379
/*
380
* A stack of levels. levels[0] is the uppermost level that is
@@ -389,6 +387,9 @@ struct cache_ref_iterator {
387
struct cache_ref_iterator_level *levels;
388
389
struct repository *repo;
390
+ struct ref_cache *cache;
391
+
392
+ int prime_dir;
393
};
394
395
static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
@@ -396,6 +397,9 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
397
struct cache_ref_iterator *iter =
398
(struct cache_ref_iterator *)ref_iterator;
399
400
+ if (!iter->levels_nr)
401
+ return ITER_DONE;
402
+
403
while (1) {
404
struct cache_ref_iterator_level *level =
405
&iter->levels[iter->levels_nr - 1];
@@ -444,6 +448,41 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
448
}
449
}
450
451
+static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
452
+ const char *prefix)
453
+{
454
+ struct cache_ref_iterator *iter =
455
+ (struct cache_ref_iterator *)ref_iterator;
456
+ struct cache_ref_iterator_level *level;
457
+ struct ref_dir *dir;
458
+
459
+ dir = get_ref_dir(iter->cache->root);
460
+ if (prefix && *prefix)
461
+ dir = find_containing_dir(dir, prefix);
462
+ if (!dir) {
463
+ iter->levels_nr = 0;
464
+ return 0;
465
+ }
466
+
467
+ if (iter->prime_dir)
468
+ prime_ref_dir(dir, prefix);
469
+ iter->levels_nr = 1;
470
+ level = &iter->levels[0];
471
+ level->index = -1;
472
+ level->dir = dir;
473
+
474
+ if (prefix && *prefix) {
475
+ free(iter->prefix);
476
+ iter->prefix = xstrdup(prefix);
477
+ level->prefix_state = PREFIX_WITHIN_DIR;
478
+ } else {
479
+ FREE_AND_NULL(iter->prefix);
480
+ level->prefix_state = PREFIX_CONTAINS_DIR;
481
+ }
482
+
483
+ return 0;
484
+}
485
+
486
static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
487
struct object_id *peeled)
488
{
@@ -456,12 +495,13 @@ static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
495
{
496
struct cache_ref_iterator *iter =
497
(struct cache_ref_iterator *)ref_iterator;
459
- free((char *)iter->prefix);
498
+ free(iter->prefix);
499
free(iter->levels);
500
}
501
502
static struct ref_iterator_vtable cache_ref_iterator_vtable = {
503
.advance = cache_ref_iterator_advance,
504
+ .seek = cache_ref_iterator_seek,
505
.peel = cache_ref_iterator_peel,
506
.release = cache_ref_iterator_release,
507
};
@@ -471,39 +511,22 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
511
struct repository *repo,
512
int prime_dir)
513
{
474
- struct ref_dir *dir;
514
struct cache_ref_iterator *iter;
515
struct ref_iterator *ref_iterator;
477
- struct cache_ref_iterator_level *level;
478
-
479
- dir = get_ref_dir(cache->root);
480
- if (prefix && *prefix)
481
- dir = find_containing_dir(dir, prefix);
482
- if (!dir)
483
- /* There's nothing to iterate over. */
484
- return empty_ref_iterator_begin();
485
-
486
- if (prime_dir)
487
- prime_ref_dir(dir, prefix);
516
517
CALLOC_ARRAY(iter, 1);
518
ref_iterator = &iter->base;
519
base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable);
520
ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
521
494
- iter->levels_nr = 1;
495
- level = &iter->levels[0];
496
- level->index = -1;
497
- level->dir = dir;
522
+ iter->repo = repo;
523
+ iter->cache = cache;
524
+ iter->prime_dir = prime_dir;
525
499
- if (prefix && *prefix) {
500
- iter->prefix = xstrdup(prefix);
501
- level->prefix_state = PREFIX_WITHIN_DIR;
502
- } else {
503
- level->prefix_state = PREFIX_CONTAINS_DIR;
526
+ if (cache_ref_iterator_seek(&iter->base, prefix) < 0) {
527
+ ref_iterator_free(&iter->base);
528
+ return NULL;
529
}
530
506
- iter->repo = repo;
507
-
531
return ref_iterator;
532
}