Subject: Re: [PATCH v2 14/16] refs/iterator: implement seeking for `packed-ref` iterators

From: Patrick Steinhardt <[email-masked]>
Date: Tue, 25 Feb 2025 08:39:54 +0100

On Mon, Feb 24, 2025 at 11:09:32PM +0800, shejialuo wrote:
> On Wed, Feb 19, 2025 at 02:23:41PM +0100, Patrick Steinhardt wrote:
> > 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 <[email-masked]>
> > ---
> >  refs/packed-backend.c | 62 +++++++++++++++++++++++++++++++++------------------
> >  1 file changed, 40 insertions(+), 22 deletions(-)
> > 
> > diff --git a/refs/packed-backend.c b/refs/packed-backend.c
> > index 38a1956d1a8..71a38acfedc 100644
> > --- a/refs/packed-backend.c
> > +++ b/refs/packed-backend.c
> > @@ -951,12 +954,41 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
> >  					    &iter->oid, iter->flags))
> >  			continue;
> >  
> > +		while (prefix && *prefix) {
> > +			if (*refname < *prefix)
> > +				BUG("packed-refs backend yielded reference preceding its prefix");
> > +			else if (*refname > *prefix)
> > +				return ITER_DONE;
> > +			prefix++;
> > +			refname++;
> > +		}
> 
> Although I cannot understand the code, I want to ask a question here, we
> we need to do this in `advance`? Should we check this for
> `packed_ref_iterator_seek` or in the `next_record` function?
> 
> Before we introduce `seek`, we don't need this logic. I somehow think we
> should do this in `packed_ref_iterator_seek`.

We cannot do this in `packed_ref_iterator_seek()` because we need to do
it for every single record that we yield from the iterator. We _could_
do it in `next_record()`, but that function is rather complex already
and really only cares about yielding the next record. On the other hand,
`advance()` already knows to skip certain entries, so putting the logic
in there to also handle termination feels like a natural fit to me.

> > @@ -1130,19 +1155,12 @@ static struct ref_iterator *packed_ref_iterator_begin(
> >  
> >  	iter->snapshot = snapshot;
> >  	acquire_snapshot(snapshot);
> > -
> > -	iter->pos = start;
> > -	iter->eof = snapshot->eof;
> >  	strbuf_init(&iter->refname_buf, 0);
> > -
> >  	iter->base.oid = &iter->oid;
> > -
> >  	iter->repo = ref_store->repo;
> >  	iter->flags = flags;
> >  
> > -	if (prefix && *prefix)
> > -		/* Stop iteration after we've gone *past* prefix: */
> > -		ref_iterator = prefix_ref_iterator_begin(ref_iterator, prefix, 0);
> > +	packed_ref_iterator_seek(&iter->base, prefix);
> 
> Why don't we check the return value here? Actually, in the previous
> patch, `cache_ref_iterator_seek` will always return 0, but you still
> check. I have thought that you just want to be more defensive.

Yeah, let's have the check over here, as well.

Patrick