refs/iterator: implement seeking for merged iterators

Implement seeking on merged iterators. The implementation is rather straight forward, with the only exception that we must not deallocate the underlying iterators once they have been exhausted. 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 9821d90f13c6442022bbbcb2d96f1b29aad76503
1 file changed +29 -9
refs/iterator.c
+29 -9
@@ -96,7 +96,8 @@ int is_empty_ref_iterator(struct ref_iterator *ref_iterator)
96 struct merge_ref_iterator {
97 struct ref_iterator base;
98
99 - struct ref_iterator *iter0, *iter1;
99 + struct ref_iterator *iter0, *iter0_owned;
100 + struct ref_iterator *iter1, *iter1_owned;
101
102 ref_iterator_select_fn *select;
103 void *cb_data;
@@ -160,13 +161,11 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
161 if (!iter->current) {
162 /* Initialize: advance both iterators to their first entries */
163 if ((ok = ref_iterator_advance(iter->iter0)) != ITER_OK) {
163 - ref_iterator_free(iter->iter0);
164 iter->iter0 = NULL;
165 if (ok == ITER_ERROR)
166 goto error;
167 }
168 if ((ok = ref_iterator_advance(iter->iter1)) != ITER_OK) {
169 - ref_iterator_free(iter->iter1);
169 iter->iter1 = NULL;
170 if (ok == ITER_ERROR)
171 goto error;
@@ -177,7 +176,6 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
176 * entry:
177 */
178 if ((ok = ref_iterator_advance(*iter->current)) != ITER_OK) {
180 - ref_iterator_free(*iter->current);
179 *iter->current = NULL;
180 if (ok == ITER_ERROR)
181 goto error;
@@ -206,7 +204,6 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
204
205 if (selection & ITER_SKIP_SECONDARY) {
206 if ((ok = ref_iterator_advance(*secondary)) != ITER_OK) {
209 - ref_iterator_free(*secondary);
207 *secondary = NULL;
208 if (ok == ITER_ERROR)
209 goto error;
@@ -226,6 +223,28 @@ error:
223 return ITER_ERROR;
224 }
225
226 +static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator,
227 + const char *prefix)
228 +{
229 + struct merge_ref_iterator *iter =
230 + (struct merge_ref_iterator *)ref_iterator;
231 + int ret;
232 +
233 + iter->current = NULL;
234 + iter->iter0 = iter->iter0_owned;
235 + iter->iter1 = iter->iter1_owned;
236 +
237 + ret = ref_iterator_seek(iter->iter0, prefix);
238 + if (ret < 0)
239 + return ret;
240 +
241 + ret = ref_iterator_seek(iter->iter1, prefix);
242 + if (ret < 0)
243 + return ret;
244 +
245 + return 0;
246 +}
247 +
248 static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
249 struct object_id *peeled)
250 {
@@ -242,12 +261,13 @@ static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
261 {
262 struct merge_ref_iterator *iter =
263 (struct merge_ref_iterator *)ref_iterator;
245 - ref_iterator_free(iter->iter0);
246 - ref_iterator_free(iter->iter1);
264 + ref_iterator_free(iter->iter0_owned);
265 + ref_iterator_free(iter->iter1_owned);
266 }
267
268 static struct ref_iterator_vtable merge_ref_iterator_vtable = {
269 .advance = merge_ref_iterator_advance,
270 + .seek = merge_ref_iterator_seek,
271 .peel = merge_ref_iterator_peel,
272 .release = merge_ref_iterator_release,
273 };
@@ -268,8 +288,8 @@ struct ref_iterator *merge_ref_iterator_begin(
288 */
289
290 base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable);
271 - iter->iter0 = iter0;
272 - iter->iter1 = iter1;
291 + iter->iter0 = iter->iter0_owned = iter0;
292 + iter->iter1 = iter->iter1_owned = iter1;
293 iter->select = select;
294 iter->cb_data = cb_data;
295 iter->current = NULL;