commit: rewrite read_graft_line

Old implementation determined number of hashes by dividing length of
line by length of hash, which works only if all hash representations
have same length.

New graft line parser works in two phases:

  1. In first phase line is scanned to verify correctness and compute
     number of hashes, then graft struct is allocated.

  2. In second phase line is scanned again to fill up already allocated
     graft struct.

This way graft parsing code can support different sizes of hashes
without any further code adaptations.

A number of alternative implementations were considered and discarded:

  - Modifying graft structure to store oid_array instead of FLEXI_ARRAY
    indicates undesirable usage of struct to readers.

  - Parsing into temporary string_list or oid_array complicates code
    by adding more return paths, as these structures needs to be
    cleared before returning from function.

  - Determining number of hashes by counting separators might cause
    maintenance issues, if this function needs to be modified in future
    again.

Signed-off-by: Patryk Obara <patryk.obara@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Patryk Obara 2017-08-18 20:33:14 +02:00 коммит произвёл Junio C Hamano
Родитель bc65d2262d
Коммит cfa5bf1608
1 изменённых файлов: 21 добавлений и 15 удалений

Просмотреть файл

@ -137,32 +137,38 @@ int register_commit_graft(struct commit_graft *graft, int ignore_dups)
struct commit_graft *read_graft_line(struct strbuf *line)
{
/* The format is just "Commit Parent1 Parent2 ...\n" */
int i;
int i, phase;
const char *tail = NULL;
struct commit_graft *graft = NULL;
const int entry_size = GIT_SHA1_HEXSZ + 1;
struct object_id dummy_oid, *oid;
strbuf_rtrim(line);
if (!line->len || line->buf[0] == '#')
return NULL;
if ((line->len + 1) % entry_size)
goto bad_graft_data;
i = (line->len + 1) / entry_size - 1;
graft = xmalloc(st_add(sizeof(*graft),
st_mult(sizeof(struct object_id), i)));
graft->nr_parent = i;
if (get_oid_hex(line->buf, &graft->oid))
goto bad_graft_data;
for (i = GIT_SHA1_HEXSZ; i < line->len; i += entry_size) {
if (line->buf[i] != ' ')
goto bad_graft_data;
if (get_sha1_hex(line->buf + i + 1, graft->parent[i/entry_size].hash))
/*
* phase 0 verifies line, counts hashes in line and allocates graft
* phase 1 fills graft
*/
for (phase = 0; phase < 2; phase++) {
oid = graft ? &graft->oid : &dummy_oid;
if (parse_oid_hex(line->buf, oid, &tail))
goto bad_graft_data;
for (i = 0; *tail != '\0'; i++) {
oid = graft ? &graft->parent[i] : &dummy_oid;
if (!isspace(*tail++) || parse_oid_hex(tail, oid, &tail))
goto bad_graft_data;
}
if (!graft) {
graft = xmalloc(st_add(sizeof(*graft),
st_mult(sizeof(struct object_id), i)));
graft->nr_parent = i;
}
}
return graft;
bad_graft_data:
error("bad graft data: %s", line->buf);
free(graft);
assert(!graft);
return NULL;
}