prefix_ref_iterator: break when we leave the prefix

If the underlying iterator is ordered, then `prefix_ref_iterator` can stop as soon as it sees a refname that comes after the prefix. This will rarely make a big difference now, because `ref_cache_iterator` only iterates over the directory containing the prefix (and usually the prefix will span a whole directory anyway). But if *hint, hint* a future reference backend doesn't itself know where to stop the iteration, then this optimization will be a big win. Note that there is no guarantee that the underlying iterator doesn't include output preceding the prefix, so we have to skip over any unwanted references before we get to the ones that we want. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Sep 13, 2017 at 19:15 UTC 157113c614034dc247e1aa6fb65448125f71f7ca
1 file changed +31 -1
refs/iterator.c
+31 -1
@@ -287,6 +287,20 @@ struct prefix_ref_iterator {
287 int trim;
288 };
289
290 +/* Return -1, 0, 1 if refname is before, inside, or after the prefix. */
291 +static int compare_prefix(const char *refname, const char *prefix)
292 +{
293 + while (*prefix) {
294 + if (*refname != *prefix)
295 + return ((unsigned char)*refname < (unsigned char)*prefix) ? -1 : +1;
296 +
297 + refname++;
298 + prefix++;
299 + }
300 +
301 + return 0;
302 +}
303 +
304 static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
305 {
306 struct prefix_ref_iterator *iter =
@@ -294,9 +308,25 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
308 int ok;
309
310 while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
297 - if (!starts_with(iter->iter0->refname, iter->prefix))
311 + int cmp = compare_prefix(iter->iter0->refname, iter->prefix);
312 +
313 + if (cmp < 0)
314 continue;
315
316 + if (cmp > 0) {
317 + /*
318 + * If the source iterator is ordered, then we
319 + * can stop the iteration as soon as we see a
320 + * refname that comes after the prefix:
321 + */
322 + if (iter->iter0->ordered) {
323 + ok = ref_iterator_abort(iter->iter0);
324 + break;
325 + } else {
326 + continue;
327 + }
328 + }
329 +
330 if (iter->trim) {
331 /*
332 * It is nonsense to trim off characters that