[PATCH] don't load and decompress objects twice with parse_object()
It turns out that parse_object() is loading and decompressing given object to free it just before calling the specific object parsing function which does mmap and decompress the same object again. This patch introduces the ability to parse specific objects directly from a memory buffer. Without this patch, running git-fsck-cache on the kernel repositorytake: real 0m13.006s user 0m11.421s sys 0m1.218s With this patch applied: real 0m8.060s user 0m7.071s sys 0m0.710s The performance increase is significant, and this is kind of a prerequisite for sane delta object support with fsck. Signed-off-by: Nicolas Pitre <nico@cam.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:

committed by
Linus Torvalds

parent
f4f21ce367
commit
bd2c39f58f
14
blob.c
14
blob.c
@ -22,21 +22,29 @@ struct blob *lookup_blob(unsigned char *sha1)
|
|||||||
return (struct blob *) obj;
|
return (struct blob *) obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)
|
||||||
|
{
|
||||||
|
item->object.parsed = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int parse_blob(struct blob *item)
|
int parse_blob(struct blob *item)
|
||||||
{
|
{
|
||||||
char type[20];
|
char type[20];
|
||||||
void *buffer;
|
void *buffer;
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (item->object.parsed)
|
if (item->object.parsed)
|
||||||
return 0;
|
return 0;
|
||||||
item->object.parsed = 1;
|
|
||||||
buffer = read_sha1_file(item->object.sha1, type, &size);
|
buffer = read_sha1_file(item->object.sha1, type, &size);
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
return error("Could not read %s",
|
return error("Could not read %s",
|
||||||
sha1_to_hex(item->object.sha1));
|
sha1_to_hex(item->object.sha1));
|
||||||
free(buffer);
|
|
||||||
if (strcmp(type, blob_type))
|
if (strcmp(type, blob_type))
|
||||||
return error("Object %s not a blob",
|
return error("Object %s not a blob",
|
||||||
sha1_to_hex(item->object.sha1));
|
sha1_to_hex(item->object.sha1));
|
||||||
return 0;
|
ret = parse_blob_buffer(item, buffer, size);
|
||||||
|
free(buffer);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
2
blob.h
2
blob.h
@ -11,6 +11,8 @@ struct blob {
|
|||||||
|
|
||||||
struct blob *lookup_blob(unsigned char *sha1);
|
struct blob *lookup_blob(unsigned char *sha1);
|
||||||
|
|
||||||
|
int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);
|
||||||
|
|
||||||
int parse_blob(struct blob *item);
|
int parse_blob(struct blob *item);
|
||||||
|
|
||||||
#endif /* BLOB_H */
|
#endif /* BLOB_H */
|
||||||
|
40
commit.c
40
commit.c
@ -41,24 +41,14 @@ static unsigned long parse_commit_date(const char *buf)
|
|||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_commit(struct commit *item)
|
int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
|
||||||
{
|
{
|
||||||
char type[20];
|
void *bufptr = buffer;
|
||||||
void * buffer, *bufptr;
|
|
||||||
unsigned long size;
|
|
||||||
unsigned char parent[20];
|
unsigned char parent[20];
|
||||||
|
|
||||||
if (item->object.parsed)
|
if (item->object.parsed)
|
||||||
return 0;
|
return 0;
|
||||||
item->object.parsed = 1;
|
item->object.parsed = 1;
|
||||||
buffer = bufptr = read_sha1_file(item->object.sha1, type, &size);
|
|
||||||
if (!buffer)
|
|
||||||
return error("Could not read %s",
|
|
||||||
sha1_to_hex(item->object.sha1));
|
|
||||||
if (strcmp(type, commit_type)) {
|
|
||||||
free(buffer);
|
|
||||||
return error("Object %s not a commit",
|
|
||||||
sha1_to_hex(item->object.sha1));
|
|
||||||
}
|
|
||||||
get_sha1_hex(bufptr + 5, parent);
|
get_sha1_hex(bufptr + 5, parent);
|
||||||
item->tree = lookup_tree(parent);
|
item->tree = lookup_tree(parent);
|
||||||
if (item->tree)
|
if (item->tree)
|
||||||
@ -74,10 +64,32 @@ int parse_commit(struct commit *item)
|
|||||||
bufptr += 48;
|
bufptr += 48;
|
||||||
}
|
}
|
||||||
item->date = parse_commit_date(bufptr);
|
item->date = parse_commit_date(bufptr);
|
||||||
free(buffer);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int parse_commit(struct commit *item)
|
||||||
|
{
|
||||||
|
char type[20];
|
||||||
|
void *buffer;
|
||||||
|
unsigned long size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (item->object.parsed)
|
||||||
|
return 0;
|
||||||
|
buffer = read_sha1_file(item->object.sha1, type, &size);
|
||||||
|
if (!buffer)
|
||||||
|
return error("Could not read %s",
|
||||||
|
sha1_to_hex(item->object.sha1));
|
||||||
|
if (strcmp(type, commit_type)) {
|
||||||
|
free(buffer);
|
||||||
|
return error("Object %s not a commit",
|
||||||
|
sha1_to_hex(item->object.sha1));
|
||||||
|
}
|
||||||
|
ret = parse_commit_buffer(item, buffer, size);
|
||||||
|
free(buffer);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void commit_list_insert(struct commit *item, struct commit_list **list_p)
|
void commit_list_insert(struct commit *item, struct commit_list **list_p)
|
||||||
{
|
{
|
||||||
struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
|
struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
|
||||||
|
2
commit.h
2
commit.h
@ -20,6 +20,8 @@ extern const char *commit_type;
|
|||||||
|
|
||||||
struct commit *lookup_commit(unsigned char *sha1);
|
struct commit *lookup_commit(unsigned char *sha1);
|
||||||
|
|
||||||
|
int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);
|
||||||
|
|
||||||
int parse_commit(struct commit *item);
|
int parse_commit(struct commit *item);
|
||||||
|
|
||||||
void commit_list_insert(struct commit *item, struct commit_list **list_p);
|
void commit_list_insert(struct commit *item, struct commit_list **list_p);
|
||||||
|
30
object.c
30
object.c
@ -104,6 +104,7 @@ struct object *parse_object(unsigned char *sha1)
|
|||||||
unsigned long mapsize;
|
unsigned long mapsize;
|
||||||
void *map = map_sha1_file(sha1, &mapsize);
|
void *map = map_sha1_file(sha1, &mapsize);
|
||||||
if (map) {
|
if (map) {
|
||||||
|
struct object *obj;
|
||||||
char type[100];
|
char type[100];
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
void *buffer = unpack_sha1_file(map, mapsize, type, &size);
|
void *buffer = unpack_sha1_file(map, mapsize, type, &size);
|
||||||
@ -112,26 +113,27 @@ struct object *parse_object(unsigned char *sha1)
|
|||||||
return NULL;
|
return NULL;
|
||||||
if (check_sha1_signature(sha1, buffer, size, type) < 0)
|
if (check_sha1_signature(sha1, buffer, size, type) < 0)
|
||||||
printf("sha1 mismatch %s\n", sha1_to_hex(sha1));
|
printf("sha1 mismatch %s\n", sha1_to_hex(sha1));
|
||||||
free(buffer);
|
|
||||||
if (!strcmp(type, "blob")) {
|
if (!strcmp(type, "blob")) {
|
||||||
struct blob *ret = lookup_blob(sha1);
|
struct blob *blob = lookup_blob(sha1);
|
||||||
parse_blob(ret);
|
parse_blob_buffer(blob, buffer, size);
|
||||||
return &ret->object;
|
obj = &blob->object;
|
||||||
} else if (!strcmp(type, "tree")) {
|
} else if (!strcmp(type, "tree")) {
|
||||||
struct tree *ret = lookup_tree(sha1);
|
struct tree *tree = lookup_tree(sha1);
|
||||||
parse_tree(ret);
|
parse_tree_buffer(tree, buffer, size);
|
||||||
return &ret->object;
|
obj = &tree->object;
|
||||||
} else if (!strcmp(type, "commit")) {
|
} else if (!strcmp(type, "commit")) {
|
||||||
struct commit *ret = lookup_commit(sha1);
|
struct commit *commit = lookup_commit(sha1);
|
||||||
parse_commit(ret);
|
parse_commit_buffer(commit, buffer, size);
|
||||||
return &ret->object;
|
obj = &commit->object;
|
||||||
} else if (!strcmp(type, "tag")) {
|
} else if (!strcmp(type, "tag")) {
|
||||||
struct tag *ret = lookup_tag(sha1);
|
struct tag *tag = lookup_tag(sha1);
|
||||||
parse_tag(ret);
|
parse_tag_buffer(tag, buffer, size);
|
||||||
return &ret->object;
|
obj = &tag->object;
|
||||||
} else {
|
} else {
|
||||||
return NULL;
|
obj = NULL;
|
||||||
}
|
}
|
||||||
|
free(buffer);
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
88
tag.c
88
tag.c
@ -21,11 +21,8 @@ struct tag *lookup_tag(unsigned char *sha1)
|
|||||||
return (struct tag *) obj;
|
return (struct tag *) obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_tag(struct tag *item)
|
int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
|
||||||
{
|
{
|
||||||
char type[20];
|
|
||||||
void *data, *bufptr;
|
|
||||||
unsigned long size;
|
|
||||||
int typelen, taglen;
|
int typelen, taglen;
|
||||||
unsigned char object[20];
|
unsigned char object[20];
|
||||||
const char *type_line, *tag_line, *sig_line;
|
const char *type_line, *tag_line, *sig_line;
|
||||||
@ -33,7 +30,50 @@ int parse_tag(struct tag *item)
|
|||||||
if (item->object.parsed)
|
if (item->object.parsed)
|
||||||
return 0;
|
return 0;
|
||||||
item->object.parsed = 1;
|
item->object.parsed = 1;
|
||||||
data = bufptr = read_sha1_file(item->object.sha1, type, &size);
|
|
||||||
|
if (size < 64)
|
||||||
|
return -1;
|
||||||
|
if (memcmp("object ", data, 7) || get_sha1_hex(data + 7, object))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
item->tagged = parse_object(object);
|
||||||
|
if (item->tagged)
|
||||||
|
add_ref(&item->object, item->tagged);
|
||||||
|
|
||||||
|
type_line = data + 48;
|
||||||
|
if (memcmp("\ntype ", type_line-1, 6))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
tag_line = strchr(type_line, '\n');
|
||||||
|
if (!tag_line || memcmp("tag ", ++tag_line, 4))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
sig_line = strchr(tag_line, '\n');
|
||||||
|
if (!sig_line)
|
||||||
|
return -1;
|
||||||
|
sig_line++;
|
||||||
|
|
||||||
|
typelen = tag_line - type_line - strlen("type \n");
|
||||||
|
if (typelen >= 20)
|
||||||
|
return -1;
|
||||||
|
taglen = sig_line - tag_line - strlen("tag \n");
|
||||||
|
item->tag = xmalloc(taglen + 1);
|
||||||
|
memcpy(item->tag, tag_line + 4, taglen);
|
||||||
|
item->tag[taglen] = '\0';
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_tag(struct tag *item)
|
||||||
|
{
|
||||||
|
char type[20];
|
||||||
|
void *data;
|
||||||
|
unsigned long size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (item->object.parsed)
|
||||||
|
return 0;
|
||||||
|
data = read_sha1_file(item->object.sha1, type, &size);
|
||||||
if (!data)
|
if (!data)
|
||||||
return error("Could not read %s",
|
return error("Could not read %s",
|
||||||
sha1_to_hex(item->object.sha1));
|
sha1_to_hex(item->object.sha1));
|
||||||
@ -42,41 +82,7 @@ int parse_tag(struct tag *item)
|
|||||||
return error("Object %s not a tag",
|
return error("Object %s not a tag",
|
||||||
sha1_to_hex(item->object.sha1));
|
sha1_to_hex(item->object.sha1));
|
||||||
}
|
}
|
||||||
|
ret = parse_tag_buffer(item, data, size);
|
||||||
if (size < 64)
|
|
||||||
goto err;
|
|
||||||
if (memcmp("object ", data, 7) || get_sha1_hex(data + 7, object))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
item->tagged = parse_object(object);
|
|
||||||
if (item->tagged)
|
|
||||||
add_ref(&item->object, item->tagged);
|
|
||||||
|
|
||||||
type_line = data + 48;
|
|
||||||
if (memcmp("\ntype ", type_line-1, 6))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
tag_line = strchr(type_line, '\n');
|
|
||||||
if (!tag_line || memcmp("tag ", ++tag_line, 4))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
sig_line = strchr(tag_line, '\n');
|
|
||||||
if (!sig_line)
|
|
||||||
goto err;
|
|
||||||
sig_line++;
|
|
||||||
|
|
||||||
typelen = tag_line - type_line - strlen("type \n");
|
|
||||||
if (typelen >= 20)
|
|
||||||
goto err;
|
|
||||||
taglen = sig_line - tag_line - strlen("tag \n");
|
|
||||||
item->tag = xmalloc(taglen + 1);
|
|
||||||
memcpy(item->tag, tag_line + 4, taglen);
|
|
||||||
item->tag[taglen] = '\0';
|
|
||||||
|
|
||||||
free(data);
|
free(data);
|
||||||
return 0;
|
return ret;
|
||||||
|
|
||||||
err:
|
|
||||||
free(data);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
1
tag.h
1
tag.h
@ -13,6 +13,7 @@ struct tag {
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern struct tag *lookup_tag(unsigned char *sha1);
|
extern struct tag *lookup_tag(unsigned char *sha1);
|
||||||
|
extern int parse_tag_buffer(struct tag *item, void *data, unsigned long size);
|
||||||
extern int parse_tag(struct tag *item);
|
extern int parse_tag(struct tag *item);
|
||||||
|
|
||||||
#endif /* TAG_H */
|
#endif /* TAG_H */
|
||||||
|
44
tree.c
44
tree.c
@ -88,24 +88,14 @@ struct tree *lookup_tree(unsigned char *sha1)
|
|||||||
return (struct tree *) obj;
|
return (struct tree *) obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_tree(struct tree *item)
|
int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
|
||||||
{
|
{
|
||||||
char type[20];
|
void *bufptr = buffer;
|
||||||
void *buffer, *bufptr;
|
|
||||||
unsigned long size;
|
|
||||||
struct tree_entry_list **list_p;
|
struct tree_entry_list **list_p;
|
||||||
|
|
||||||
if (item->object.parsed)
|
if (item->object.parsed)
|
||||||
return 0;
|
return 0;
|
||||||
item->object.parsed = 1;
|
item->object.parsed = 1;
|
||||||
buffer = bufptr = read_sha1_file(item->object.sha1, type, &size);
|
|
||||||
if (!buffer)
|
|
||||||
return error("Could not read %s",
|
|
||||||
sha1_to_hex(item->object.sha1));
|
|
||||||
if (strcmp(type, tree_type)) {
|
|
||||||
free(buffer);
|
|
||||||
return error("Object %s not a tree",
|
|
||||||
sha1_to_hex(item->object.sha1));
|
|
||||||
}
|
|
||||||
list_p = &item->entries;
|
list_p = &item->entries;
|
||||||
while (size) {
|
while (size) {
|
||||||
struct object *obj;
|
struct object *obj;
|
||||||
@ -115,10 +105,8 @@ int parse_tree(struct tree *item)
|
|||||||
char *path = strchr(bufptr, ' ');
|
char *path = strchr(bufptr, ' ');
|
||||||
unsigned int mode;
|
unsigned int mode;
|
||||||
if (size < len + 20 || !path ||
|
if (size < len + 20 || !path ||
|
||||||
sscanf(bufptr, "%o", &mode) != 1) {
|
sscanf(bufptr, "%o", &mode) != 1)
|
||||||
free(buffer);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
entry = xmalloc(sizeof(struct tree_entry_list));
|
entry = xmalloc(sizeof(struct tree_entry_list));
|
||||||
entry->name = strdup(path + 1);
|
entry->name = strdup(path + 1);
|
||||||
@ -144,6 +132,28 @@ int parse_tree(struct tree *item)
|
|||||||
*list_p = entry;
|
*list_p = entry;
|
||||||
list_p = &entry->next;
|
list_p = &entry->next;
|
||||||
}
|
}
|
||||||
free(buffer);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int parse_tree(struct tree *item)
|
||||||
|
{
|
||||||
|
char type[20];
|
||||||
|
void *buffer;
|
||||||
|
unsigned long size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (item->object.parsed)
|
||||||
|
return 0;
|
||||||
|
buffer = read_sha1_file(item->object.sha1, type, &size);
|
||||||
|
if (!buffer)
|
||||||
|
return error("Could not read %s",
|
||||||
|
sha1_to_hex(item->object.sha1));
|
||||||
|
if (strcmp(type, tree_type)) {
|
||||||
|
free(buffer);
|
||||||
|
return error("Object %s not a tree",
|
||||||
|
sha1_to_hex(item->object.sha1));
|
||||||
|
}
|
||||||
|
ret = parse_tree_buffer(item, buffer, size);
|
||||||
|
free(buffer);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
2
tree.h
2
tree.h
@ -25,6 +25,8 @@ struct tree {
|
|||||||
|
|
||||||
struct tree *lookup_tree(unsigned char *sha1);
|
struct tree *lookup_tree(unsigned char *sha1);
|
||||||
|
|
||||||
|
int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size);
|
||||||
|
|
||||||
int parse_tree(struct tree *tree);
|
int parse_tree(struct tree *tree);
|
||||||
|
|
||||||
#endif /* TREE_H */
|
#endif /* TREE_H */
|
||||||
|
Reference in New Issue
Block a user