On Tue, Sep 5, 2017 at 10:45 AM, Jeff King <[email protected]> wrote:
> On Tue, Aug 29, 2017 at 07:18:22PM +0200, Martin Ågren wrote:
>
>> [...]
>> Rearrange the handling of `referent`, so that we don't add it directly
>> to `affected_refnames`. Instead, first just check whether `referent`
>> exists in the string list, and later add `new_update->refname`.
>
> Coincidentally[1] I came across this same leak, and my solution ended up
> slightly different. I'll share it here in case it's of interest.
>
> In your solution we end up searching the string list twice: once to see
> if we have the item, and then again to insert it. Whereas in the
> original we did both with a single search.
>
> But we can observe that either:
>
> 1. The item already existed, in which case our insert was a noop, and
> we're good.
>
> or
>
> 2. We inserted it, in which case we proceed with creating new_update.
>
> We can then in O(1) replace the pointer in the string list item
> with the storage in new_update. We know we're not violating any
> string_list invariants because the strings contain the same bytes.
>
> I.e.:
>
> diff --git a/refs/files-backend.c b/refs/files-backend.c
> index 9266f5ab9d..1d16c1b33e 100644
> --- a/refs/files-backend.c
> +++ b/refs/files-backend.c
> @@ -2178,6 +2178,11 @@ static int split_symref_update(struct files_ref_store
> *refs,
> update->flags |= REF_LOG_ONLY | REF_NODEREF;
> update->flags &= ~REF_HAVE_OLD;
>
> + /*
> + * Re-point at the storage provided by our ref_update, which we know
> + * will last as long as the affected_refnames list.
> + */
> + item->string = new_update->refname;
> item->util = new_update;
>
> return 0;
>
> It feels pretty dirty, though. It would certainly be a bug if we ever
> decided to switch affected_refnames to duplicate its strings.
>
> So given that your solution is only a constant-time factor worse in
> efficiency, we should probably prefer it as the more maintainable
> option.
This is clever, but I don't like that it requires outside code to
change internal `string_list` structures in a way that is not
documented to be OK.
If we cared about getting rid of the extra `O(lg N)` search (and I
agree with you that it doesn't matter in this case), I think the clean
way to do it would be for `string_list` to expose a method like
struct string_list_item *string_list_insert_at_index(struct
string_list *list, size_t index, const char *string);
and to use it, together with `string_list_find_insert_index()`, to
avoid having to search twice.
Michael