{"thread":{"id":"506","subject":"[PATCH] don't load and decompress objects twice with parse_object()","startedAt":"2005-05-06T17:48:34Z","lastAt":"2005-05-06T17:48:34Z","messageCount":1,"participants":["Nicolas Pitre"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"2689","messageId":"Pine.LNX.4.62.0505061326310.14033@localhost.localdomain","threadId":"506","inReplyTo":null,"subject":"[PATCH] don't load and decompress objects twice with parse_object()","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2005-05-06T17:48:34Z","receivedAt":"2005-05-06T17:48:34Z","isPatch":true,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"It turns out that parse_object() is loading and decompressing given \nobject to free it just before calling the specific object parsing \nfunction which does mmap and decompress the same object again. This \npatch introduces the ability to parse specific objects directly from a \nmemory buffer.\n\nWithout this patch, running git-fsck-cache on the kernel repositorytake:\n\n\treal    0m13.006s\n\tuser    0m11.421s\n\tsys     0m1.218s\n\nWith this patch applied:\n\n\treal    0m8.060s\n\tuser    0m7.071s\n\tsys     0m0.710s\n\nThe performance increase is significant, and this is kind of a \nprerequisite for sane delta object support with fsck.\n\nSigned-off-by: Nicolas Pitre <nico@cam.org>\n\nIndex: git/tag.c\n===================================================================\n--- git.orig/tag.c\n+++ git/tag.c\n@@ -21,11 +21,8 @@\n         return (struct tag *) obj;\n }\n \n-int parse_tag(struct tag *item)\n+int parse_tag_buffer(struct tag *item, void *data, unsigned long size)\n {\n-        char type[20];\n-        void *data, *bufptr;\n-        unsigned long size;\n \tint typelen, taglen;\n \tunsigned char object[20];\n \tconst char *type_line, *tag_line, *sig_line;\n@@ -33,20 +30,11 @@\n         if (item->object.parsed)\n                 return 0;\n         item->object.parsed = 1;\n-        data = bufptr = read_sha1_file(item->object.sha1, type, &size);\n-        if (!data)\n-                return error(\"Could not read %s\",\n-                             sha1_to_hex(item->object.sha1));\n-        if (strcmp(type, tag_type)) {\n-\t\tfree(data);\n-                return error(\"Object %s not a tag\",\n-                             sha1_to_hex(item->object.sha1));\n-\t}\n \n \tif (size < 64)\n-\t\tgoto err;\n+\t\treturn -1;\n \tif (memcmp(\"object \", data, 7) || get_sha1_hex(data + 7, object))\n-\t\tgoto err;\n+\t\treturn -1;\n \n \titem->tagged = parse_object(object);\n \tif (item->tagged)\n@@ -54,29 +42,47 @@\n \n \ttype_line = data + 48;\n \tif (memcmp(\"\\ntype \", type_line-1, 6))\n-\t\tgoto err;\n+\t\treturn -1;\n \n \ttag_line = strchr(type_line, '\\n');\n \tif (!tag_line || memcmp(\"tag \", ++tag_line, 4))\n-\t\tgoto err;\n+\t\treturn -1;\n \n \tsig_line = strchr(tag_line, '\\n');\n \tif (!sig_line)\n-\t\tgoto err;\n+\t\treturn -1;\n \tsig_line++;\n \n \ttypelen = tag_line - type_line - strlen(\"type \\n\");\n \tif (typelen >= 20)\n-\t\tgoto err;\n+\t\treturn -1;\n \ttaglen = sig_line - tag_line - strlen(\"tag \\n\");\n \titem->tag = xmalloc(taglen + 1);\n \tmemcpy(item->tag, tag_line + 4, taglen);\n \titem->tag[taglen] = '\\0';\n \n-\tfree(data);\n \treturn 0;\n+}\n+\n+int parse_tag(struct tag *item)\n+{\n+        char type[20];\n+        void *data;\n+        unsigned long size;\n+\tint ret;\n \n-err:\n+        if (item->object.parsed)\n+                return 0;\n+        data = read_sha1_file(item->object.sha1, type, &size);\n+        if (!data)\n+                return error(\"Could not read %s\",\n+                             sha1_to_hex(item->object.sha1));\n+        if (strcmp(type, tag_type)) {\n+\t\tfree(data);\n+                return error(\"Object %s not a tag\",\n+                             sha1_to_hex(item->object.sha1));\n+\t}\n+\tret = parse_tag_buffer(item, data, size);\n \tfree(data);\n-\treturn -1;\n+\treturn ret;\n }\nIndex: git/tree.c\n===================================================================\n--- git.orig/tree.c\n+++ git/tree.c\n@@ -88,24 +88,14 @@\n \treturn (struct tree *) obj;\n }\n \n-int parse_tree(struct tree *item)\n+int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)\n {\n-\tchar type[20];\n-\tvoid *buffer, *bufptr;\n-\tunsigned long size;\n+\tvoid *bufptr = buffer;\n \tstruct tree_entry_list **list_p;\n+\n \tif (item->object.parsed)\n \t\treturn 0;\n \titem->object.parsed = 1;\n-\tbuffer = bufptr = read_sha1_file(item->object.sha1, type, &size);\n-\tif (!buffer)\n-\t\treturn error(\"Could not read %s\",\n-\t\t\t     sha1_to_hex(item->object.sha1));\n-\tif (strcmp(type, tree_type)) {\n-\t\tfree(buffer);\n-\t\treturn error(\"Object %s not a tree\",\n-\t\t\t     sha1_to_hex(item->object.sha1));\n-\t}\n \tlist_p = &item->entries;\n \twhile (size) {\n \t\tstruct object *obj;\n@@ -115,10 +105,8 @@\n \t\tchar *path = strchr(bufptr, ' ');\n \t\tunsigned int mode;\n \t\tif (size < len + 20 || !path || \n-\t\t    sscanf(bufptr, \"%o\", &mode) != 1) {\n-\t\t\tfree(buffer);\n+\t\t    sscanf(bufptr, \"%o\", &mode) != 1)\n \t\t\treturn -1;\n-\t\t}\n \n \t\tentry = xmalloc(sizeof(struct tree_entry_list));\n \t\tentry->name = strdup(path + 1);\n@@ -144,6 +132,28 @@\n \t\t*list_p = entry;\n \t\tlist_p = &entry->next;\n \t}\n-\tfree(buffer);\n \treturn 0;\n }\n+\n+int parse_tree(struct tree *item)\n+{\n+\t char type[20];\n+\t void *buffer;\n+\t unsigned long size;\n+\t int ret;\n+\n+\tif (item->object.parsed)\n+\t\treturn 0;\n+\tbuffer = read_sha1_file(item->object.sha1, type, &size);\n+\tif (!buffer)\n+\t\treturn error(\"Could not read %s\",\n+\t\t\t     sha1_to_hex(item->object.sha1));\n+\tif (strcmp(type, tree_type)) {\n+\t\tfree(buffer);\n+\t\treturn error(\"Object %s not a tree\",\n+\t\t\t     sha1_to_hex(item->object.sha1));\n+\t}\n+\tret = parse_tree_buffer(item, buffer, size);\n+\tfree(buffer);\n+\treturn ret;\n+}\nIndex: git/blob.c\n===================================================================\n--- git.orig/blob.c\n+++ git/blob.c\n@@ -22,21 +22,29 @@\n \treturn (struct blob *) obj;\n }\n \n+int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)\n+{\n+        item->object.parsed = 1;\n+\treturn 0;\n+}\n+\n int parse_blob(struct blob *item)\n {\n         char type[20];\n         void *buffer;\n         unsigned long size;\n+\tint ret;\n+\n         if (item->object.parsed)\n                 return 0;\n-        item->object.parsed = 1;\n         buffer = read_sha1_file(item->object.sha1, type, &size);\n         if (!buffer)\n                 return error(\"Could not read %s\",\n                              sha1_to_hex(item->object.sha1));\n-\tfree(buffer);\n         if (strcmp(type, blob_type))\n                 return error(\"Object %s not a blob\",\n                              sha1_to_hex(item->object.sha1));\n-\treturn 0;\n+\tret = parse_blob_buffer(item, buffer, size);\n+\tfree(buffer);\n+\treturn ret;\n }\nIndex: git/tag.h\n===================================================================\n--- git.orig/tag.h\n+++ git/tag.h\n@@ -13,6 +13,7 @@\n };\n \n extern struct tag *lookup_tag(unsigned char *sha1);\n+extern int parse_tag_buffer(struct tag *item, void *data, unsigned long size);\n extern int parse_tag(struct tag *item);\n \n #endif /* TAG_H */\nIndex: git/commit.c\n===================================================================\n--- git.orig/commit.c\n+++ git/commit.c\n@@ -41,24 +41,14 @@\n \treturn date;\n }\n \n-int parse_commit(struct commit *item)\n+int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)\n {\n-\tchar type[20];\n-\tvoid * buffer, *bufptr;\n-\tunsigned long size;\n+\tvoid *bufptr = buffer;\n \tunsigned char parent[20];\n+\n \tif (item->object.parsed)\n \t\treturn 0;\n \titem->object.parsed = 1;\n-\tbuffer = bufptr = read_sha1_file(item->object.sha1, type, &size);\n-\tif (!buffer)\n-\t\treturn error(\"Could not read %s\",\n-\t\t\t     sha1_to_hex(item->object.sha1));\n-\tif (strcmp(type, commit_type)) {\n-\t\tfree(buffer);\n-\t\treturn error(\"Object %s not a commit\",\n-\t\t\t     sha1_to_hex(item->object.sha1));\n-\t}\n \tget_sha1_hex(bufptr + 5, parent);\n \titem->tree = lookup_tree(parent);\n \tif (item->tree)\n@@ -74,10 +64,32 @@\n \t\tbufptr += 48;\n \t}\n \titem->date = parse_commit_date(bufptr);\n-\tfree(buffer);\n \treturn 0;\n }\n \n+int parse_commit(struct commit *item)\n+{\n+\tchar type[20];\n+\tvoid *buffer;\n+\tunsigned long size;\n+\tint ret;\n+\n+\tif (item->object.parsed)\n+\t\treturn 0;\n+\tbuffer = read_sha1_file(item->object.sha1, type, &size);\n+\tif (!buffer)\n+\t\treturn error(\"Could not read %s\",\n+\t\t\t     sha1_to_hex(item->object.sha1));\n+\tif (strcmp(type, commit_type)) {\n+\t\tfree(buffer);\n+\t\treturn error(\"Object %s not a commit\",\n+\t\t\t     sha1_to_hex(item->object.sha1));\n+\t}\n+\tret = parse_commit_buffer(item, buffer, size);\n+\tfree(buffer);\n+\treturn ret;\n+}\n+\n void commit_list_insert(struct commit *item, struct commit_list **list_p)\n {\n \tstruct commit_list *new_list = xmalloc(sizeof(struct commit_list));\nIndex: git/object.c\n===================================================================\n--- git.orig/object.c\n+++ git/object.c\n@@ -104,6 +104,7 @@\n \tunsigned long mapsize;\n \tvoid *map = map_sha1_file(sha1, &mapsize);\n \tif (map) {\n+\t\tstruct object *obj;\n \t\tchar type[100];\n \t\tunsigned long size;\n \t\tvoid *buffer = unpack_sha1_file(map, mapsize, type, &size);\n@@ -112,26 +113,27 @@\n \t\t\treturn NULL;\n \t\tif (check_sha1_signature(sha1, buffer, size, type) < 0)\n \t\t\tprintf(\"sha1 mismatch %s\\n\", sha1_to_hex(sha1));\n-\t\tfree(buffer);\n \t\tif (!strcmp(type, \"blob\")) {\n-\t\t\tstruct blob *ret = lookup_blob(sha1);\n-\t\t\tparse_blob(ret);\n-\t\t\treturn &ret->object;\n+\t\t\tstruct blob *blob = lookup_blob(sha1);\n+\t\t\tparse_blob_buffer(blob, buffer, size);\n+\t\t\tobj = &blob->object;\n \t\t} else if (!strcmp(type, \"tree\")) {\n-\t\t\tstruct tree *ret = lookup_tree(sha1);\n-\t\t\tparse_tree(ret);\n-\t\t\treturn &ret->object;\n+\t\t\tstruct tree *tree = lookup_tree(sha1);\n+\t\t\tparse_tree_buffer(tree, buffer, size);\n+\t\t\tobj = &tree->object;\n \t\t} else if (!strcmp(type, \"commit\")) {\n-\t\t\tstruct commit *ret = lookup_commit(sha1);\n-\t\t\tparse_commit(ret);\n-\t\t\treturn &ret->object;\n+\t\t\tstruct commit *commit = lookup_commit(sha1);\n+\t\t\tparse_commit_buffer(commit, buffer, size);\n+\t\t\tobj = &commit->object;\n \t\t} else if (!strcmp(type, \"tag\")) {\n-\t\t\tstruct tag *ret = lookup_tag(sha1);\n-\t\t\tparse_tag(ret);\n-\t\t\treturn &ret->object;\n+\t\t\tstruct tag *tag = lookup_tag(sha1);\n+\t\t\tparse_tag_buffer(tag, buffer, size);\n+\t\t\tobj = &tag->object;\n \t\t} else {\n-\t\t\treturn NULL;\n+\t\t\tobj = NULL;\n \t\t}\n+\t\tfree(buffer);\n+\t\treturn obj;\n \t}\n \treturn NULL;\n }\nIndex: git/tree.h\n===================================================================\n--- git.orig/tree.h\n+++ git/tree.h\n@@ -25,6 +25,8 @@\n \n struct tree *lookup_tree(unsigned char *sha1);\n \n+int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size);\n+\n int parse_tree(struct tree *tree);\n \n #endif /* TREE_H */\nIndex: git/blob.h\n===================================================================\n--- git.orig/blob.h\n+++ git/blob.h\n@@ -11,6 +11,8 @@\n \n struct blob *lookup_blob(unsigned char *sha1);\n \n+int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);\n+\n int parse_blob(struct blob *item);\n \n #endif /* BLOB_H */\nIndex: git/commit.h\n===================================================================\n--- git.orig/commit.h\n+++ git/commit.h\n@@ -20,6 +20,8 @@\n \n struct commit *lookup_commit(unsigned char *sha1);\n \n+int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);\n+\n int parse_commit(struct commit *item);\n \n void commit_list_insert(struct commit *item, struct commit_list **list_p);\n"}]}