attr: remove maybe-real, maybe-macro from git_attr

Whether or not a git attribute is real or a macro isn't a property of the attribute but rather it depends on the attribute stack (which .gitattribute files were read). This patch removes the 'maybe_real' and 'maybe_macro' fields in a git_attr and instead adds the 'macro' field to a attr_check_item. The 'macro' indicates (if non-NULL) that a particular attribute is a macro for the given attribute stack. It's populated, through a quick scan of the attribute stack, with the match_attr that corresponds to the macro's definition. This way the attribute stack only needs to be scanned a single time prior to attribute collection instead of each time a macro needs to be expanded. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Brandon Williams committed Jan 27, 2017 at 18:02 UTC 60a12722ac80bc55b43581a7719d25d1bb7a9882
1 file changed +37 -38
attr.c
+37 -38
@@ -30,20 +30,9 @@ static const char git_attr__unknown[] = "(builtin)unknown";
30
31 struct git_attr {
32 int attr_nr; /* unique attribute number */
33 - int maybe_macro;
34 - int maybe_real;
33 char name[FLEX_ARRAY]; /* attribute name */
34 };
35
38 -/*
39 - * NEEDSWORK: maybe-real, maybe-macro are not property of
40 - * an attribute, as it depends on what .gitattributes are
41 - * read. Once we introduce per git_attr_check attr_stack
42 - * and check_all_attr, the optimization based on them will
43 - * become unnecessary and can go away. So is this variable.
44 - */
45 -static int cannot_trust_maybe_real;
46 -
36 const char *git_attr_name(const struct git_attr *attr)
37 {
38 return attr->name;
@@ -142,6 +131,12 @@ static void attr_hashmap_add(struct attr_hashmap *map,
131 struct all_attrs_item {
132 const struct git_attr *attr;
133 const char *value;
134 + /*
135 + * If 'macro' is non-NULL, indicates that 'attr' is a macro based on
136 + * the current attribute stack and contains a pointer to the match_attr
137 + * definition of the macro
138 + */
139 + const struct match_attr *macro;
140 };
141
142 /*
@@ -187,6 +182,7 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
182 */
183 for (i = 0; i < check->all_attrs_nr; i++) {
184 check->all_attrs[i].value = ATTR__UNKNOWN;
185 + check->all_attrs[i].macro = NULL;
186 }
187 }
188
@@ -238,8 +234,6 @@ static struct git_attr *git_attr_internal(const char *name, int namelen)
234 if (!a) {
235 FLEX_ALLOC_MEM(a, name, name, namelen);
236 a->attr_nr = g_attr_hashmap.map.size;
241 - a->maybe_real = 0;
242 - a->maybe_macro = 0;
237
238 attr_hashmap_add(&g_attr_hashmap, a->name, namelen, a);
239 assert(a->attr_nr == (g_attr_hashmap.map.size - 1));
@@ -402,7 +396,6 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
396 (is_macro ? 0 : namelen + 1));
397 if (is_macro) {
398 res->u.attr = git_attr_internal(name, namelen);
405 - res->u.attr->maybe_macro = 1;
399 } else {
400 char *p = (char *)&(res->state[num_attr]);
401 memcpy(p, name, namelen);
@@ -423,10 +416,6 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
416 /* Second pass to fill the attr_states */
417 for (cp = states, i = 0; *cp; i++) {
418 cp = parse_attr(src, lineno, cp, &(res->state[i]));
426 - if (!is_macro)
427 - res->state[i].attr->maybe_real = 1;
428 - if (res->state[i].attr->maybe_macro)
429 - cannot_trust_maybe_real = 1;
419 }
420
421 strbuf_release(&pattern);
@@ -904,7 +893,7 @@ static int path_matches(const char *pathname, int pathlen,
893 static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem);
894
895 static int fill_one(const char *what, struct all_attrs_item *all_attrs,
907 - struct match_attr *a, int rem)
896 + const struct match_attr *a, int rem)
897 {
898 int i;
899
@@ -945,24 +934,34 @@ static int fill(const char *path, int pathlen, int basename_offset,
934
935 static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem)
936 {
948 - struct attr_stack *stk;
949 - int i;
937 + const struct all_attrs_item *item = &all_attrs[nr];
938
951 - if (all_attrs[nr].value != ATTR__TRUE ||
952 - !all_attrs[nr].attr->maybe_macro)
939 + if (item->macro && item->value == ATTR__TRUE)
940 + return fill_one("expand", all_attrs, item->macro, rem);
941 + else
942 return rem;
943 +}
944
955 - for (stk = attr_stack; stk; stk = stk->prev) {
956 - for (i = stk->num_matches - 1; 0 <= i; i--) {
957 - struct match_attr *ma = stk->attrs[i];
958 - if (!ma->is_macro)
959 - continue;
960 - if (ma->u.attr->attr_nr == nr)
961 - return fill_one("expand", all_attrs, ma, rem);
945 +/*
946 + * Marks the attributes which are macros based on the attribute stack.
947 + * This prevents having to search through the attribute stack each time
948 + * a macro needs to be expanded during the fill stage.
949 + */
950 +static void determine_macros(struct all_attrs_item *all_attrs,
951 + const struct attr_stack *stack)
952 +{
953 + for (; stack; stack = stack->prev) {
954 + int i;
955 + for (i = stack->num_matches - 1; i >= 0; i--) {
956 + const struct match_attr *ma = stack->attrs[i];
957 + if (ma->is_macro) {
958 + int n = ma->u.attr->attr_nr;
959 + if (!all_attrs[n].macro) {
960 + all_attrs[n].macro = ma;
961 + }
962 + }
963 }
964 }
964 -
965 - return rem;
965 }
966
967 /*
@@ -992,15 +991,15 @@ static void collect_some_attrs(const char *path, struct attr_check *check)
991
992 prepare_attr_stack(path, dirlen);
993 all_attrs_init(&g_attr_hashmap, check);
994 + determine_macros(check->all_attrs, attr_stack);
995
996 - if (check->nr && !cannot_trust_maybe_real) {
996 + if (check->nr) {
997 rem = 0;
998 for (i = 0; i < check->nr; i++) {
999 - const struct git_attr *a = check->items[i].attr;
1000 - if (!a->maybe_real) {
1001 - struct all_attrs_item *c;
1002 - c = check->all_attrs + a->attr_nr;
1003 - c->value = ATTR__UNSET;
999 + int n = check->items[i].attr->attr_nr;
1000 + struct all_attrs_item *item = &check->all_attrs[n];
1001 + if (item->macro) {
1002 + item->value = ATTR__UNSET;
1003 rem++;
1004 }
1005 }