Skip to content

Commit d1cf155

Browse files
mhaggergitster
authored andcommitted
packed_ref_iterator_begin(): iterate using mmapped_ref_iterator
Now that we have an efficient way to iterate, in order, over the mmapped contents of the `packed-refs` file, we can use that directly to implement reference iteration for the `packed_ref_store`, rather than iterating over the `ref_cache`. This is the next step towards getting rid of the `ref_cache` entirely. Signed-off-by: Michael Haggerty <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 02b920f commit d1cf155

File tree

1 file changed

+106
-3
lines changed

1 file changed

+106
-3
lines changed

refs/packed-backend.c

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,27 @@ static int cmp_packed_ref_entries(const void *v1, const void *v2)
397397
}
398398
}
399399

400+
/*
401+
* Compare a packed-refs record pointed to by `rec` to the specified
402+
* NUL-terminated refname.
403+
*/
404+
static int cmp_entry_to_refname(const char *rec, const char *refname)
405+
{
406+
const char *r1 = rec + GIT_SHA1_HEXSZ + 1;
407+
const char *r2 = refname;
408+
409+
while (1) {
410+
if (*r1 == '\n')
411+
return *r2 ? -1 : 0;
412+
if (!*r2)
413+
return 1;
414+
if (*r1 != *r2)
415+
return (unsigned char)*r1 < (unsigned char)*r2 ? -1 : +1;
416+
r1++;
417+
r2++;
418+
}
419+
}
420+
400421
/*
401422
* `packed_refs->buf` is not known to be sorted. Check whether it is,
402423
* and if not, sort it into new memory and munmap/free the old
@@ -503,6 +524,17 @@ static const char *find_start_of_record(const char *buf, const char *p)
503524
return p;
504525
}
505526

527+
/*
528+
* Return a pointer to the start of the record following the record
529+
* that contains `*p`. If none is found before `end`, return `end`.
530+
*/
531+
static const char *find_end_of_record(const char *p, const char *end)
532+
{
533+
while (++p < end && (p[-1] != '\n' || p[0] == '^'))
534+
;
535+
return p;
536+
}
537+
506538
/*
507539
* We want to be able to compare mmapped reference records quickly,
508540
* without totally parsing them. We can do so because the records are
@@ -592,6 +624,65 @@ static int load_contents(struct packed_ref_cache *packed_refs)
592624
return 1;
593625
}
594626

627+
/*
628+
* Find the place in `cache->buf` where the start of the record for
629+
* `refname` starts. If `mustexist` is true and the reference doesn't
630+
* exist, then return NULL. If `mustexist` is false and the reference
631+
* doesn't exist, then return the point where that reference would be
632+
* inserted. In the latter mode, `refname` doesn't have to be a proper
633+
* reference name; for example, one could search for "refs/replace/"
634+
* to find the start of any replace references.
635+
*
636+
* The record is sought using a binary search, so `cache->buf` must be
637+
* sorted.
638+
*/
639+
static const char *find_reference_location(struct packed_ref_cache *cache,
640+
const char *refname, int mustexist)
641+
{
642+
/*
643+
* This is not *quite* a garden-variety binary search, because
644+
* the data we're searching is made up of records, and we
645+
* always need to find the beginning of a record to do a
646+
* comparison. A "record" here is one line for the reference
647+
* itself and zero or one peel lines that start with '^'. Our
648+
* loop invariant is described in the next two comments.
649+
*/
650+
651+
/*
652+
* A pointer to the character at the start of a record whose
653+
* preceding records all have reference names that come
654+
* *before* `refname`.
655+
*/
656+
const char *lo = cache->buf + cache->header_len;
657+
658+
/*
659+
* A pointer to a the first character of a record whose
660+
* reference name comes *after* `refname`.
661+
*/
662+
const char *hi = cache->eof;
663+
664+
while (lo < hi) {
665+
const char *mid, *rec;
666+
int cmp;
667+
668+
mid = lo + (hi - lo) / 2;
669+
rec = find_start_of_record(lo, mid);
670+
cmp = cmp_entry_to_refname(rec, refname);
671+
if (cmp < 0) {
672+
lo = find_end_of_record(mid, hi);
673+
} else if (cmp > 0) {
674+
hi = rec;
675+
} else {
676+
return rec;
677+
}
678+
}
679+
680+
if (mustexist)
681+
return NULL;
682+
else
683+
return lo;
684+
}
685+
595686
/*
596687
* Read from the `packed-refs` file into a newly-allocated
597688
* `packed_ref_cache` and return it. The return value will already
@@ -888,6 +979,8 @@ static struct ref_iterator *packed_ref_iterator_begin(
888979
const char *prefix, unsigned int flags)
889980
{
890981
struct packed_ref_store *refs;
982+
struct packed_ref_cache *packed_refs;
983+
const char *start;
891984
struct packed_ref_iterator *iter;
892985
struct ref_iterator *ref_iterator;
893986
unsigned int required_flags = REF_STORE_READ;
@@ -905,13 +998,23 @@ static struct ref_iterator *packed_ref_iterator_begin(
905998
* the packed-ref cache is up to date with what is on disk,
906999
* and re-reads it if not.
9071000
*/
1001+
iter->cache = packed_refs = get_packed_ref_cache(refs);
1002+
acquire_packed_ref_cache(packed_refs);
9081003

909-
iter->cache = get_packed_ref_cache(refs);
910-
acquire_packed_ref_cache(iter->cache);
911-
iter->iter0 = cache_ref_iterator_begin(iter->cache->cache, prefix, 0);
1004+
if (prefix && *prefix)
1005+
start = find_reference_location(packed_refs, prefix, 0);
1006+
else
1007+
start = packed_refs->buf + packed_refs->header_len;
1008+
1009+
iter->iter0 = mmapped_ref_iterator_begin(
1010+
packed_refs, start, packed_refs->eof);
9121011

9131012
iter->flags = flags;
9141013

1014+
if (prefix && *prefix)
1015+
/* Stop iteration after we've gone *past* prefix: */
1016+
ref_iterator = prefix_ref_iterator_begin(ref_iterator, prefix, 0);
1017+
9151018
return ref_iterator;
9161019
}
9171020

0 commit comments

Comments
 (0)