refs/iterator: implement seeking for packed-ref iterators

Implement seeking of `packed-ref` iterators. The implementation is again straight forward, except that we cannot continue to use the prefix iterator as we would otherwise not be able to reseek the iterator anymore in case one first asks for an empty and then for a non-empty prefix. Instead, we open-code the logic to in `advance()`. 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 22600c045298f4f3664f94f6cbbf337903c72e82
1 file changed +43 -22
refs/packed-backend.c
+43 -22
@@ -819,6 +819,8 @@ struct packed_ref_iterator {
819
820 struct snapshot *snapshot;
821
822 + char *prefix;
823 +
824 /* The current position in the snapshot's buffer: */
825 const char *pos;
826
@@ -841,11 +843,9 @@ struct packed_ref_iterator {
843 };
844
845 /*
844 - * Move the iterator to the next record in the snapshot, without
845 - * respect for whether the record is actually required by the current
846 - * iteration. Adjust the fields in `iter` and return `ITER_OK` or
847 - * `ITER_DONE`. This function does not free the iterator in the case
848 - * of `ITER_DONE`.
846 + * Move the iterator to the next record in the snapshot. Adjust the fields in
847 + * `iter` and return `ITER_OK` or `ITER_DONE`. This function does not free the
848 + * iterator in the case of `ITER_DONE`.
849 */
850 static int next_record(struct packed_ref_iterator *iter)
851 {
@@ -942,6 +942,9 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
942 int ok;
943
944 while ((ok = next_record(iter)) == ITER_OK) {
945 + const char *refname = iter->base.refname;
946 + const char *prefix = iter->prefix;
947 +
948 if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
949 !is_per_worktree_ref(iter->base.refname))
950 continue;
@@ -951,12 +954,41 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
954 &iter->oid, iter->flags))
955 continue;
956
957 + while (prefix && *prefix) {
958 + if (*refname < *prefix)
959 + BUG("packed-refs backend yielded reference preceding its prefix");
960 + else if (*refname > *prefix)
961 + return ITER_DONE;
962 + prefix++;
963 + refname++;
964 + }
965 +
966 return ITER_OK;
967 }
968
969 return ok;
970 }
971
972 +static int packed_ref_iterator_seek(struct ref_iterator *ref_iterator,
973 + const char *prefix)
974 +{
975 + struct packed_ref_iterator *iter =
976 + (struct packed_ref_iterator *)ref_iterator;
977 + const char *start;
978 +
979 + if (prefix && *prefix)
980 + start = find_reference_location(iter->snapshot, prefix, 0);
981 + else
982 + start = iter->snapshot->start;
983 +
984 + free(iter->prefix);
985 + iter->prefix = xstrdup_or_null(prefix);
986 + iter->pos = start;
987 + iter->eof = iter->snapshot->eof;
988 +
989 + return 0;
990 +}
991 +
992 static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
993 struct object_id *peeled)
994 {
@@ -979,11 +1011,13 @@ static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
1011 (struct packed_ref_iterator *)ref_iterator;
1012 strbuf_release(&iter->refname_buf);
1013 free(iter->jump);
1014 + free(iter->prefix);
1015 release_snapshot(iter->snapshot);
1016 }
1017
1018 static struct ref_iterator_vtable packed_ref_iterator_vtable = {
1019 .advance = packed_ref_iterator_advance,
1020 + .seek = packed_ref_iterator_seek,
1021 .peel = packed_ref_iterator_peel,
1022 .release = packed_ref_iterator_release,
1023 };
@@ -1097,7 +1131,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
1131 {
1132 struct packed_ref_store *refs;
1133 struct snapshot *snapshot;
1100 - const char *start;
1134 struct packed_ref_iterator *iter;
1135 struct ref_iterator *ref_iterator;
1136 unsigned int required_flags = REF_STORE_READ;
@@ -1113,14 +1146,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
1146 */
1147 snapshot = get_snapshot(refs);
1148
1116 - if (prefix && *prefix)
1117 - start = find_reference_location(snapshot, prefix, 0);
1118 - else
1119 - start = snapshot->start;
1120 -
1121 - if (start == snapshot->eof)
1122 - return empty_ref_iterator_begin();
1123 -
1149 CALLOC_ARRAY(iter, 1);
1150 ref_iterator = &iter->base;
1151 base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable);
@@ -1130,19 +1155,15 @@ static struct ref_iterator *packed_ref_iterator_begin(
1155
1156 iter->snapshot = snapshot;
1157 acquire_snapshot(snapshot);
1133 -
1134 - iter->pos = start;
1135 - iter->eof = snapshot->eof;
1158 strbuf_init(&iter->refname_buf, 0);
1137 -
1159 iter->base.oid = &iter->oid;
1139 -
1160 iter->repo = ref_store->repo;
1161 iter->flags = flags;
1162
1143 - if (prefix && *prefix)
1144 - /* Stop iteration after we've gone *past* prefix: */
1145 - ref_iterator = prefix_ref_iterator_begin(ref_iterator, prefix, 0);
1163 + if (packed_ref_iterator_seek(&iter->base, prefix) < 0) {
1164 + ref_iterator_free(&iter->base);
1165 + return NULL;
1166 + }
1167
1168 return ref_iterator;
1169 }