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 <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Michael Haggerty committed
Sep 25, 2017 at 10:00 UTC
d1cf15516feb024f34ae5fbbdad8f538b4e62fce
1 file changed
+106
-3
refs/packed-backend.c
+106
-3
@@ -397,6 +397,27 @@ static int cmp_packed_ref_entries(const void *v1, const void *v2)
397
}
398
}
399
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
+
421
/*
422
* `packed_refs->buf` is not known to be sorted. Check whether it is,
423
* 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)
524
return p;
525
}
526
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
+
538
/*
539
* We want to be able to compare mmapped reference records quickly,
540
* 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)
624
return 1;
625
}
626
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
+
686
/*
687
* Read from the `packed-refs` file into a newly-allocated
688
* `packed_ref_cache` and return it. The return value will already
@@ -888,6 +979,8 @@ static struct ref_iterator *packed_ref_iterator_begin(
979
const char *prefix, unsigned int flags)
980
{
981
struct packed_ref_store *refs;
982
+ struct packed_ref_cache *packed_refs;
983
+ const char *start;
984
struct packed_ref_iterator *iter;
985
struct ref_iterator *ref_iterator;
986
unsigned int required_flags = REF_STORE_READ;
@@ -905,13 +998,23 @@ static struct ref_iterator *packed_ref_iterator_begin(
998
* the packed-ref cache is up to date with what is on disk,
999
* and re-reads it if not.
1000
*/
1001
+ iter->cache = packed_refs = get_packed_ref_cache(refs);
1002
+ acquire_packed_ref_cache(packed_refs);
1003
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);
1011
1012
iter->flags = flags;
1013
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
+
1018
return ref_iterator;
1019
}
1020