teach git-index-pack about deltas with offset to base

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Nicolas Pitre 2006-09-21 00:08:33 -04:00 committed by Junio C Hamano
parent 209c554ab4
commit 53dda6ff62

View File

@ -18,10 +18,15 @@ struct object_entry
unsigned char sha1[20]; unsigned char sha1[20];
}; };
union delta_base {
unsigned char sha1[20];
unsigned long offset;
};
struct delta_entry struct delta_entry
{ {
struct object_entry *obj; struct object_entry *obj;
unsigned char base_sha1[20]; union delta_base base;
}; };
static const char *pack_name; static const char *pack_name;
@ -134,13 +139,13 @@ static void *unpack_entry_data(unsigned long offset,
static void *unpack_raw_entry(unsigned long offset, static void *unpack_raw_entry(unsigned long offset,
enum object_type *obj_type, enum object_type *obj_type,
unsigned long *obj_size, unsigned long *obj_size,
unsigned char *delta_base, union delta_base *delta_base,
unsigned long *next_obj_offset) unsigned long *next_obj_offset)
{ {
unsigned long pack_limit = pack_size - 20; unsigned long pack_limit = pack_size - 20;
unsigned long pos = offset; unsigned long pos = offset;
unsigned char c; unsigned char c;
unsigned long size; unsigned long size, base_offset;
unsigned shift; unsigned shift;
enum object_type type; enum object_type type;
void *data; void *data;
@ -161,26 +166,43 @@ static void *unpack_raw_entry(unsigned long offset,
case OBJ_REF_DELTA: case OBJ_REF_DELTA:
if (pos + 20 >= pack_limit) if (pos + 20 >= pack_limit)
bad_object(offset, "object extends past end of pack"); bad_object(offset, "object extends past end of pack");
hashcpy(delta_base, pack_base + pos); hashcpy(delta_base->sha1, pack_base + pos);
pos += 20; pos += 20;
/* fallthru */ break;
case OBJ_OFS_DELTA:
memset(delta_base, 0, sizeof(*delta_base));
c = pack_base[pos++];
base_offset = c & 127;
while (c & 128) {
base_offset += 1;
if (!base_offset || base_offset & ~(~0UL >> 7))
bad_object(offset, "offset value overflow for delta base object");
if (pos >= pack_limit)
bad_object(offset, "object extends past end of pack");
c = pack_base[pos++];
base_offset = (base_offset << 7) + (c & 127);
}
delta_base->offset = offset - base_offset;
if (delta_base->offset >= offset)
bad_object(offset, "delta base offset is out of bound");
break;
case OBJ_COMMIT: case OBJ_COMMIT:
case OBJ_TREE: case OBJ_TREE:
case OBJ_BLOB: case OBJ_BLOB:
case OBJ_TAG: case OBJ_TAG:
data = unpack_entry_data(offset, &pos, size);
break; break;
default: default:
bad_object(offset, "bad object type %d", type); bad_object(offset, "bad object type %d", type);
} }
data = unpack_entry_data(offset, &pos, size);
*obj_type = type; *obj_type = type;
*obj_size = size; *obj_size = size;
*next_obj_offset = pos; *next_obj_offset = pos;
return data; return data;
} }
static int find_delta(const unsigned char *base_sha1) static int find_delta(const union delta_base *base)
{ {
int first = 0, last = nr_deltas; int first = 0, last = nr_deltas;
@ -189,7 +211,7 @@ static int find_delta(const unsigned char *base_sha1)
struct delta_entry *delta = &deltas[next]; struct delta_entry *delta = &deltas[next];
int cmp; int cmp;
cmp = hashcmp(base_sha1, delta->base_sha1); cmp = memcmp(base, &delta->base, sizeof(*base));
if (!cmp) if (!cmp)
return next; return next;
if (cmp < 0) { if (cmp < 0) {
@ -201,18 +223,18 @@ static int find_delta(const unsigned char *base_sha1)
return -first-1; return -first-1;
} }
static int find_deltas_based_on_sha1(const unsigned char *base_sha1, static int find_delta_childs(const union delta_base *base,
int *first_index, int *last_index) int *first_index, int *last_index)
{ {
int first = find_delta(base_sha1); int first = find_delta(base);
int last = first; int last = first;
int end = nr_deltas - 1; int end = nr_deltas - 1;
if (first < 0) if (first < 0)
return -1; return -1;
while (first > 0 && !hashcmp(deltas[first - 1].base_sha1, base_sha1)) while (first > 0 && !memcmp(&deltas[first - 1].base, base, sizeof(*base)))
--first; --first;
while (last < end && !hashcmp(deltas[last + 1].base_sha1, base_sha1)) while (last < end && !memcmp(&deltas[last + 1].base, base, sizeof(*base)))
++last; ++last;
*first_index = first; *first_index = first;
*last_index = last; *last_index = last;
@ -253,13 +275,13 @@ static void resolve_delta(struct delta_entry *delta, void *base_data,
void *result; void *result;
unsigned long result_size; unsigned long result_size;
enum object_type delta_type; enum object_type delta_type;
unsigned char base_sha1[20]; union delta_base delta_base;
unsigned long next_obj_offset; unsigned long next_obj_offset;
int j, first, last; int j, first, last;
obj->real_type = type; obj->real_type = type;
delta_data = unpack_raw_entry(obj->offset, &delta_type, delta_data = unpack_raw_entry(obj->offset, &delta_type,
&delta_size, base_sha1, &delta_size, &delta_base,
&next_obj_offset); &next_obj_offset);
result = patch_delta(base_data, base_size, delta_data, delta_size, result = patch_delta(base_data, base_size, delta_data, delta_size,
&result_size); &result_size);
@ -267,10 +289,22 @@ static void resolve_delta(struct delta_entry *delta, void *base_data,
if (!result) if (!result)
bad_object(obj->offset, "failed to apply delta"); bad_object(obj->offset, "failed to apply delta");
sha1_object(result, result_size, type, obj->sha1); sha1_object(result, result_size, type, obj->sha1);
if (!find_deltas_based_on_sha1(obj->sha1, &first, &last)) {
hashcpy(delta_base.sha1, obj->sha1);
if (!find_delta_childs(&delta_base, &first, &last)) {
for (j = first; j <= last; j++) for (j = first; j <= last; j++)
resolve_delta(&deltas[j], result, result_size, type); if (deltas[j].obj->type == OBJ_REF_DELTA)
resolve_delta(&deltas[j], result, result_size, type);
} }
memset(&delta_base, 0, sizeof(delta_base));
delta_base.offset = obj->offset;
if (!find_delta_childs(&delta_base, &first, &last)) {
for (j = first; j <= last; j++)
if (deltas[j].obj->type == OBJ_OFS_DELTA)
resolve_delta(&deltas[j], result, result_size, type);
}
free(result); free(result);
} }
@ -278,14 +312,14 @@ static int compare_delta_entry(const void *a, const void *b)
{ {
const struct delta_entry *delta_a = a; const struct delta_entry *delta_a = a;
const struct delta_entry *delta_b = b; const struct delta_entry *delta_b = b;
return hashcmp(delta_a->base_sha1, delta_b->base_sha1); return memcmp(&delta_a->base, &delta_b->base, sizeof(union delta_base));
} }
static void parse_pack_objects(void) static void parse_pack_objects(void)
{ {
int i; int i;
unsigned long offset = sizeof(struct pack_header); unsigned long offset = sizeof(struct pack_header);
unsigned char base_sha1[20]; struct delta_entry *delta = deltas;
void *data; void *data;
unsigned long data_size; unsigned long data_size;
@ -299,12 +333,12 @@ static void parse_pack_objects(void)
struct object_entry *obj = &objects[i]; struct object_entry *obj = &objects[i];
obj->offset = offset; obj->offset = offset;
data = unpack_raw_entry(offset, &obj->type, &data_size, data = unpack_raw_entry(offset, &obj->type, &data_size,
base_sha1, &offset); &delta->base, &offset);
obj->real_type = obj->type; obj->real_type = obj->type;
if (obj->type == OBJ_REF_DELTA) { if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) {
struct delta_entry *delta = &deltas[nr_deltas++]; nr_deltas++;
delta->obj = obj; delta->obj = obj;
hashcpy(delta->base_sha1, base_sha1); delta++;
} else } else
sha1_object(data, data_size, obj->type, obj->sha1); sha1_object(data, data_size, obj->type, obj->sha1);
free(data); free(data);
@ -312,7 +346,7 @@ static void parse_pack_objects(void)
if (offset != pack_size - 20) if (offset != pack_size - 20)
die("packfile '%s' has junk at the end", pack_name); die("packfile '%s' has junk at the end", pack_name);
/* Sort deltas by base SHA1 for fast searching */ /* Sort deltas by base SHA1/offset for fast searching */
qsort(deltas, nr_deltas, sizeof(struct delta_entry), qsort(deltas, nr_deltas, sizeof(struct delta_entry),
compare_delta_entry); compare_delta_entry);
@ -326,22 +360,37 @@ static void parse_pack_objects(void)
*/ */
for (i = 0; i < nr_objects; i++) { for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i]; struct object_entry *obj = &objects[i];
int j, first, last; union delta_base base;
int j, ref, ref_first, ref_last, ofs, ofs_first, ofs_last;
if (obj->type == OBJ_REF_DELTA) if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
continue; continue;
if (find_deltas_based_on_sha1(obj->sha1, &first, &last)) hashcpy(base.sha1, obj->sha1);
ref = !find_delta_childs(&base, &ref_first, &ref_last);
memset(&base, 0, sizeof(base));
base.offset = obj->offset;
ofs = !find_delta_childs(&base, &ofs_first, &ofs_last);
if (!ref && !ofs)
continue; continue;
data = unpack_raw_entry(obj->offset, &obj->type, &data_size, data = unpack_raw_entry(obj->offset, &obj->type, &data_size,
base_sha1, &offset); &base, &offset);
for (j = first; j <= last; j++) if (ref)
resolve_delta(&deltas[j], data, data_size, obj->type); for (j = ref_first; j <= ref_last; j++)
if (deltas[j].obj->type == OBJ_REF_DELTA)
resolve_delta(&deltas[j], data,
data_size, obj->type);
if (ofs)
for (j = ofs_first; j <= ofs_last; j++)
if (deltas[j].obj->type == OBJ_OFS_DELTA)
resolve_delta(&deltas[j], data,
data_size, obj->type);
free(data); free(data);
} }
/* Check for unresolved deltas */ /* Check for unresolved deltas */
for (i = 0; i < nr_deltas; i++) { for (i = 0; i < nr_deltas; i++) {
if (deltas[i].obj->real_type == OBJ_REF_DELTA) if (deltas[i].obj->real_type == OBJ_REF_DELTA ||
deltas[i].obj->real_type == OBJ_OFS_DELTA)
die("packfile '%s' has unresolved deltas", pack_name); die("packfile '%s' has unresolved deltas", pack_name);
} }
} }