git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 5/5] load_ref_decorations(): avoid parsing non-tag objects

From
Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Date
Jun 22, 2021, 18:27 UTC
Message-ID
<87zgvh20j3.fsf@evledraar.gmail.com>
In-Reply-To
<YNIYqFFFti73UT5+@coredump.intra.peff.net>
On Tue, Jun 22 2021, Jeff King wrote:
Show 19 quoted lines
> On Tue, Jun 22, 2021 at 12:35:46PM -0400, Derrick Stolee wrote:
>
>> On 6/22/2021 12:08 PM, Jeff King wrote:
>> 
>> > -	obj = parse_object(the_repository, oid);
>> > -	if (!obj)
>> > +	objtype = oid_object_info(the_repository, oid, NULL);
>> > +	if (type < 0)
>> >  		return 0;
>> 
>> Do you mean "if (objtype < 0)" here? There is a 'type' variable,
>> but it is an enum decoration_type and I can't find a reason why
>> it would be negative. oid_object_info() _does_ return -1 if there
>> is a problem loading the object, so that would make sense.
>
> Whoops, thanks for catching that. I originally called it "enum
> object_type type", but then of course the compiler informed that there
> was already a "type" variable in the function. So I renamed it to
> "objtype" but missed updating that line. But it still compiled. Yikes. :)
[Enter Captain Hindsight]

If you use a slightly different coding style and leverage the information the compiler has to work with you'd get it to error for you, e.g. this on your original patch would catch it:

	diff --git a/log-tree.c b/log-tree.c
	index 8b700e9c142..7e3a011b533 100644
	--- a/log-tree.c
	+++ b/log-tree.c
	@@ -157,9 +157,12 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
	 	}
	 
	 	objtype = oid_object_info(the_repository, oid, NULL);
	-	if (type < 0)
	+	switch (type) {
	+	case OBJ_BAD:
	 		return 0;
	-	obj = lookup_object_by_type(the_repository, oid, objtype);
	+	default:
	+		obj = lookup_object_by_type(the_repository, oid, objtype);
	+	}
	 
	 	if (starts_with(refname, "refs/heads/"))
	 		type = DECORATION_REF_LOCAL;
IMO the real problem is an over-reliance on C being so happy to treat
enums as ints (well, with them being ints). If you consistently use
labels you get the compiler to do the checking. For me with gcc and
clang with that on top:
	
	log-tree.c:161:2: error: case value ‘4294967295’ not in enumerated type ‘enum decoration_type’ [-Werror=switch]
	  case OBJ_BAD:
	  ^~~~
	log-tree.c:161:7: error: case value not in enumerated type 'enum decoration_type' [-Werror,-Wswitch]
	        case OBJ_BAD:
	             ^

I think we've disagreed on that exact point before recently, i.e. you think we shouldn't rely on OBJ_BAD in that way, and instead check for any negative value: https://lore.kernel.org/git/YHCZh5nLNVEHCWV2@coredump.intra.peff.net/

This sort of thing is a good reason to pick the opposite pattern. You get the same type checking you'd usually get with anything else in C.

Yes, it is more verbose e.g. in this case, and particularly (as noted downthread of what I linked to) because "enum object_type" contains so many uncommon things, and really should be split up.

In practice I don't think it's too verbose, because once you start
consistently using the pattern you'll usually not be doing conversions
all over the place, and would just do this sort of thing via a helper
that does the type checking, e.g. something like this (or anything else
where you don't lose the type & labels):
	
	diff --git a/log-tree.c b/log-tree.c
	index 8b700e9c142..a61fb01ba3f 100644
	--- a/log-tree.c
	+++ b/log-tree.c
	@@ -130,6 +130,30 @@ static int ref_filter_match(const char *refname,
	 	return 1;
	 }
	 
	+static enum object_type oid_object_info_ok(struct repository *repo,
	+					   struct object_id *oid,
	+					   enum object_type *typep,
	+					   unsigned long *sizep)
	+{
	+	enum object_type type = oid_object_info(repo, oid, sizep);
	+	*typep = type;
	+	switch (type) {
	+	case OBJ_BAD:
	+		return 0;
	+	case OBJ_COMMIT:
	+	case OBJ_TREE:
	+	case OBJ_BLOB:
	+	case OBJ_TAG:
	+		return 1;
	+	case OBJ_NONE:
	+	case OBJ_OFS_DELTA:
	+	case OBJ_REF_DELTA:
	+	case OBJ_ANY:
	+	case OBJ_MAX:
	+		BUG("the enum_object type is too large!");
	+	}
	+}
	+
	 static int add_ref_decoration(const char *refname, const struct object_id *oid,
	 			      int flags, void *cb_data)
	 {
	@@ -156,8 +180,7 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
	 		return 0;
	 	}
	 
	-	objtype = oid_object_info(the_repository, oid, NULL);
	-	if (type < 0)
	+	if (!oid_object_info_ok(the_repository, oid, &type, NULL))
	 		return 0;
	 	obj = lookup_object_by_type(the_repository, oid, objtype);
	 

With that pattern GCC narrowlry pulls ahead with showing 4 warnings just about the loss of the type, with Clang at 3 :)

Previous: Derrick StoleeNext: Jeff King
Message 11 of 16 in “some "log --decorate" optimizations”
  1. 0/5 some "log --decorate" optimizationsJeff King, Jun 22, 2021
  2. 1/5 pretty.h: update and expand docstring for userformat_find_requirements()Jeff King, Jun 22, 2021
  3. 2/5 log: avoid loading decorations for userformats that don't need itJeff King, Jun 22, 2021
  4. 3/5 object.h: expand docstring for lookup_unknown_object()Jeff King, Jun 22, 2021
  5. 4/5 object.h: add lookup_object_by_type() functionJeff King, Jun 22, 2021
  6. 5/5 load_ref_decorations(): avoid parsing non-tag objectsJeff King, Jun 22, 2021
  7. Derrick StoleeJun 22, 2021
  8. Jeff KingJun 22, 2021
  9. Jeff KingJun 22, 2021
  10. Derrick StoleeJun 22, 2021
  11. Ævar Arnfjörð BjarmasonJun 22, 2021
  12. Jeff KingJun 22, 2021
  13. Ævar Arnfjörð BjarmasonJun 22, 2021
  14. Jeff KingJun 22, 2021
  15. Taylor BlauJun 23, 2021
  16. Jeff KingJun 23, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.