{"thread":{"id":"35192","subject":"[PATCH 0/19] pack bitmaps","startedAt":"2013-10-24T17:59:15Z","lastAt":"2013-10-31T12:03:03Z","messageCount":87,"participants":["Jeff King","Junio C Hamano","Duy Nguyen","Shawn Pearce","Thomas Rast","Nguyễn Thái Ngọc Duy","SZEDER Gábor","Vicent Marti","Vicent Martí"],"isPatch":true,"patchVersion":1,"patchTotal":19},"messages":[{"id":"229401","messageId":"20131024175915.GA23398@sigill.intra.peff.net","threadId":"35192","inReplyTo":null,"subject":"[PATCH 0/19] pack bitmaps","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T17:59:15Z","receivedAt":"2013-10-24T17:59:15Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This series implements JGit-style pack bitmaps to speed up fetching and\ncloning. For example, here is a simulation of the server side of a clone\nof a fully-packed kernel repo (measuring actual clones is harder,\nbecause the client does a lot of work on resolving deltas):\n\n   [before]\n   $ time git pack-objects --all --stdout </dev/null >/dev/null\n   Counting objects: 3237103, done.\n   Compressing objects: 100% (508752/508752), done.\n   Total 3237103 (delta 2699584), reused 3237103 (delta 2699584)\n\n   real    0m44.111s\n   user    0m42.396s\n   sys     0m3.544s\n\n\n   [after]\n   $ time git pack-objects --all --stdout </dev/null >/dev/null\n   Reusing existing pack: 3237103, done.\n   Total 3237103 (delta 0), reused 0 (delta 0)\n\n   real    0m1.636s\n   user    0m1.460s\n   sys     0m0.172s\n\n\nThis helps eliminate load on the server side, but it also means that we\nactually start transferring objects way faster, which means the clones\nfinish faster. If you look at current clones of torvalds/linux from\nkernel.org, it's almost two minutes before they actually start sending\nyou any data, during which time the client is twiddling its thumbs.\n\nThe bitmaps implemented here are compatible with those produced by JGit.\nWe can read JGit-produced bitmaps, and JGit can read ours. The one\nexception is the final patch, which adds an optional name-hash cache.\nIt's added in such a way that existing implementations can ignore it,\nand is marked with a flag in the header. However, JGit is very picky\nabout the \"flags\" field; it will reject any bitmap index with a flag it\ndoes not know about.\n\nThe patches are:\n\n  [01/19]: sha1write: make buffer const-correct\n  [02/19]: revindex: Export new APIs\n  [03/19]: pack-objects: Refactor the packing list\n  [04/19]: pack-objects: factor out name_hash\n  [05/19]: revision: allow setting custom limiter function\n  [06/19]: sha1_file: export `git_open_noatime`\n  [07/19]: compat: add endianness helpers\n  [08/19]: ewah: compressed bitmap implementation\n\n    Refactoring and support for the rest of the series.\n\n  [09/19]: documentation: add documentation for the bitmap format\n  [10/19]: pack-bitmap: add support for bitmap indexes\n  [11/19]: pack-objects: use bitmaps when packing objects\n  [12/19]: rev-list: add bitmap mode to speed up object lists\n\n    Bitmap reading (you can test it against JGit at this point by\n    running \"jgit debug-gc\", and then cloning or running rev-list).\n\n  [13/19]: pack-objects: implement bitmap writing\n  [14/19]: repack: stop using magic number for ARRAY_SIZE(exts)\n  [15/19]: repack: turn exts array into array-of-struct\n  [16/19]: repack: handle optional files created by pack-objects\n  [17/19]: repack: consider bitmaps when performing repacks\n\n    Bitmap writing (you can test against JGit by running\n    \"git repack -adb\", and then running \"jgit daemon\" to\n    serve the result).\n\n  [18/19]: t: add basic bitmap functionality tests\n\n    With reading and writing, we can do our own tests.\n\n  [19/19]: pack-bitmap: implement optional name_hash cache\n\n    And this is our extension.\n\nA similar series has been running on github.com for the past couple of\nmonths, though not every repository has had bitmaps turned on (but some\nvery busy ones have).  We've hopefully squeezed out all of the bugs and\ncorner cases over that time. However, I did rebase this on a more modern\nversion of \"master\"; among other conflicts, this required porting the\ngit-repack changes from shell to C. So it's entirely possible I've\nintroduced new bugs. :)\n\nThe idea and original implementation for bitmaps comes from Shawn and\nColby, of course. The hard work in this series was done by Vicent Marti,\nand he is credited as the author in most of the patches. I've added some\nwindow dressing and helped a little with debugging and review. But along\nwith Vicent, I should be able to help with answering questions for\nreview, and as time goes on, I'm familiar enough with the code to deal\nwith bugs and reviewing future changes.\n\n-Peff\n"},{"id":"229402","messageId":"20131024175949.GA24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 01/19] sha1write: make buffer const-correct","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T17:59:49Z","receivedAt":"2013-10-24T17:59:49Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We are passed a \"void *\" and write it out without ever\ntouching it; let's indicate that by using \"const\".\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n csum-file.c | 6 +++---\n csum-file.h | 2 +-\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/csum-file.c b/csum-file.c\nindex 53f5375..465971c 100644\n--- a/csum-file.c\n+++ b/csum-file.c\n@@ -11,7 +11,7 @@\n #include \"progress.h\"\n #include \"csum-file.h\"\n \n-static void flush(struct sha1file *f, void *buf, unsigned int count)\n+static void flush(struct sha1file *f, const void *buf, unsigned int count)\n {\n \tif (0 <= f->check_fd && count)  {\n \t\tunsigned char check_buffer[8192];\n@@ -86,13 +86,13 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)\n \treturn fd;\n }\n \n-int sha1write(struct sha1file *f, void *buf, unsigned int count)\n+int sha1write(struct sha1file *f, const void *buf, unsigned int count)\n {\n \twhile (count) {\n \t\tunsigned offset = f->offset;\n \t\tunsigned left = sizeof(f->buffer) - offset;\n \t\tunsigned nr = count > left ? left : count;\n-\t\tvoid *data;\n+\t\tconst void *data;\n \n \t\tif (f->do_crc)\n \t\t\tf->crc32 = crc32(f->crc32, buf, nr);\ndiff --git a/csum-file.h b/csum-file.h\nindex 3b540bd..9dedb03 100644\n--- a/csum-file.h\n+++ b/csum-file.h\n@@ -34,7 +34,7 @@ extern struct sha1file *sha1fd(int fd, const char *name);\n extern struct sha1file *sha1fd_check(const char *name);\n extern struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp);\n extern int sha1close(struct sha1file *, unsigned char *, unsigned int);\n-extern int sha1write(struct sha1file *, void *, unsigned int);\n+extern int sha1write(struct sha1file *, const void *, unsigned int);\n extern void sha1flush(struct sha1file *f);\n extern void crc32_begin(struct sha1file *);\n extern uint32_t crc32_end(struct sha1file *);\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229403","messageId":"20131024180036.GB24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 02/19] revindex: Export new APIs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:00:36Z","receivedAt":"2013-10-24T18:00:36Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nAllow users to efficiently lookup consecutive entries that are expected\nto be found on the same revindex by exporting `find_revindex_position`:\nthis function takes a pointer to revindex itself, instead of looking up\nthe proper revindex for a given packfile on each call.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n pack-revindex.c | 38 +++++++++++++++++++++++++-------------\n pack-revindex.h |  8 ++++++++\n 2 files changed, 33 insertions(+), 13 deletions(-)\n\ndiff --git a/pack-revindex.c b/pack-revindex.c\nindex b4d2b35..0bb13b1 100644\n--- a/pack-revindex.c\n+++ b/pack-revindex.c\n@@ -16,11 +16,6 @@\n  * get the object sha1 from the main index.\n  */\n \n-struct pack_revindex {\n-\tstruct packed_git *p;\n-\tstruct revindex_entry *revindex;\n-};\n-\n static struct pack_revindex *pack_revindex;\n static int pack_revindex_hashsz;\n \n@@ -201,15 +196,14 @@ static void create_pack_revindex(struct pack_revindex *rix)\n \tsort_revindex(rix->revindex, num_ent, p->pack_size);\n }\n \n-struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)\n+struct pack_revindex *revindex_for_pack(struct packed_git *p)\n {\n \tint num;\n-\tunsigned lo, hi;\n \tstruct pack_revindex *rix;\n-\tstruct revindex_entry *revindex;\n \n \tif (!pack_revindex_hashsz)\n \t\tinit_pack_revindex();\n+\n \tnum = pack_revindex_ix(p);\n \tif (num < 0)\n \t\tdie(\"internal error: pack revindex fubar\");\n@@ -217,21 +211,39 @@ struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)\n \trix = &pack_revindex[num];\n \tif (!rix->revindex)\n \t\tcreate_pack_revindex(rix);\n-\trevindex = rix->revindex;\n \n-\tlo = 0;\n-\thi = p->num_objects + 1;\n+\treturn rix;\n+}\n+\n+int find_revindex_position(struct pack_revindex *pridx, off_t ofs)\n+{\n+\tint lo = 0;\n+\tint hi = pridx->p->num_objects + 1;\n+\tstruct revindex_entry *revindex = pridx->revindex;\n+\n \tdo {\n \t\tunsigned mi = lo + (hi - lo) / 2;\n \t\tif (revindex[mi].offset == ofs) {\n-\t\t\treturn revindex + mi;\n+\t\t\treturn mi;\n \t\t} else if (ofs < revindex[mi].offset)\n \t\t\thi = mi;\n \t\telse\n \t\t\tlo = mi + 1;\n \t} while (lo < hi);\n+\n \terror(\"bad offset for revindex\");\n-\treturn NULL;\n+\treturn -1;\n+}\n+\n+struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)\n+{\n+\tstruct pack_revindex *pridx = revindex_for_pack(p);\n+\tint pos = find_revindex_position(pridx, ofs);\n+\n+\tif (pos < 0)\n+\t\treturn NULL;\n+\n+\treturn pridx->revindex + pos;\n }\n \n void discard_revindex(void)\ndiff --git a/pack-revindex.h b/pack-revindex.h\nindex 8d5027a..866ca9c 100644\n--- a/pack-revindex.h\n+++ b/pack-revindex.h\n@@ -6,6 +6,14 @@ struct revindex_entry {\n \tunsigned int nr;\n };\n \n+struct pack_revindex {\n+\tstruct packed_git *p;\n+\tstruct revindex_entry *revindex;\n+};\n+\n+struct pack_revindex *revindex_for_pack(struct packed_git *p);\n+int find_revindex_position(struct pack_revindex *pridx, off_t ofs);\n+\n struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs);\n void discard_revindex(void);\n \n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229404","messageId":"20131024180106.GC24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 03/19] pack-objects: Refactor the packing list","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:01:06Z","receivedAt":"2013-10-24T18:01:06Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThe hash table that stores the packing list for a given `pack-objects`\nrun was tightly coupled to the pack-objects code.\n\nIn this commit, we refactor the hash table and the underlying storage\narray into a `packing_data` struct. The functionality for accessing and\nadding entries to the packing list is hence accessible from other parts\nof Git besides the `pack-objects` builtin.\n\nThis refactoring is a requirement for further patches in this series\nthat will require accessing the commit packing list from outside of\n`pack-objects`.\n\nThe hash table implementation has been minimally altered: we now\nuse table sizes which are always a power of two, to ensure a uniform\nindex distribution in the array.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Makefile               |   2 +\n builtin/pack-objects.c | 175 +++++++++++--------------------------------------\n pack-objects.c         | 111 +++++++++++++++++++++++++++++++\n pack-objects.h         |  47 +++++++++++++\n 4 files changed, 200 insertions(+), 135 deletions(-)\n create mode 100644 pack-objects.c\n create mode 100644 pack-objects.h\n\ndiff --git a/Makefile b/Makefile\nindex af847f8..48ff0bd 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -694,6 +694,7 @@ LIB_H += notes-merge.h\n LIB_H += notes-utils.h\n LIB_H += notes.h\n LIB_H += object.h\n+LIB_H += pack-objects.h\n LIB_H += pack-revindex.h\n LIB_H += pack.h\n LIB_H += parse-options.h\n@@ -831,6 +832,7 @@ LIB_OBJS += notes-merge.o\n LIB_OBJS += notes-utils.o\n LIB_OBJS += object.o\n LIB_OBJS += pack-check.o\n+LIB_OBJS += pack-objects.o\n LIB_OBJS += pack-revindex.o\n LIB_OBJS += pack-write.o\n LIB_OBJS += pager.o\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 36273dd..f3f0cf9 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -14,6 +14,7 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"list-objects.h\"\n+#include \"pack-objects.h\"\n #include \"progress.h\"\n #include \"refs.h\"\n #include \"streaming.h\"\n@@ -25,42 +26,15 @@ static const char *pack_usage[] = {\n \tNULL\n };\n \n-struct object_entry {\n-\tstruct pack_idx_entry idx;\n-\tunsigned long size;\t/* uncompressed size */\n-\tstruct packed_git *in_pack; \t/* already in pack */\n-\toff_t in_pack_offset;\n-\tstruct object_entry *delta;\t/* delta base object */\n-\tstruct object_entry *delta_child; /* deltified objects who bases me */\n-\tstruct object_entry *delta_sibling; /* other deltified objects who\n-\t\t\t\t\t     * uses the same base as me\n-\t\t\t\t\t     */\n-\tvoid *delta_data;\t/* cached delta (uncompressed) */\n-\tunsigned long delta_size;\t/* delta data size (uncompressed) */\n-\tunsigned long z_delta_size;\t/* delta data size (compressed) */\n-\tenum object_type type;\n-\tenum object_type in_pack_type;\t/* could be delta */\n-\tuint32_t hash;\t\t\t/* name hint hash */\n-\tunsigned char in_pack_header_size;\n-\tunsigned preferred_base:1; /*\n-\t\t\t\t    * we do not pack this, but is available\n-\t\t\t\t    * to be used as the base object to delta\n-\t\t\t\t    * objects against.\n-\t\t\t\t    */\n-\tunsigned no_try_delta:1;\n-\tunsigned tagged:1; /* near the very tip of refs */\n-\tunsigned filled:1; /* assigned write-order */\n-};\n-\n /*\n- * Objects we are going to pack are collected in objects array (dynamically\n- * expanded).  nr_objects & nr_alloc controls this array.  They are stored\n- * in the order we see -- typically rev-list --objects order that gives us\n- * nice \"minimum seek\" order.\n+ * Objects we are going to pack are collected in the `to_pack` structure.\n+ * It contains an array (dynamically expanded) of the object data, and a map\n+ * that can resolve SHA1s to their position in the array.\n  */\n-static struct object_entry *objects;\n+static struct packing_data to_pack;\n+\n static struct pack_idx_entry **written_list;\n-static uint32_t nr_objects, nr_alloc, nr_result, nr_written;\n+static uint32_t nr_result, nr_written;\n \n static int non_empty;\n static int reuse_delta = 1, reuse_object = 1;\n@@ -90,21 +64,11 @@ static unsigned long cache_max_small_delta_size = 1000;\n static unsigned long window_memory_limit = 0;\n \n /*\n- * The object names in objects array are hashed with this hashtable,\n- * to help looking up the entry by object name.\n- * This hashtable is built after all the objects are seen.\n- */\n-static int *object_ix;\n-static int object_ix_hashsz;\n-static struct object_entry *locate_object_entry(const unsigned char *sha1);\n-\n-/*\n  * stats\n  */\n static uint32_t written, written_delta;\n static uint32_t reused, reused_delta;\n \n-\n static void *get_delta(struct object_entry *entry)\n {\n \tunsigned long size, base_size, delta_size;\n@@ -553,12 +517,12 @@ static int mark_tagged(const char *path, const unsigned char *sha1, int flag,\n \t\t       void *cb_data)\n {\n \tunsigned char peeled[20];\n-\tstruct object_entry *entry = locate_object_entry(sha1);\n+\tstruct object_entry *entry = packlist_find(&to_pack, sha1, NULL);\n \n \tif (entry)\n \t\tentry->tagged = 1;\n \tif (!peel_ref(path, peeled)) {\n-\t\tentry = locate_object_entry(peeled);\n+\t\tentry = packlist_find(&to_pack, peeled, NULL);\n \t\tif (entry)\n \t\t\tentry->tagged = 1;\n \t}\n@@ -633,9 +597,10 @@ static struct object_entry **compute_write_order(void)\n {\n \tunsigned int i, wo_end, last_untagged;\n \n-\tstruct object_entry **wo = xmalloc(nr_objects * sizeof(*wo));\n+\tstruct object_entry **wo = xmalloc(to_pack.nr_objects * sizeof(*wo));\n+\tstruct object_entry *objects = to_pack.objects;\n \n-\tfor (i = 0; i < nr_objects; i++) {\n+\tfor (i = 0; i < to_pack.nr_objects; i++) {\n \t\tobjects[i].tagged = 0;\n \t\tobjects[i].filled = 0;\n \t\tobjects[i].delta_child = NULL;\n@@ -647,7 +612,7 @@ static struct object_entry **compute_write_order(void)\n \t * Make sure delta_sibling is sorted in the original\n \t * recency order.\n \t */\n-\tfor (i = nr_objects; i > 0;) {\n+\tfor (i = to_pack.nr_objects; i > 0;) {\n \t\tstruct object_entry *e = &objects[--i];\n \t\tif (!e->delta)\n \t\t\tcontinue;\n@@ -665,7 +630,7 @@ static struct object_entry **compute_write_order(void)\n \t * Give the objects in the original recency order until\n \t * we see a tagged tip.\n \t */\n-\tfor (i = wo_end = 0; i < nr_objects; i++) {\n+\tfor (i = wo_end = 0; i < to_pack.nr_objects; i++) {\n \t\tif (objects[i].tagged)\n \t\t\tbreak;\n \t\tadd_to_write_order(wo, &wo_end, &objects[i]);\n@@ -675,7 +640,7 @@ static struct object_entry **compute_write_order(void)\n \t/*\n \t * Then fill all the tagged tips.\n \t */\n-\tfor (; i < nr_objects; i++) {\n+\tfor (; i < to_pack.nr_objects; i++) {\n \t\tif (objects[i].tagged)\n \t\t\tadd_to_write_order(wo, &wo_end, &objects[i]);\n \t}\n@@ -683,7 +648,7 @@ static struct object_entry **compute_write_order(void)\n \t/*\n \t * And then all remaining commits and tags.\n \t */\n-\tfor (i = last_untagged; i < nr_objects; i++) {\n+\tfor (i = last_untagged; i < to_pack.nr_objects; i++) {\n \t\tif (objects[i].type != OBJ_COMMIT &&\n \t\t    objects[i].type != OBJ_TAG)\n \t\t\tcontinue;\n@@ -693,7 +658,7 @@ static struct object_entry **compute_write_order(void)\n \t/*\n \t * And then all the trees.\n \t */\n-\tfor (i = last_untagged; i < nr_objects; i++) {\n+\tfor (i = last_untagged; i < to_pack.nr_objects; i++) {\n \t\tif (objects[i].type != OBJ_TREE)\n \t\t\tcontinue;\n \t\tadd_to_write_order(wo, &wo_end, &objects[i]);\n@@ -702,13 +667,13 @@ static struct object_entry **compute_write_order(void)\n \t/*\n \t * Finally all the rest in really tight order\n \t */\n-\tfor (i = last_untagged; i < nr_objects; i++) {\n+\tfor (i = last_untagged; i < to_pack.nr_objects; i++) {\n \t\tif (!objects[i].filled)\n \t\t\tadd_family_to_write_order(wo, &wo_end, &objects[i]);\n \t}\n \n-\tif (wo_end != nr_objects)\n-\t\tdie(\"ordered %u objects, expected %\"PRIu32, wo_end, nr_objects);\n+\tif (wo_end != to_pack.nr_objects)\n+\t\tdie(\"ordered %u objects, expected %\"PRIu32, wo_end, to_pack.nr_objects);\n \n \treturn wo;\n }\n@@ -724,7 +689,7 @@ static void write_pack_file(void)\n \n \tif (progress > pack_to_stdout)\n \t\tprogress_state = start_progress(\"Writing objects\", nr_result);\n-\twritten_list = xmalloc(nr_objects * sizeof(*written_list));\n+\twritten_list = xmalloc(to_pack.nr_objects * sizeof(*written_list));\n \twrite_order = compute_write_order();\n \n \tdo {\n@@ -740,7 +705,7 @@ static void write_pack_file(void)\n \t\tif (!offset)\n \t\t\tdie_errno(\"unable to write pack header\");\n \t\tnr_written = 0;\n-\t\tfor (; i < nr_objects; i++) {\n+\t\tfor (; i < to_pack.nr_objects; i++) {\n \t\t\tstruct object_entry *e = write_order[i];\n \t\t\tif (write_one(f, e, &offset) == WRITE_ONE_BREAK)\n \t\t\t\tbreak;\n@@ -803,7 +768,7 @@ static void write_pack_file(void)\n \t\t\twritten_list[j]->offset = (off_t)-1;\n \t\t}\n \t\tnr_remaining -= nr_written;\n-\t} while (nr_remaining && i < nr_objects);\n+\t} while (nr_remaining && i < to_pack.nr_objects);\n \n \tfree(written_list);\n \tfree(write_order);\n@@ -813,53 +778,6 @@ static void write_pack_file(void)\n \t\t\twritten, nr_result);\n }\n \n-static int locate_object_entry_hash(const unsigned char *sha1)\n-{\n-\tint i;\n-\tunsigned int ui;\n-\tmemcpy(&ui, sha1, sizeof(unsigned int));\n-\ti = ui % object_ix_hashsz;\n-\twhile (0 < object_ix[i]) {\n-\t\tif (!hashcmp(sha1, objects[object_ix[i] - 1].idx.sha1))\n-\t\t\treturn i;\n-\t\tif (++i == object_ix_hashsz)\n-\t\t\ti = 0;\n-\t}\n-\treturn -1 - i;\n-}\n-\n-static struct object_entry *locate_object_entry(const unsigned char *sha1)\n-{\n-\tint i;\n-\n-\tif (!object_ix_hashsz)\n-\t\treturn NULL;\n-\n-\ti = locate_object_entry_hash(sha1);\n-\tif (0 <= i)\n-\t\treturn &objects[object_ix[i]-1];\n-\treturn NULL;\n-}\n-\n-static void rehash_objects(void)\n-{\n-\tuint32_t i;\n-\tstruct object_entry *oe;\n-\n-\tobject_ix_hashsz = nr_objects * 3;\n-\tif (object_ix_hashsz < 1024)\n-\t\tobject_ix_hashsz = 1024;\n-\tobject_ix = xrealloc(object_ix, sizeof(int) * object_ix_hashsz);\n-\tmemset(object_ix, 0, sizeof(int) * object_ix_hashsz);\n-\tfor (i = 0, oe = objects; i < nr_objects; i++, oe++) {\n-\t\tint ix = locate_object_entry_hash(oe->idx.sha1);\n-\t\tif (0 <= ix)\n-\t\t\tcontinue;\n-\t\tix = -1 - ix;\n-\t\tobject_ix[ix] = i + 1;\n-\t}\n-}\n-\n static uint32_t name_hash(const char *name)\n {\n \tuint32_t c, hash = 0;\n@@ -908,13 +826,12 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,\n \tstruct object_entry *entry;\n \tstruct packed_git *p, *found_pack = NULL;\n \toff_t found_offset = 0;\n-\tint ix;\n \tuint32_t hash = name_hash(name);\n+\tuint32_t index_pos;\n \n-\tix = nr_objects ? locate_object_entry_hash(sha1) : -1;\n-\tif (ix >= 0) {\n+\tentry = packlist_find(&to_pack, sha1, &index_pos);\n+\tif (entry) {\n \t\tif (exclude) {\n-\t\t\tentry = objects + object_ix[ix] - 1;\n \t\t\tif (!entry->preferred_base)\n \t\t\t\tnr_result--;\n \t\t\tentry->preferred_base = 1;\n@@ -947,14 +864,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,\n \t\t}\n \t}\n \n-\tif (nr_objects >= nr_alloc) {\n-\t\tnr_alloc = (nr_alloc  + 1024) * 3 / 2;\n-\t\tobjects = xrealloc(objects, nr_alloc * sizeof(*entry));\n-\t}\n-\n-\tentry = objects + nr_objects++;\n-\tmemset(entry, 0, sizeof(*entry));\n-\thashcpy(entry->idx.sha1, sha1);\n+\tentry = packlist_alloc(&to_pack, sha1, index_pos);\n \tentry->hash = hash;\n \tif (type)\n \t\tentry->type = type;\n@@ -967,12 +877,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,\n \t\tentry->in_pack_offset = found_offset;\n \t}\n \n-\tif (object_ix_hashsz * 3 <= nr_objects * 4)\n-\t\trehash_objects();\n-\telse\n-\t\tobject_ix[-1 - ix] = nr_objects;\n-\n-\tdisplay_progress(progress_state, nr_objects);\n+\tdisplay_progress(progress_state, to_pack.nr_objects);\n \n \tif (name && no_try_delta(name))\n \t\tentry->no_try_delta = 1;\n@@ -1329,7 +1234,7 @@ static void check_object(struct object_entry *entry)\n \t\t\tbreak;\n \t\t}\n \n-\t\tif (base_ref && (base_entry = locate_object_entry(base_ref))) {\n+\t\tif (base_ref && (base_entry = packlist_find(&to_pack, base_ref, NULL))) {\n \t\t\t/*\n \t\t\t * If base_ref was set above that means we wish to\n \t\t\t * reuse delta data, and we even found that base\n@@ -1403,12 +1308,12 @@ static void get_object_details(void)\n \tuint32_t i;\n \tstruct object_entry **sorted_by_offset;\n \n-\tsorted_by_offset = xcalloc(nr_objects, sizeof(struct object_entry *));\n-\tfor (i = 0; i < nr_objects; i++)\n-\t\tsorted_by_offset[i] = objects + i;\n-\tqsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);\n+\tsorted_by_offset = xcalloc(to_pack.nr_objects, sizeof(struct object_entry *));\n+\tfor (i = 0; i < to_pack.nr_objects; i++)\n+\t\tsorted_by_offset[i] = to_pack.objects + i;\n+\tqsort(sorted_by_offset, to_pack.nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);\n \n-\tfor (i = 0; i < nr_objects; i++) {\n+\tfor (i = 0; i < to_pack.nr_objects; i++) {\n \t\tstruct object_entry *entry = sorted_by_offset[i];\n \t\tcheck_object(entry);\n \t\tif (big_file_threshold < entry->size)\n@@ -2034,7 +1939,7 @@ static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, vo\n \n \tif (!prefixcmp(path, \"refs/tags/\") && /* is a tag? */\n \t    !peel_ref(path, peeled)        && /* peelable? */\n-\t    locate_object_entry(peeled))      /* object packed? */\n+\t    packlist_find(&to_pack, peeled, NULL))      /* object packed? */\n \t\tadd_object_entry(sha1, OBJ_TAG, NULL, 0);\n \treturn 0;\n }\n@@ -2057,14 +1962,14 @@ static void prepare_pack(int window, int depth)\n \tif (!pack_to_stdout)\n \t\tdo_check_packed_object_crc = 1;\n \n-\tif (!nr_objects || !window || !depth)\n+\tif (!to_pack.nr_objects || !window || !depth)\n \t\treturn;\n \n-\tdelta_list = xmalloc(nr_objects * sizeof(*delta_list));\n+\tdelta_list = xmalloc(to_pack.nr_objects * sizeof(*delta_list));\n \tnr_deltas = n = 0;\n \n-\tfor (i = 0; i < nr_objects; i++) {\n-\t\tstruct object_entry *entry = objects + i;\n+\tfor (i = 0; i < to_pack.nr_objects; i++) {\n+\t\tstruct object_entry *entry = to_pack.objects + i;\n \n \t\tif (entry->delta)\n \t\t\t/* This happens if we decided to reuse existing\n@@ -2342,7 +2247,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs)\n \n \t\tfor (i = 0; i < p->num_objects; i++) {\n \t\t\tsha1 = nth_packed_object_sha1(p, i);\n-\t\t\tif (!locate_object_entry(sha1) &&\n+\t\t\tif (!packlist_find(&to_pack, sha1, NULL) &&\n \t\t\t\t!has_sha1_pack_kept_or_nonlocal(sha1))\n \t\t\t\tif (force_object_loose(sha1, p->mtime))\n \t\t\t\t\tdie(\"unable to force loose object\");\ndiff --git a/pack-objects.c b/pack-objects.c\nnew file mode 100644\nindex 0000000..d01d851\n--- /dev/null\n+++ b/pack-objects.c\n@@ -0,0 +1,111 @@\n+#include \"cache.h\"\n+#include \"object.h\"\n+#include \"pack.h\"\n+#include \"pack-objects.h\"\n+\n+static uint32_t locate_object_entry_hash(struct packing_data *pdata,\n+\t\t\t\t\t const unsigned char *sha1,\n+\t\t\t\t\t int *found)\n+{\n+\tuint32_t i, hash, mask = (pdata->index_size - 1);\n+\n+\tmemcpy(&hash, sha1, sizeof(uint32_t));\n+\ti = hash & mask;\n+\n+\twhile (pdata->index[i] > 0) {\n+\t\tuint32_t pos = pdata->index[i] - 1;\n+\n+\t\tif (!hashcmp(sha1, pdata->objects[pos].idx.sha1)) {\n+\t\t\t*found = 1;\n+\t\t\treturn i;\n+\t\t}\n+\n+\t\ti = (i + 1) & mask;\n+\t}\n+\n+\t*found = 0;\n+\treturn i;\n+}\n+\n+static inline uint32_t closest_pow2(uint32_t v)\n+{\n+\tv = v - 1;\n+\tv |= v >> 1;\n+\tv |= v >> 2;\n+\tv |= v >> 4;\n+\tv |= v >> 8;\n+\tv |= v >> 16;\n+\treturn v + 1;\n+}\n+\n+static void rehash_objects(struct packing_data *pdata)\n+{\n+\tuint32_t i;\n+\tstruct object_entry *entry;\n+\n+\tpdata->index_size = closest_pow2(pdata->nr_objects * 3);\n+\tif (pdata->index_size < 1024)\n+\t\tpdata->index_size = 1024;\n+\n+\tpdata->index = xrealloc(pdata->index, sizeof(uint32_t) * pdata->index_size);\n+\tmemset(pdata->index, 0, sizeof(int) * pdata->index_size);\n+\n+\tentry = pdata->objects;\n+\n+\tfor (i = 0; i < pdata->nr_objects; i++) {\n+\t\tint found;\n+\t\tuint32_t ix = locate_object_entry_hash(pdata, entry->idx.sha1, &found);\n+\n+\t\tif (found)\n+\t\t\tdie(\"BUG: Duplicate object in hash\");\n+\n+\t\tpdata->index[ix] = i + 1;\n+\t\tentry++;\n+\t}\n+}\n+\n+struct object_entry *packlist_find(struct packing_data *pdata,\n+\t\t\t\t   const unsigned char *sha1,\n+\t\t\t\t   uint32_t *index_pos)\n+{\n+\tuint32_t i;\n+\tint found;\n+\n+\tif (!pdata->index_size)\n+\t\treturn NULL;\n+\n+\ti = locate_object_entry_hash(pdata, sha1, &found);\n+\n+\tif (index_pos)\n+\t\t*index_pos = i;\n+\n+\tif (!found)\n+\t\treturn NULL;\n+\n+\treturn &pdata->objects[pdata->index[i] - 1];\n+}\n+\n+struct object_entry *packlist_alloc(struct packing_data *pdata,\n+\t\t\t\t    const unsigned char *sha1,\n+\t\t\t\t    uint32_t index_pos)\n+{\n+\tstruct object_entry *new_entry;\n+\n+\tif (pdata->nr_objects >= pdata->nr_alloc) {\n+\t\tpdata->nr_alloc = (pdata->nr_alloc  + 1024) * 3 / 2;\n+\t\tpdata->objects = xrealloc(pdata->objects,\n+\t\t\t\t\t  pdata->nr_alloc * sizeof(*new_entry));\n+\t}\n+\n+\tnew_entry = pdata->objects + pdata->nr_objects++;\n+\n+\tmemset(new_entry, 0, sizeof(*new_entry));\n+\thashcpy(new_entry->idx.sha1, sha1);\n+\n+\tif (pdata->index_size * 3 <= pdata->nr_objects * 4)\n+\t\trehash_objects(pdata);\n+\telse\n+\t\tpdata->index[index_pos] = pdata->nr_objects;\n+\n+\treturn new_entry;\n+}\ndiff --git a/pack-objects.h b/pack-objects.h\nnew file mode 100644\nindex 0000000..f528215\n--- /dev/null\n+++ b/pack-objects.h\n@@ -0,0 +1,47 @@\n+#ifndef PACK_OBJECTS_H\n+#define PACK_OBJECTS_H\n+\n+struct object_entry {\n+\tstruct pack_idx_entry idx;\n+\tunsigned long size;\t/* uncompressed size */\n+\tstruct packed_git *in_pack;\t/* already in pack */\n+\toff_t in_pack_offset;\n+\tstruct object_entry *delta;\t/* delta base object */\n+\tstruct object_entry *delta_child; /* deltified objects who bases me */\n+\tstruct object_entry *delta_sibling; /* other deltified objects who\n+\t\t\t\t\t     * uses the same base as me\n+\t\t\t\t\t     */\n+\tvoid *delta_data;\t/* cached delta (uncompressed) */\n+\tunsigned long delta_size;\t/* delta data size (uncompressed) */\n+\tunsigned long z_delta_size;\t/* delta data size (compressed) */\n+\tenum object_type type;\n+\tenum object_type in_pack_type;\t/* could be delta */\n+\tuint32_t hash;\t\t\t/* name hint hash */\n+\tunsigned char in_pack_header_size;\n+\tunsigned preferred_base:1; /*\n+\t\t\t\t    * we do not pack this, but is available\n+\t\t\t\t    * to be used as the base object to delta\n+\t\t\t\t    * objects against.\n+\t\t\t\t    */\n+\tunsigned no_try_delta:1;\n+\tunsigned tagged:1; /* near the very tip of refs */\n+\tunsigned filled:1; /* assigned write-order */\n+};\n+\n+struct packing_data {\n+\tstruct object_entry *objects;\n+\tuint32_t nr_objects, nr_alloc;\n+\n+\tint32_t *index;\n+\tuint32_t index_size;\n+};\n+\n+struct object_entry *packlist_alloc(struct packing_data *pdata,\n+\t\t\t\t    const unsigned char *sha1,\n+\t\t\t\t    uint32_t index_pos);\n+\n+struct object_entry *packlist_find(struct packing_data *pdata,\n+\t\t\t\t   const unsigned char *sha1,\n+\t\t\t\t   uint32_t *index_pos);\n+\n+#endif\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229405","messageId":"20131024180129.GD24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 04/19] pack-objects: factor out name_hash","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:01:29Z","receivedAt":"2013-10-24T18:01:29Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nAs the pack-objects system grows beyond the single\npack-objects.c file, more parts (like the soon-to-exist\nbitmap code) will need to compute hashes for matching\ndeltas. Factor out name_hash to make it available to other\nfiles.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/pack-objects.c | 24 ++----------------------\n pack-objects.h         | 20 ++++++++++++++++++++\n 2 files changed, 22 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex f3f0cf9..faf746b 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -778,26 +778,6 @@ static void write_pack_file(void)\n \t\t\twritten, nr_result);\n }\n \n-static uint32_t name_hash(const char *name)\n-{\n-\tuint32_t c, hash = 0;\n-\n-\tif (!name)\n-\t\treturn 0;\n-\n-\t/*\n-\t * This effectively just creates a sortable number from the\n-\t * last sixteen non-whitespace characters. Last characters\n-\t * count \"most\", so things that end in \".c\" sort together.\n-\t */\n-\twhile ((c = *name++) != 0) {\n-\t\tif (isspace(c))\n-\t\t\tcontinue;\n-\t\thash = (hash >> 2) + (c << 24);\n-\t}\n-\treturn hash;\n-}\n-\n static void setup_delta_attr_check(struct git_attr_check *check)\n {\n \tstatic struct git_attr *attr_delta;\n@@ -826,7 +806,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,\n \tstruct object_entry *entry;\n \tstruct packed_git *p, *found_pack = NULL;\n \toff_t found_offset = 0;\n-\tuint32_t hash = name_hash(name);\n+\tuint32_t hash = pack_name_hash(name);\n \tuint32_t index_pos;\n \n \tentry = packlist_find(&to_pack, sha1, &index_pos);\n@@ -1082,7 +1062,7 @@ static void add_preferred_base_object(const char *name)\n {\n \tstruct pbase_tree *it;\n \tint cmplen;\n-\tunsigned hash = name_hash(name);\n+\tunsigned hash = pack_name_hash(name);\n \n \tif (!num_preferred_base || check_pbase_path(hash))\n \t\treturn;\ndiff --git a/pack-objects.h b/pack-objects.h\nindex f528215..90ad0a8 100644\n--- a/pack-objects.h\n+++ b/pack-objects.h\n@@ -44,4 +44,24 @@ struct object_entry *packlist_find(struct packing_data *pdata,\n \t\t\t\t   const unsigned char *sha1,\n \t\t\t\t   uint32_t *index_pos);\n \n+static inline uint32_t pack_name_hash(const char *name)\n+{\n+\tuint32_t c, hash = 0;\n+\n+\tif (!name)\n+\t\treturn 0;\n+\n+\t/*\n+\t * This effectively just creates a sortable number from the\n+\t * last sixteen non-whitespace characters. Last characters\n+\t * count \"most\", so things that end in \".c\" sort together.\n+\t */\n+\twhile ((c = *name++) != 0) {\n+\t\tif (isspace(c))\n+\t\t\tcontinue;\n+\t\thash = (hash >> 2) + (c << 24);\n+\t}\n+\treturn hash;\n+}\n+\n #endif\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229406","messageId":"20131024180141.GE24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 05/19] revision: allow setting custom limiter function","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:01:41Z","receivedAt":"2013-10-24T18:01:41Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThis commit enables users of `struct rev_info` to peform custom limiting\nduring a revision walk (i.e. `get_revision`).\n\nIf the field `include_check` has been set to a callback, this callback\nwill be issued once for each commit before it is added to the \"pending\"\nlist of the revwalk. If the include check returns 0, the commit will be\nmarked as added but won't be pushed to the pending list, effectively\nlimiting the walk.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n revision.c | 4 ++++\n revision.h | 2 ++\n 2 files changed, 6 insertions(+)\n\ndiff --git a/revision.c b/revision.c\nindex 0173e01..cddd605 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -779,6 +779,10 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,\n \t\treturn 0;\n \tcommit->object.flags |= ADDED;\n \n+\tif (revs->include_check &&\n+\t    !revs->include_check(commit, revs->include_check_data))\n+\t\treturn 0;\n+\n \t/*\n \t * If the commit is uninteresting, don't try to\n \t * prune parents - we want the maximal uninteresting\ndiff --git a/revision.h b/revision.h\nindex e7f1d21..9957f3c 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -168,6 +168,8 @@ struct rev_info {\n \tunsigned long min_age;\n \tint min_parents;\n \tint max_parents;\n+\tint (*include_check)(struct commit *, void *);\n+\tvoid *include_check_data;\n \n \t/* diff info for patches and for paths limiting */\n \tstruct diff_options diffopt;\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229407","messageId":"20131024180147.GF24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 06/19] sha1_file: export `git_open_noatime`","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:01:47Z","receivedAt":"2013-10-24T18:01:47Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThe `git_open_noatime` helper can be of general interest for other\nconsumers of git's different on-disk formats.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n cache.h     | 1 +\n sha1_file.c | 4 +---\n 2 files changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 5e3fc72..f2e5aa7 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -780,6 +780,7 @@ extern int hash_sha1_file(const void *buf, unsigned long len, const char *type,\n extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);\n extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);\n extern int force_object_loose(const unsigned char *sha1, time_t mtime);\n+extern int git_open_noatime(const char *name);\n extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);\n extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);\n extern int parse_sha1_header(const char *hdr, unsigned long *sizep);\ndiff --git a/sha1_file.c b/sha1_file.c\nindex f80bbe4..4714bd8 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -239,8 +239,6 @@ char *sha1_pack_index_name(const unsigned char *sha1)\n struct alternate_object_database *alt_odb_list;\n static struct alternate_object_database **alt_odb_tail;\n \n-static int git_open_noatime(const char *name);\n-\n /*\n  * Prepare alternate object database registry.\n  *\n@@ -1357,7 +1355,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map,\n \treturn hashcmp(sha1, real_sha1) ? -1 : 0;\n }\n \n-static int git_open_noatime(const char *name)\n+int git_open_noatime(const char *name)\n {\n \tstatic int sha1_file_open_flag = O_NOATIME;\n \n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229408","messageId":"20131024180157.GG24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 07/19] compat: add endianness helpers","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:01:57Z","receivedAt":"2013-10-24T18:01:57Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThe POSIX standard doesn't currently define a `nothll`/`htonll`\nfunction pair to perform network-to-host and host-to-network\nswaps of 64-bit data. These 64-bit swaps are necessary for the on-disk\nstorage of EWAH bitmaps if they are not in native byte order.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n compat/bswap.h | 35 +++++++++++++++++++++++++++++++++++\n 1 file changed, 35 insertions(+)\n\ndiff --git a/compat/bswap.h b/compat/bswap.h\nindex 5061214..ea1a9ed 100644\n--- a/compat/bswap.h\n+++ b/compat/bswap.h\n@@ -47,4 +47,39 @@ static inline uint32_t git_bswap32(uint32_t x)\n #define ntohl(x) bswap32(x)\n #define htonl(x) bswap32(x)\n \n+#ifndef __BYTE_ORDER\n+#\tif defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)\n+#\t\tdefine __BYTE_ORDER BYTE_ORDER\n+#\t\tdefine __LITTLE_ENDIAN LITTLE_ENDIAN\n+#\t\tdefine __BIG_ENDIAN BIG_ENDIAN\n+#\telse\n+#\t\terror \"Cannot determine endianness\"\n+#\tendif\n+#endif\n+\n+#if __BYTE_ORDER == __BIG_ENDIAN\n+# define ntohll(n) (n)\n+# define htonll(n) (n)\n+#elif __BYTE_ORDER == __LITTLE_ENDIAN\n+#\tif defined(__GNUC__) && defined(__GLIBC__)\n+#\t\tinclude <byteswap.h>\n+#\telse /* GNUC & GLIBC */\n+static inline uint64_t bswap_64(uint64_t val)\n+{\n+\treturn ((val & (uint64_t)0x00000000000000ffULL) << 56)\n+\t\t| ((val & (uint64_t)0x000000000000ff00ULL) << 40)\n+\t\t| ((val & (uint64_t)0x0000000000ff0000ULL) << 24)\n+\t\t| ((val & (uint64_t)0x00000000ff000000ULL) <<  8)\n+\t\t| ((val & (uint64_t)0x000000ff00000000ULL) >>  8)\n+\t\t| ((val & (uint64_t)0x0000ff0000000000ULL) >> 24)\n+\t\t| ((val & (uint64_t)0x00ff000000000000ULL) >> 40)\n+\t\t| ((val & (uint64_t)0xff00000000000000ULL) >> 56);\n+}\n+#\tendif /* GNUC & GLIBC */\n+#\tdefine ntohll(n) bswap_64(n)\n+#\tdefine htonll(n) bswap_64(n)\n+#else /* __BYTE_ORDER */\n+#\terror \"Can't define htonll or ntohll!\"\n+#endif\n+\n #endif\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229409","messageId":"20131024180217.GH24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 08/19] ewah: compressed bitmap implementation","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:02:17Z","receivedAt":"2013-10-24T18:02:17Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nEWAH is a word-aligned compressed variant of a bitset (i.e. a data\nstructure that acts as a 0-indexed boolean array for many entries).\n\nIt uses a 64-bit run-length encoding (RLE) compression scheme,\ntrading some compression for better processing speed.\n\nThe goal of this word-aligned implementation is not to achieve\nthe best compression, but rather to improve query processing time.\nAs it stands right now, this EWAH implementation will always be more\nefficient storage-wise than its uncompressed alternative.\n\nEWAH arrays will be used as the on-disk format to store reachability\nbitmaps for all objects in a repository while keeping reasonable sizes,\nin the same way that JGit does.\n\nThis EWAH implementation is a mostly straightforward port of the\noriginal `javaewah` library that JGit currently uses. The library is\nself-contained and has been embedded whole (4 files) inside the `ewah`\nfolder to ease redistribution.\n\nThe library is re-licensed under the GPLv2 with the permission of Daniel\nLemire, the original author. The source code for the C version can\nbe found on GitHub:\n\n\thttps://github.com/vmg/libewok\n\nThe original Java implementation can also be found on GitHub:\n\n\thttps://github.com/lemire/javaewah\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Makefile           |   6 +\n ewah/bitmap.c      | 226 +++++++++++++++++\n ewah/ewah_bitmap.c | 701 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n ewah/ewah_io.c     | 192 +++++++++++++++\n ewah/ewah_rlw.c    | 120 +++++++++\n ewah/ewok.h        | 223 +++++++++++++++++\n ewah/ewok_rlw.h    | 114 +++++++++\n 7 files changed, 1582 insertions(+)\n create mode 100644 ewah/bitmap.c\n create mode 100644 ewah/ewah_bitmap.c\n create mode 100644 ewah/ewah_io.c\n create mode 100644 ewah/ewah_rlw.c\n create mode 100644 ewah/ewok.h\n create mode 100644 ewah/ewok_rlw.h\n\ndiff --git a/Makefile b/Makefile\nindex 48ff0bd..eacb940 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -667,6 +667,8 @@ LIB_H += diff.h\n LIB_H += diffcore.h\n LIB_H += dir.h\n LIB_H += exec_cmd.h\n+LIB_H += ewah/ewok.h\n+LIB_H += ewah/ewok_rlw.h\n LIB_H += fetch-pack.h\n LIB_H += fmt-merge-msg.h\n LIB_H += fsck.h\n@@ -800,6 +802,10 @@ LIB_OBJS += dir.o\n LIB_OBJS += editor.o\n LIB_OBJS += entry.o\n LIB_OBJS += environment.o\n+LIB_OBJS += ewah/bitmap.o\n+LIB_OBJS += ewah/ewah_bitmap.o\n+LIB_OBJS += ewah/ewah_io.o\n+LIB_OBJS += ewah/ewah_rlw.o\n LIB_OBJS += exec_cmd.o\n LIB_OBJS += fetch-pack.o\n LIB_OBJS += fsck.o\ndiff --git a/ewah/bitmap.c b/ewah/bitmap.c\nnew file mode 100644\nindex 0000000..d248577\n--- /dev/null\n+++ b/ewah/bitmap.c\n@@ -0,0 +1,226 @@\n+/**\n+ * Copyright 2013, GitHub, Inc\n+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n+ *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; either version 2\n+ * of the License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+ */\n+#include <assert.h>\n+#include <stdlib.h>\n+#include <string.h>\n+\n+#include \"ewok.h\"\n+\n+#define MASK(x) ((eword_t)1 << (x % BITS_IN_WORD))\n+#define BLOCK(x) (x / BITS_IN_WORD)\n+\n+struct bitmap *bitmap_new(void)\n+{\n+\tstruct bitmap *bitmap = ewah_malloc(sizeof(struct bitmap));\n+\tbitmap->words = ewah_calloc(32, sizeof(eword_t));\n+\tbitmap->word_alloc = 32;\n+\treturn bitmap;\n+}\n+\n+void bitmap_set(struct bitmap *self, size_t pos)\n+{\n+\tsize_t block = BLOCK(pos);\n+\n+\tif (block >= self->word_alloc) {\n+\t\tsize_t old_size = self->word_alloc;\n+\t\tself->word_alloc = block * 2;\n+\t\tself->words = ewah_realloc(self->words, self->word_alloc * sizeof(eword_t));\n+\n+\t\tmemset(self->words + old_size, 0x0,\n+\t\t\t(self->word_alloc - old_size) * sizeof(eword_t));\n+\t}\n+\n+\tself->words[block] |= MASK(pos);\n+}\n+\n+void bitmap_clear(struct bitmap *self, size_t pos)\n+{\n+\tsize_t block = BLOCK(pos);\n+\n+\tif (block < self->word_alloc)\n+\t\tself->words[block] &= ~MASK(pos);\n+}\n+\n+int bitmap_get(struct bitmap *self, size_t pos)\n+{\n+\tsize_t block = BLOCK(pos);\n+\treturn block < self->word_alloc && (self->words[block] & MASK(pos)) != 0;\n+}\n+\n+struct ewah_bitmap *bitmap_to_ewah(struct bitmap *bitmap)\n+{\n+\tstruct ewah_bitmap *ewah = ewah_new();\n+\tsize_t i, running_empty_words = 0;\n+\teword_t last_word = 0;\n+\n+\tfor (i = 0; i < bitmap->word_alloc; ++i) {\n+\t\tif (bitmap->words[i] == 0) {\n+\t\t\trunning_empty_words++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (last_word != 0) {\n+\t\t\tewah_add(ewah, last_word);\n+\t\t}\n+\n+\t\tif (running_empty_words > 0) {\n+\t\t\tewah_add_empty_words(ewah, 0, running_empty_words);\n+\t\t\trunning_empty_words = 0;\n+\t\t}\n+\n+\t\tlast_word = bitmap->words[i];\n+\t}\n+\n+\tewah_add(ewah, last_word);\n+\treturn ewah;\n+}\n+\n+struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah)\n+{\n+\tstruct bitmap *bitmap = bitmap_new();\n+\tstruct ewah_iterator it;\n+\teword_t blowup;\n+\tsize_t i = 0;\n+\n+\tewah_iterator_init(&it, ewah);\n+\n+\twhile (ewah_iterator_next(&blowup, &it)) {\n+\t\tif (i >= bitmap->word_alloc) {\n+\t\t\tbitmap->word_alloc *= 1.5;\n+\t\t\tbitmap->words = ewah_realloc(\n+\t\t\t\tbitmap->words, bitmap->word_alloc * sizeof(eword_t));\n+\t\t}\n+\n+\t\tbitmap->words[i++] = blowup;\n+\t}\n+\n+\tbitmap->word_alloc = i;\n+\treturn bitmap;\n+}\n+\n+void bitmap_and_not(struct bitmap *self, struct bitmap *other)\n+{\n+\tconst size_t count = (self->word_alloc < other->word_alloc) ?\n+\t\tself->word_alloc : other->word_alloc;\n+\n+\tsize_t i;\n+\n+\tfor (i = 0; i < count; ++i) {\n+\t\tself->words[i] &= ~other->words[i];\n+\t}\n+}\n+\n+void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)\n+{\n+\tsize_t original_size = self->word_alloc;\n+\tsize_t other_final = (other->bit_size / BITS_IN_WORD) + 1;\n+\tsize_t i = 0;\n+\tstruct ewah_iterator it;\n+\teword_t word;\n+\n+\tif (self->word_alloc < other_final) {\n+\t\tself->word_alloc = other_final;\n+\t\tself->words = ewah_realloc(self->words, self->word_alloc * sizeof(eword_t));\n+\t\tmemset(self->words + original_size, 0x0,\n+\t\t\t(self->word_alloc - original_size) * sizeof(eword_t));\n+\t}\n+\n+\tewah_iterator_init(&it, other);\n+\n+\twhile (ewah_iterator_next(&word, &it)) {\n+\t\tself->words[i++] |= word;\n+\t}\n+}\n+\n+void bitmap_each_bit(struct bitmap *self, ewah_callback callback, void *data)\n+{\n+\tsize_t pos = 0, i;\n+\n+\tfor (i = 0; i < self->word_alloc; ++i) {\n+\t\teword_t word = self->words[i];\n+\t\tuint32_t offset;\n+\n+\t\tif (word == (eword_t)~0) {\n+\t\t\tfor (offset = 0; offset < BITS_IN_WORD; ++offset) {\n+\t\t\t\tcallback(pos++, data);\n+\t\t\t}\n+\t\t} else {\n+\t\t\tfor (offset = 0; offset < BITS_IN_WORD; ++offset) {\n+\t\t\t\tif ((word >> offset) == 0)\n+\t\t\t\t\tbreak;\n+\n+\t\t\t\toffset += ewah_bit_ctz64(word >> offset);\n+\t\t\t\tcallback(pos + offset, data);\n+\t\t\t}\n+\t\t\tpos += BITS_IN_WORD;\n+\t\t}\n+\t}\n+}\n+\n+size_t bitmap_popcount(struct bitmap *self)\n+{\n+\tsize_t i, count = 0;\n+\n+\tfor (i = 0; i < self->word_alloc; ++i) {\n+\t\tcount += ewah_bit_popcount64(self->words[i]);\n+\t}\n+\n+\treturn count;\n+}\n+\n+int bitmap_equals(struct bitmap *self, struct bitmap *other)\n+{\n+\tstruct bitmap *big, *small;\n+\tsize_t i;\n+\n+\tif (self->word_alloc < other->word_alloc) {\n+\t\tsmall = self;\n+\t\tbig = other;\n+\t} else {\n+\t\tsmall = other;\n+\t\tbig = self;\n+\t}\n+\n+\tfor (i = 0; i < small->word_alloc; ++i) {\n+\t\tif (small->words[i] != big->words[i])\n+\t\t\treturn 0;\n+\t}\n+\n+\tfor (; i < big->word_alloc; ++i) {\n+\t\tif (big->words[i] != 0)\n+\t\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+void bitmap_reset(struct bitmap *bitmap)\n+{\n+\tmemset(bitmap->words, 0x0, bitmap->word_alloc * sizeof(eword_t));\n+}\n+\n+void bitmap_free(struct bitmap *bitmap)\n+{\n+\tif (bitmap == NULL)\n+\t\treturn;\n+\n+\tfree(bitmap->words);\n+\tfree(bitmap);\n+}\ndiff --git a/ewah/ewah_bitmap.c b/ewah/ewah_bitmap.c\nnew file mode 100644\nindex 0000000..b74a1eb\n--- /dev/null\n+++ b/ewah/ewah_bitmap.c\n@@ -0,0 +1,701 @@\n+/**\n+ * Copyright 2013, GitHub, Inc\n+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n+ *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; either version 2\n+ * of the License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+ */\n+#include <assert.h>\n+#include <stdlib.h>\n+#include <unistd.h>\n+#include <string.h>\n+#include <stdio.h>\n+\n+#include \"ewok.h\"\n+#include \"ewok_rlw.h\"\n+\n+static inline size_t min_size(size_t a, size_t b)\n+{\n+\treturn a < b ? a : b;\n+}\n+\n+static inline size_t max_size(size_t a, size_t b)\n+{\n+\treturn a > b ? a : b;\n+}\n+\n+static inline void buffer_grow(struct ewah_bitmap *self, size_t new_size)\n+{\n+\tsize_t rlw_offset = (uint8_t *)self->rlw - (uint8_t *)self->buffer;\n+\n+\tif (self->alloc_size >= new_size)\n+\t\treturn;\n+\n+\tself->alloc_size = new_size;\n+\tself->buffer = ewah_realloc(self->buffer, self->alloc_size * sizeof(eword_t));\n+\tself->rlw = self->buffer + (rlw_offset / sizeof(size_t));\n+}\n+\n+static inline void buffer_push(struct ewah_bitmap *self, eword_t value)\n+{\n+\tif (self->buffer_size + 1 >= self->alloc_size) {\n+\t\tbuffer_grow(self, self->buffer_size * 3 / 2);\n+\t}\n+\n+\tself->buffer[self->buffer_size++] = value;\n+}\n+\n+static void buffer_push_rlw(struct ewah_bitmap *self, eword_t value)\n+{\n+\tbuffer_push(self, value);\n+\tself->rlw = self->buffer + self->buffer_size - 1;\n+}\n+\n+static size_t add_empty_words(struct ewah_bitmap *self, int v, size_t number)\n+{\n+\tsize_t added = 0;\n+\n+\tif (rlw_get_run_bit(self->rlw) != v && rlw_size(self->rlw) == 0) {\n+\t\trlw_set_run_bit(self->rlw, v);\n+\t}\n+\telse if (rlw_get_literal_words(self->rlw) != 0 || rlw_get_run_bit(self->rlw) != v) {\n+\t\tbuffer_push_rlw(self, 0);\n+\t\tif (v) rlw_set_run_bit(self->rlw, v);\n+\t\tadded++;\n+\t}\n+\n+\teword_t runlen = rlw_get_running_len(self->rlw);\n+\teword_t can_add = min_size(number, RLW_LARGEST_RUNNING_COUNT - runlen);\n+\n+\trlw_set_running_len(self->rlw, runlen + can_add);\n+\tnumber -= can_add;\n+\n+\twhile (number >= RLW_LARGEST_RUNNING_COUNT) {\n+\t\tbuffer_push_rlw(self, 0);\n+\t\tadded++;\n+\n+\t\tif (v) rlw_set_run_bit(self->rlw, v);\n+\t\trlw_set_running_len(self->rlw, RLW_LARGEST_RUNNING_COUNT);\n+\n+\t\tnumber -= RLW_LARGEST_RUNNING_COUNT;\n+\t}\n+\n+\tif (number > 0) {\n+\t\tbuffer_push_rlw(self, 0);\n+\t\tadded++;\n+\n+\t\tif (v) rlw_set_run_bit(self->rlw, v);\n+\t\trlw_set_running_len(self->rlw, number);\n+\t}\n+\n+\treturn added;\n+}\n+\n+size_t ewah_add_empty_words(struct ewah_bitmap *self, int v, size_t number)\n+{\n+\tif (number == 0)\n+\t\treturn 0;\n+\n+\tself->bit_size += number * BITS_IN_WORD;\n+\treturn add_empty_words(self, v, number);\n+}\n+\n+static size_t add_literal(struct ewah_bitmap *self, eword_t new_data)\n+{\n+\teword_t current_num = rlw_get_literal_words(self->rlw);\n+\n+\tif (current_num >= RLW_LARGEST_LITERAL_COUNT) {\n+\t\tbuffer_push_rlw(self, 0);\n+\n+\t\trlw_set_literal_words(self->rlw, 1);\n+\t\tbuffer_push(self, new_data);\n+\t\treturn 2;\n+\t}\n+\n+\trlw_set_literal_words(self->rlw, current_num + 1);\n+\n+\t/* sanity check */\n+\tassert(rlw_get_literal_words(self->rlw) == current_num + 1);\n+\n+\tbuffer_push(self, new_data);\n+\treturn 1;\n+}\n+\n+void ewah_add_dirty_words(\n+\tstruct ewah_bitmap *self, const eword_t *buffer, size_t number, int negate)\n+{\n+\tsize_t literals, can_add;\n+\n+\twhile (1) {\n+\t\tliterals = rlw_get_literal_words(self->rlw);\n+\t\tcan_add = min_size(number, RLW_LARGEST_LITERAL_COUNT - literals);\n+\n+\t\trlw_set_literal_words(self->rlw, literals + can_add);\n+\n+\t\tif (self->buffer_size + can_add >= self->alloc_size) {\n+\t\t\tbuffer_grow(self, (self->buffer_size + can_add) * 3 / 2);\n+\t\t}\n+\n+\t\tif (negate) {\n+\t\t\tsize_t i;\n+\t\t\tfor (i = 0; i < can_add; ++i)\n+\t\t\t\tself->buffer[self->buffer_size++] = ~buffer[i];\n+\t\t} else {\n+\t\t\tmemcpy(self->buffer + self->buffer_size, buffer, can_add * sizeof(eword_t));\n+\t\t\tself->buffer_size += can_add;\n+\t\t}\n+\n+\t\tself->bit_size += can_add * BITS_IN_WORD;\n+\n+\t\tif (number - can_add == 0)\n+\t\t\tbreak;\n+\n+\t\tbuffer_push_rlw(self, 0);\n+\t\tbuffer += can_add;\n+\t\tnumber -= can_add;\n+\t}\n+}\n+\n+static size_t add_empty_word(struct ewah_bitmap *self, int v)\n+{\n+\tint no_literal = (rlw_get_literal_words(self->rlw) == 0);\n+\teword_t run_len = rlw_get_running_len(self->rlw);\n+\n+\tif (no_literal && run_len == 0) {\n+\t\trlw_set_run_bit(self->rlw, v);\n+\t\tassert(rlw_get_run_bit(self->rlw) == v);\n+\t}\n+\n+\tif (no_literal && rlw_get_run_bit(self->rlw) == v &&\n+\t\trun_len < RLW_LARGEST_RUNNING_COUNT) {\n+\t\trlw_set_running_len(self->rlw, run_len + 1);\n+\t\tassert(rlw_get_running_len(self->rlw) == run_len + 1);\n+\t\treturn 0;\n+\t}\n+\n+\telse {\n+\t\tbuffer_push_rlw(self, 0);\n+\n+\t\tassert(rlw_get_running_len(self->rlw) == 0);\n+\t\tassert(rlw_get_run_bit(self->rlw) == 0);\n+\t\tassert(rlw_get_literal_words(self->rlw) == 0);\n+\n+\t\trlw_set_run_bit(self->rlw, v);\n+\t\tassert(rlw_get_run_bit(self->rlw) == v);\n+\n+\t\trlw_set_running_len(self->rlw, 1);\n+\t\tassert(rlw_get_running_len(self->rlw) == 1);\n+\t\tassert(rlw_get_literal_words(self->rlw) == 0);\n+\t\treturn 1;\n+\t}\n+}\n+\n+size_t ewah_add(struct ewah_bitmap *self, eword_t word)\n+{\n+\tself->bit_size += BITS_IN_WORD;\n+\n+\tif (word == 0)\n+\t\treturn add_empty_word(self, 0);\n+\n+\tif (word == (eword_t)(~0))\n+\t\treturn add_empty_word(self, 1);\n+\n+\treturn add_literal(self, word);\n+}\n+\n+void ewah_set(struct ewah_bitmap *self, size_t i)\n+{\n+\tconst size_t dist =\n+\t\t(i + BITS_IN_WORD) / BITS_IN_WORD -\n+\t\t(self->bit_size + BITS_IN_WORD - 1) / BITS_IN_WORD;\n+\n+\tassert(i >= self->bit_size);\n+\n+\tself->bit_size = i + 1;\n+\n+\tif (dist > 0) {\n+\t\tif (dist > 1)\n+\t\t\tadd_empty_words(self, 0, dist - 1);\n+\n+\t\tadd_literal(self, (eword_t)1 << (i % BITS_IN_WORD));\n+\t\treturn;\n+\t}\n+\n+\tif (rlw_get_literal_words(self->rlw) == 0) {\n+\t\trlw_set_running_len(self->rlw, rlw_get_running_len(self->rlw) - 1);\n+\t\tadd_literal(self, (eword_t)1 << (i % BITS_IN_WORD));\n+\t\treturn;\n+\t}\n+\n+\tself->buffer[self->buffer_size - 1] |= ((eword_t)1 << (i % BITS_IN_WORD));\n+\n+\t/* check if we just completed a stream of 1s */\n+\tif (self->buffer[self->buffer_size - 1] == (eword_t)(~0)) {\n+\t\tself->buffer[--self->buffer_size] = 0;\n+\t\trlw_set_literal_words(self->rlw, rlw_get_literal_words(self->rlw) - 1);\n+\t\tadd_empty_word(self, 1);\n+\t}\n+}\n+\n+void ewah_each_bit(struct ewah_bitmap *self, void (*callback)(size_t, void*), void *payload)\n+{\n+\tsize_t pos = 0;\n+\tsize_t pointer = 0;\n+\tsize_t k;\n+\n+\twhile (pointer < self->buffer_size) {\n+\t\teword_t *word = &self->buffer[pointer];\n+\n+\t\tif (rlw_get_run_bit(word)) {\n+\t\t\tsize_t len = rlw_get_running_len(word) * BITS_IN_WORD;\n+\t\t\tfor (k = 0; k < len; ++k, ++pos) {\n+\t\t\t\tcallback(pos, payload);\n+\t\t\t}\n+\t\t} else {\n+\t\t\tpos += rlw_get_running_len(word) * BITS_IN_WORD;\n+\t\t}\n+\n+\t\t++pointer;\n+\n+\t\tfor (k = 0; k < rlw_get_literal_words(word); ++k) {\n+\t\t\tint c;\n+\n+\t\t\t/* todo: zero count optimization */\n+\t\t\tfor (c = 0; c < BITS_IN_WORD; ++c, ++pos) {\n+\t\t\t\tif ((self->buffer[pointer] & ((eword_t)1 << c)) != 0) {\n+\t\t\t\t\tcallback(pos, payload);\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\t++pointer;\n+\t\t}\n+\t}\n+}\n+\n+struct ewah_bitmap *ewah_new(void)\n+{\n+\tstruct ewah_bitmap *self;\n+\n+\tself = ewah_malloc(sizeof(struct ewah_bitmap));\n+\tif (self == NULL)\n+\t\treturn NULL;\n+\n+\tself->buffer = ewah_malloc(32 * sizeof(eword_t));\n+\tself->alloc_size = 32;\n+\n+\tewah_clear(self);\n+\treturn self;\n+}\n+\n+void ewah_clear(struct ewah_bitmap *self)\n+{\n+\tself->buffer_size = 1;\n+\tself->buffer[0] = 0;\n+\tself->bit_size = 0;\n+\tself->rlw = self->buffer;\n+}\n+\n+void ewah_free(struct ewah_bitmap *self)\n+{\n+\tif (self->alloc_size)\n+\t\tfree(self->buffer);\n+\n+\tfree(self);\n+}\n+\n+static void read_new_rlw(struct ewah_iterator *it)\n+{\n+\tconst eword_t *word = NULL;\n+\n+\tit->literals = 0;\n+\tit->compressed = 0;\n+\n+\twhile (1) {\n+\t\tword = &it->buffer[it->pointer];\n+\n+\t\tit->rl = rlw_get_running_len(word);\n+\t\tit->lw = rlw_get_literal_words(word);\n+\t\tit->b = rlw_get_run_bit(word);\n+\n+\t\tif (it->rl || it->lw)\n+\t\t\treturn;\n+\n+\t\tif (it->pointer < it->buffer_size - 1) {\n+\t\t\tit->pointer++;\n+\t\t} else {\n+\t\t\tit->pointer = it->buffer_size;\n+\t\t\treturn;\n+\t\t}\n+\t}\n+}\n+\n+int ewah_iterator_next(eword_t *next, struct ewah_iterator *it)\n+{\n+\tif (it->pointer >= it->buffer_size)\n+\t\treturn 0;\n+\n+\tif (it->compressed < it->rl) {\n+\t\tit->compressed++;\n+\t\t*next = it->b ? (eword_t)(~0) : 0;\n+\t} else {\n+\t\tassert(it->literals < it->lw);\n+\n+\t\tit->literals++;\n+\t\tit->pointer++;\n+\n+\t\tassert(it->pointer < it->buffer_size);\n+\n+\t\t*next = it->buffer[it->pointer];\n+\t}\n+\n+\tif (it->compressed == it->rl && it->literals == it->lw) {\n+\t\tif (++it->pointer < it->buffer_size)\n+\t\t\tread_new_rlw(it);\n+\t}\n+\n+\treturn 1;\n+}\n+\n+void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent)\n+{\n+\tit->buffer = parent->buffer;\n+\tit->buffer_size = parent->buffer_size;\n+\tit->pointer = 0;\n+\n+\tit->lw = 0;\n+\tit->rl = 0;\n+\tit->compressed = 0;\n+\tit->literals = 0;\n+\tit->b = 0;\n+\n+\tif (it->pointer < it->buffer_size)\n+\t\tread_new_rlw(it);\n+}\n+\n+void ewah_dump(struct ewah_bitmap *self)\n+{\n+\tsize_t i;\n+\tfprintf(stderr, \"%zu bits | %zu words | \", self->bit_size, self->buffer_size);\n+\n+\tfor (i = 0; i < self->buffer_size; ++i)\n+\t\tfprintf(stderr, \"%016llx \", (unsigned long long)self->buffer[i]);\n+\n+\tfprintf(stderr, \"\\n\");\n+}\n+\n+void ewah_not(struct ewah_bitmap *self)\n+{\n+\tsize_t pointer = 0;\n+\n+\twhile (pointer < self->buffer_size) {\n+\t\teword_t *word = &self->buffer[pointer];\n+\t\tsize_t literals, k;\n+\n+\t\trlw_xor_run_bit(word);\n+\t\t++pointer;\n+\n+\t\tliterals = rlw_get_literal_words(word);\n+\t\tfor (k = 0; k < literals; ++k) {\n+\t\t\tself->buffer[pointer] = ~self->buffer[pointer];\n+\t\t\t++pointer;\n+\t\t}\n+\t}\n+}\n+\n+void ewah_xor(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out)\n+{\n+\tstruct rlw_iterator rlw_i;\n+\tstruct rlw_iterator rlw_j;\n+\n+\trlwit_init(&rlw_i, ewah_i);\n+\trlwit_init(&rlw_j, ewah_j);\n+\n+\twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n+\t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n+\t\t\tstruct rlw_iterator *prey, *predator;\n+\t\t\tsize_t index;\n+\t\t\tint negate_words;\n+\n+\t\t\tif (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {\n+\t\t\t\tprey = &rlw_i;\n+\t\t\t\tpredator = &rlw_j;\n+\t\t\t} else {\n+\t\t\t\tprey = &rlw_j;\n+\t\t\t\tpredator = &rlw_i;\n+\t\t\t}\n+\n+\t\t\tnegate_words = !!predator->rlw.running_bit;\n+\t\t\tindex = rlwit_discharge(prey, out, predator->rlw.running_len, negate_words);\n+\n+\t\t\tewah_add_empty_words(out, negate_words, predator->rlw.running_len - index);\n+\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n+\t\t}\n+\n+\t\tsize_t literals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n+\n+\t\tif (literals) {\n+\t\t\tsize_t k;\n+\n+\t\t\tfor (k = 0; k < literals; ++k) {\n+\t\t\t\tewah_add(out,\n+\t\t\t\t\trlw_i.buffer[rlw_i.literal_word_start + k] ^\n+\t\t\t\t\trlw_j.buffer[rlw_j.literal_word_start + k]\n+\t\t\t\t);\n+\t\t\t}\n+\n+\t\t\trlwit_discard_first_words(&rlw_i, literals);\n+\t\t\trlwit_discard_first_words(&rlw_j, literals);\n+\t\t}\n+\t}\n+\n+\tif (rlwit_word_size(&rlw_i) > 0) {\n+\t\trlwit_discharge(&rlw_i, out, ~0, 0);\n+\t} else {\n+\t\trlwit_discharge(&rlw_j, out, ~0, 0);\n+\t}\n+\n+\tout->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);\n+}\n+\n+void ewah_and(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out)\n+{\n+\tstruct rlw_iterator rlw_i;\n+\tstruct rlw_iterator rlw_j;\n+\n+\trlwit_init(&rlw_i, ewah_i);\n+\trlwit_init(&rlw_j, ewah_j);\n+\n+\twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n+\t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n+\t\t\tstruct rlw_iterator *prey, *predator;\n+\n+\t\t\tif (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {\n+\t\t\t\tprey = &rlw_i;\n+\t\t\t\tpredator = &rlw_j;\n+\t\t\t} else {\n+\t\t\t\tprey = &rlw_j;\n+\t\t\t\tpredator = &rlw_i;\n+\t\t\t}\n+\n+\t\t\tif (predator->rlw.running_bit == 0) {\n+\t\t\t\tewah_add_empty_words(out, 0, predator->rlw.running_len);\n+\t\t\t\trlwit_discard_first_words(prey, predator->rlw.running_len);\n+\t\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n+\t\t\t} else {\n+\t\t\t\tsize_t index;\n+\t\t\t\tindex = rlwit_discharge(prey, out, predator->rlw.running_len, 0);\n+\t\t\t\tewah_add_empty_words(out, 0, predator->rlw.running_len - index);\n+\t\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n+\t\t\t}\n+\t\t}\n+\n+\t\tsize_t literals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n+\n+\t\tif (literals) {\n+\t\t\tsize_t k;\n+\n+\t\t\tfor (k = 0; k < literals; ++k) {\n+\t\t\t\tewah_add(out,\n+\t\t\t\t\trlw_i.buffer[rlw_i.literal_word_start + k] &\n+\t\t\t\t\trlw_j.buffer[rlw_j.literal_word_start + k]\n+\t\t\t\t);\n+\t\t\t}\n+\n+\t\t\trlwit_discard_first_words(&rlw_i, literals);\n+\t\t\trlwit_discard_first_words(&rlw_j, literals);\n+\t\t}\n+\t}\n+\n+\tif (rlwit_word_size(&rlw_i) > 0) {\n+\t\trlwit_discharge_empty(&rlw_i, out);\n+\t} else {\n+\t\trlwit_discharge_empty(&rlw_j, out);\n+\t}\n+\n+\tout->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);\n+}\n+\n+void ewah_and_not(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out)\n+{\n+\tstruct rlw_iterator rlw_i;\n+\tstruct rlw_iterator rlw_j;\n+\n+\trlwit_init(&rlw_i, ewah_i);\n+\trlwit_init(&rlw_j, ewah_j);\n+\n+\twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n+\t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n+\t\t\tstruct rlw_iterator *prey, *predator;\n+\n+\t\t\tif (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {\n+\t\t\t\tprey = &rlw_i;\n+\t\t\t\tpredator = &rlw_j;\n+\t\t\t} else {\n+\t\t\t\tprey = &rlw_j;\n+\t\t\t\tpredator = &rlw_i;\n+\t\t\t}\n+\n+\t\t\tif ((predator->rlw.running_bit && prey == &rlw_i) ||\n+\t\t\t\t(!predator->rlw.running_bit && prey != &rlw_i)) {\n+\t\t\t\tewah_add_empty_words(out, 0, predator->rlw.running_len);\n+\t\t\t\trlwit_discard_first_words(prey, predator->rlw.running_len);\n+\t\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n+\t\t\t} else {\n+\t\t\t\tsize_t index;\n+\t\t\t\tint negate_words;\n+\n+\t\t\t\tnegate_words = (&rlw_i != prey);\n+\t\t\t\tindex = rlwit_discharge(prey, out, predator->rlw.running_len, negate_words);\n+\t\t\t\tewah_add_empty_words(out, negate_words, predator->rlw.running_len - index);\n+\t\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n+\t\t\t}\n+\t\t}\n+\n+\t\tsize_t literals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n+\n+\t\tif (literals) {\n+\t\t\tsize_t k;\n+\n+\t\t\tfor (k = 0; k < literals; ++k) {\n+\t\t\t\tewah_add(out,\n+\t\t\t\t\trlw_i.buffer[rlw_i.literal_word_start + k] &\n+\t\t\t\t\t~(rlw_j.buffer[rlw_j.literal_word_start + k])\n+\t\t\t\t);\n+\t\t\t}\n+\n+\t\t\trlwit_discard_first_words(&rlw_i, literals);\n+\t\t\trlwit_discard_first_words(&rlw_j, literals);\n+\t\t}\n+\t}\n+\n+\tif (rlwit_word_size(&rlw_i) > 0) {\n+\t\trlwit_discharge(&rlw_i, out, ~0, 0);\n+\t} else {\n+\t\trlwit_discharge_empty(&rlw_j, out);\n+\t}\n+\n+\tout->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);\n+}\n+\n+void ewah_or(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out)\n+{\n+\tstruct rlw_iterator rlw_i;\n+\tstruct rlw_iterator rlw_j;\n+\n+\trlwit_init(&rlw_i, ewah_i);\n+\trlwit_init(&rlw_j, ewah_j);\n+\n+\twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n+\t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n+\t\t\tstruct rlw_iterator *prey, *predator;\n+\n+\t\t\tif (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {\n+\t\t\t\tprey = &rlw_i;\n+\t\t\t\tpredator = &rlw_j;\n+\t\t\t} else {\n+\t\t\t\tprey = &rlw_j;\n+\t\t\t\tpredator = &rlw_i;\n+\t\t\t}\n+\n+\n+\t\t\tif (predator->rlw.running_bit) {\n+\t\t\t\tewah_add_empty_words(out, 0, predator->rlw.running_len);\n+\t\t\t\trlwit_discard_first_words(prey, predator->rlw.running_len);\n+\t\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n+\t\t\t} else {\n+\t\t\t\tsize_t index;\n+\t\t\t\tindex = rlwit_discharge(prey, out, predator->rlw.running_len, 0);\n+\t\t\t\tewah_add_empty_words(out, 0, predator->rlw.running_len - index);\n+\t\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n+\t\t\t}\n+\t\t}\n+\n+\t\tsize_t literals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n+\n+\t\tif (literals) {\n+\t\t\tsize_t k;\n+\n+\t\t\tfor (k = 0; k < literals; ++k) {\n+\t\t\t\tewah_add(out,\n+\t\t\t\t\trlw_i.buffer[rlw_i.literal_word_start + k] |\n+\t\t\t\t\trlw_j.buffer[rlw_j.literal_word_start + k]\n+\t\t\t\t);\n+\t\t\t}\n+\n+\t\t\trlwit_discard_first_words(&rlw_i, literals);\n+\t\t\trlwit_discard_first_words(&rlw_j, literals);\n+\t\t}\n+\t}\n+\n+\tif (rlwit_word_size(&rlw_i) > 0) {\n+\t\trlwit_discharge(&rlw_i, out, ~0, 0);\n+\t} else {\n+\t\trlwit_discharge(&rlw_j, out, ~0, 0);\n+\t}\n+\n+\tout->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);\n+}\n+\n+\n+#define BITMAP_POOL_MAX 16\n+static struct ewah_bitmap *bitmap_pool[BITMAP_POOL_MAX];\n+static size_t bitmap_pool_size;\n+\n+struct ewah_bitmap *ewah_pool_new(void)\n+{\n+\tif (bitmap_pool_size)\n+\t\treturn bitmap_pool[--bitmap_pool_size];\n+\n+\treturn ewah_new();\n+}\n+\n+void ewah_pool_free(struct ewah_bitmap *self)\n+{\n+\tif (self == NULL)\n+\t\treturn;\n+\n+\tif (bitmap_pool_size == BITMAP_POOL_MAX ||\n+\t\tself->alloc_size == 0) {\n+\t\tewah_free(self);\n+\t\treturn;\n+\t}\n+\n+\tewah_clear(self);\n+\tbitmap_pool[bitmap_pool_size++] = self;\n+}\n+\n+uint32_t ewah_checksum(struct ewah_bitmap *self)\n+{\n+\tconst uint8_t *p = (uint8_t *)self->buffer;\n+\tuint32_t crc = (uint32_t)self->bit_size;\n+\tsize_t size = self->buffer_size * sizeof(eword_t);\n+\n+\twhile (size--)\n+\t\tcrc = (crc << 5) - crc + (uint32_t)*p++;\n+\n+\treturn crc;\n+}\ndiff --git a/ewah/ewah_io.c b/ewah/ewah_io.c\nnew file mode 100644\nindex 0000000..db6c062\n--- /dev/null\n+++ b/ewah/ewah_io.c\n@@ -0,0 +1,192 @@\n+/**\n+ * Copyright 2013, GitHub, Inc\n+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n+ *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; either version 2\n+ * of the License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+ */\n+#include <stdlib.h>\n+#include <unistd.h>\n+#include <stdio.h>\n+\n+#include \"git-compat-util.h\"\n+#include \"ewok.h\"\n+\n+int ewah_serialize_native(struct ewah_bitmap *self, int fd)\n+{\n+\tuint32_t write32;\n+\tsize_t to_write = self->buffer_size * 8;\n+\n+\t/* 32 bit -- bit size fr the map */\n+\twrite32 = (uint32_t)self->bit_size;\n+\tif (write(fd, &write32, 4) != 4)\n+\t\treturn -1;\n+\n+\t/** 32 bit -- number of compressed 64-bit words */\n+\twrite32 = (uint32_t)self->buffer_size;\n+\tif (write(fd, &write32, 4) != 4)\n+\t\treturn -1;\n+\n+\tif (write(fd, self->buffer, to_write) != to_write)\n+\t\treturn -1;\n+\n+\t/** 32 bit -- position for the RLW */\n+\twrite32 = self->rlw - self->buffer;\n+\tif (write(fd, &write32, 4) != 4)\n+\t\treturn -1;\n+\n+\treturn (3 * 4) + to_write;\n+}\n+\n+int ewah_serialize_to(struct ewah_bitmap *self,\n+\t\t      int (*write_fun)(void *, const void *, size_t),\n+\t\t      void *data)\n+{\n+\tsize_t i;\n+\teword_t dump[2048];\n+\tconst size_t words_per_dump = sizeof(dump) / sizeof(eword_t);\n+\n+\t/* 32 bit -- bit size fr the map */\n+\tuint32_t bitsize =  htonl((uint32_t)self->bit_size);\n+\tif (write_fun(data, &bitsize, 4) != 4)\n+\t\treturn -1;\n+\n+\t/** 32 bit -- number of compressed 64-bit words */\n+\tuint32_t word_count =  htonl((uint32_t)self->buffer_size);\n+\tif (write_fun(data, &word_count, 4) != 4)\n+\t\treturn -1;\n+\n+\t/** 64 bit x N -- compressed words */\n+\tconst eword_t *buffer = self->buffer;\n+\tsize_t words_left = self->buffer_size;\n+\n+\twhile (words_left >= words_per_dump) {\n+\t\tfor (i = 0; i < words_per_dump; ++i, ++buffer)\n+\t\t\tdump[i] = htonll(*buffer);\n+\n+\t\tif (write_fun(data, dump, sizeof(dump)) != sizeof(dump))\n+\t\t\treturn -1;\n+\n+\t\twords_left -= words_per_dump;\n+\t}\n+\n+\tif (words_left) {\n+\t\tfor (i = 0; i < words_left; ++i, ++buffer)\n+\t\t\tdump[i] = htonll(*buffer);\n+\n+\t\tif (write_fun(data, dump, words_left * 8) != words_left * 8)\n+\t\t\treturn -1;\n+\t}\n+\n+\t/** 32 bit -- position for the RLW */\n+\tuint32_t rlw_pos = (uint8_t*)self->rlw - (uint8_t *)self->buffer;\n+\trlw_pos = htonl(rlw_pos / sizeof(eword_t));\n+\n+\tif (write_fun(data, &rlw_pos, 4) != 4)\n+\t\treturn -1;\n+\n+\treturn (3 * 4) + (self->buffer_size * 8);\n+}\n+\n+static int write_helper(void *fd, const void *buf, size_t len)\n+{\n+\treturn write((intptr_t)fd, buf, len);\n+}\n+\n+int ewah_serialize(struct ewah_bitmap *self, int fd)\n+{\n+\treturn ewah_serialize_to(self, write_helper, (void *)(intptr_t)fd);\n+}\n+\n+int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len)\n+{\n+\tuint32_t *read32 = map;\n+\teword_t *read64;\n+\tsize_t i;\n+\n+\tself->bit_size = ntohl(*read32++);\n+\tself->buffer_size = self->alloc_size = ntohl(*read32++);\n+\tself->buffer = ewah_realloc(self->buffer, self->alloc_size * sizeof(eword_t));\n+\n+\tif (!self->buffer)\n+\t\treturn -1;\n+\n+\tfor (i = 0, read64 = (void *)read32; i < self->buffer_size; ++i) {\n+\t\tself->buffer[i] = ntohll(*read64++);\n+\t}\n+\n+\tread32 = (void *)read64;\n+\tself->rlw = self->buffer + ntohl(*read32++);\n+\n+\treturn (3 * 4) + (self->buffer_size * 8);\n+}\n+\n+int ewah_deserialize(struct ewah_bitmap *self, int fd)\n+{\n+\tsize_t i;\n+\teword_t dump[2048];\n+\tconst size_t words_per_dump = sizeof(dump) / sizeof(eword_t);\n+\n+\tewah_clear(self);\n+\n+\t/* 32 bit -- bit size fr the map */\n+\tuint32_t bitsize;\n+\tif (read(fd, &bitsize, 4) != 4)\n+\t\treturn -1;\n+\n+\tself->bit_size = (size_t)ntohl(bitsize);\n+\n+\t/** 32 bit -- number of compressed 64-bit words */\n+\tuint32_t word_count;\n+\tif (read(fd, &word_count, 4) != 4)\n+\t\treturn -1;\n+\n+\tself->buffer_size = self->alloc_size = (size_t)ntohl(word_count);\n+\tself->buffer = ewah_realloc(self->buffer, self->alloc_size * sizeof(eword_t));\n+\n+\tif (!self->buffer)\n+\t\treturn -1;\n+\n+\t/** 64 bit x N -- compressed words */\n+\teword_t *buffer = self->buffer;\n+\tsize_t words_left = self->buffer_size;\n+\n+\twhile (words_left >= words_per_dump) {\n+\t\tif (read(fd, dump, sizeof(dump)) != sizeof(dump))\n+\t\t\treturn -1;\n+\n+\t\tfor (i = 0; i < words_per_dump; ++i, ++buffer)\n+\t\t\t*buffer = ntohll(dump[i]);\n+\n+\t\twords_left -= words_per_dump;\n+\t}\n+\n+\tif (words_left) {\n+\t\tif (read(fd, dump, words_left * 8) != words_left * 8)\n+\t\t\treturn -1;\n+\n+\t\tfor (i = 0; i < words_left; ++i, ++buffer)\n+\t\t\t*buffer = ntohll(dump[i]);\n+\t}\n+\n+\t/** 32 bit -- position for the RLW */\n+\tuint32_t rlw_pos;\n+\tif (read(fd, &rlw_pos, 4) != 4)\n+\t\treturn -1;\n+\n+\tself->rlw = self->buffer + ntohl(rlw_pos);\n+\n+\treturn 0;\n+}\ndiff --git a/ewah/ewah_rlw.c b/ewah/ewah_rlw.c\nnew file mode 100644\nindex 0000000..0281025\n--- /dev/null\n+++ b/ewah/ewah_rlw.c\n@@ -0,0 +1,120 @@\n+/**\n+ * Copyright 2013, GitHub, Inc\n+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n+ *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; either version 2\n+ * of the License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+ */\n+#include <assert.h>\n+#include <stdlib.h>\n+#include <unistd.h>\n+#include <string.h>\n+\n+#include \"ewok.h\"\n+#include \"ewok_rlw.h\"\n+\n+static inline int next_word(struct rlw_iterator *it)\n+{\n+\tif (it->pointer >= it->size)\n+\t\treturn 0;\n+\n+\tit->rlw.word = &it->buffer[it->pointer];\n+\tit->pointer += rlw_get_literal_words(it->rlw.word) + 1;\n+\n+\tit->rlw.literal_words = rlw_get_literal_words(it->rlw.word);\n+\tit->rlw.running_len = rlw_get_running_len(it->rlw.word);\n+\tit->rlw.running_bit = rlw_get_run_bit(it->rlw.word);\n+\tit->rlw.literal_word_offset = 0;\n+\n+\treturn 1;\n+}\n+\n+void rlwit_init(struct rlw_iterator *it, struct ewah_bitmap *from_ewah)\n+{\n+\tit->buffer = from_ewah->buffer;\n+\tit->size = from_ewah->buffer_size;\n+\tit->pointer = 0;\n+\n+\tnext_word(it);\n+\n+\tit->literal_word_start = rlwit_literal_words(it) + it->rlw.literal_word_offset;\n+}\n+\n+void rlwit_discard_first_words(struct rlw_iterator *it, size_t x)\n+{\n+\twhile (x > 0) {\n+\t\tsize_t discard;\n+\n+\t\tif (it->rlw.running_len > x) {\n+\t\t\tit->rlw.running_len -= x;\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tx -= it->rlw.running_len;\n+\t\tit->rlw.running_len = 0;\n+\n+\t\tdiscard = (x > it->rlw.literal_words) ? it->rlw.literal_words : x;\n+\n+\t\tit->literal_word_start += discard;\n+\t\tit->rlw.literal_words -= discard;\n+\t\tx -= discard;\n+\n+\t\tif (x > 0 || rlwit_word_size(it) == 0) {\n+\t\t\tif (!next_word(it))\n+\t\t\t\tbreak;\n+\n+\t\t\tit->literal_word_start =\n+\t\t\t\trlwit_literal_words(it) + it->rlw.literal_word_offset;\n+\t\t}\n+\t}\n+}\n+\n+size_t rlwit_discharge(\n+\tstruct rlw_iterator *it, struct ewah_bitmap *out, size_t max, int negate)\n+{\n+\tsize_t index = 0;\n+\n+\twhile (index < max && rlwit_word_size(it) > 0) {\n+\t\tsize_t pd, pl = it->rlw.running_len;\n+\n+\t\tif (index + pl > max) {\n+\t\t\tpl = max - index;\n+\t\t}\n+\n+\t\tewah_add_empty_words(out, it->rlw.running_bit ^ negate, pl);\n+\t\tindex += pl;\n+\n+\t\tpd = it->rlw.literal_words;\n+\t\tif (pd + index > max) {\n+\t\t\tpd = max - index;\n+\t\t}\n+\n+\t\tewah_add_dirty_words(out,\n+\t\t\tit->buffer + it->literal_word_start, pd, negate);\n+\n+\t\trlwit_discard_first_words(it, pd + pl);\n+\t\tindex += pd;\n+\t}\n+\n+\treturn index;\n+}\n+\n+void rlwit_discharge_empty(struct rlw_iterator *it, struct ewah_bitmap *out)\n+{\n+\twhile (rlwit_word_size(it) > 0) {\n+\t\tewah_add_empty_words(out, 0, rlwit_word_size(it));\n+\t\trlwit_discard_first_words(it, rlwit_word_size(it));\n+\t}\n+}\ndiff --git a/ewah/ewok.h b/ewah/ewok.h\nnew file mode 100644\nindex 0000000..3db7703\n--- /dev/null\n+++ b/ewah/ewok.h\n@@ -0,0 +1,223 @@\n+/**\n+ * Copyright 2013, GitHub, Inc\n+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n+ *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; either version 2\n+ * of the License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+ */\n+#ifndef __EWOK_BITMAP_C__\n+#define __EWOK_BITMAP_C__\n+\n+#include <stdint.h>\n+\n+#ifndef ewah_malloc\n+#\tdefine ewah_malloc malloc\n+#endif\n+#ifndef ewah_realloc\n+#\tdefine ewah_realloc realloc\n+#endif\n+#ifndef ewah_calloc\n+#\tdefine ewah_calloc calloc\n+#endif\n+\n+typedef uint64_t eword_t;\n+#define BITS_IN_WORD (sizeof(eword_t) * 8)\n+\n+/**\n+ * Do not use __builtin_popcountll. The GCC implementation\n+ * is notoriously slow on all platforms.\n+ *\n+ * See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36041\n+ */\n+static inline uint32_t ewah_bit_popcount64(uint64_t x)\n+{\n+\tx = (x & 0x5555555555555555ULL) + ((x >>  1) & 0x5555555555555555ULL);\n+\tx = (x & 0x3333333333333333ULL) + ((x >>  2) & 0x3333333333333333ULL);\n+\tx = (x & 0x0F0F0F0F0F0F0F0FULL) + ((x >>  4) & 0x0F0F0F0F0F0F0F0FULL);\n+\treturn (x * 0x0101010101010101ULL) >> 56;\n+}\n+\n+#define ewah_bit_ctz64(x) __builtin_ctzll(x)\n+\n+struct ewah_bitmap {\n+\teword_t *buffer;\n+\tsize_t buffer_size;\n+\tsize_t alloc_size;\n+\tsize_t bit_size;\n+\teword_t *rlw;\n+};\n+\n+typedef void (*ewah_callback)(size_t pos, void *);\n+\n+struct ewah_bitmap *ewah_pool_new(void);\n+void ewah_pool_free(struct ewah_bitmap *self);\n+\n+/**\n+ * Allocate a new EWAH Compressed bitmap\n+ */\n+struct ewah_bitmap *ewah_new(void);\n+\n+/**\n+ * Clear all the bits in the bitmap. Does not free or resize\n+ * memory.\n+ */\n+void ewah_clear(struct ewah_bitmap *self);\n+\n+/**\n+ * Free all the memory of the bitmap\n+ */\n+void ewah_free(struct ewah_bitmap *self);\n+\n+int ewah_serialize_to(struct ewah_bitmap *self,\n+\t\t      int (*write_fun)(void *out, const void *buf, size_t len),\n+\t\t      void *out);\n+int ewah_serialize(struct ewah_bitmap *self, int fd);\n+int ewah_serialize_native(struct ewah_bitmap *self, int fd);\n+\n+int ewah_deserialize(struct ewah_bitmap *self, int fd);\n+int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len);\n+int ewah_read_mmap_native(struct ewah_bitmap *self, void *map, size_t len);\n+\n+uint32_t ewah_checksum(struct ewah_bitmap *self);\n+\n+/**\n+ * Logical not (bitwise negation) in-place on the bitmap\n+ *\n+ * This operation is linear time based on the size of the bitmap.\n+ */\n+void ewah_not(struct ewah_bitmap *self);\n+\n+/**\n+ * Call the given callback with the position of every single bit\n+ * that has been set on the bitmap.\n+ *\n+ * This is an efficient operation that does not fully decompress\n+ * the bitmap.\n+ */\n+void ewah_each_bit(struct ewah_bitmap *self, ewah_callback callback, void *payload);\n+\n+/**\n+ * Set a given bit on the bitmap.\n+ *\n+ * The bit at position `pos` will be set to true. Because of the\n+ * way that the bitmap is compressed, a set bit cannot be unset\n+ * later on.\n+ *\n+ * Furthermore, since the bitmap uses streaming compression, bits\n+ * can only set incrementally.\n+ *\n+ * E.g.\n+ *\t\tewah_set(bitmap, 1); // ok\n+ *\t\tewah_set(bitmap, 76); // ok\n+ *\t\tewah_set(bitmap, 77); // ok\n+ *\t\tewah_set(bitmap, 8712800127); // ok\n+ *\t\tewah_set(bitmap, 25); // failed, assert raised\n+ */\n+void ewah_set(struct ewah_bitmap *self, size_t i);\n+\n+struct ewah_iterator {\n+\tconst eword_t *buffer;\n+\tsize_t buffer_size;\n+\n+\tsize_t pointer;\n+\teword_t compressed, literals;\n+\teword_t rl, lw;\n+\tint b;\n+};\n+\n+/**\n+ * Initialize a new iterator to run through the bitmap in uncompressed form.\n+ *\n+ * The iterator can be stack allocated. The underlying bitmap must not be freed\n+ * before the iteration is over.\n+ *\n+ * E.g.\n+ *\n+ *\t\tstruct ewah_bitmap *bitmap = ewah_new();\n+ *\t\tstruct ewah_iterator it;\n+ *\n+ *\t\tewah_iterator_init(&it, bitmap);\n+ */\n+void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent);\n+\n+/**\n+ * Yield every single word in the bitmap in uncompressed form. This is:\n+ * yield single words (32-64 bits) where each bit represents an actual\n+ * bit from the bitmap.\n+ *\n+ * Return: true if a word was yield, false if there are no words left\n+ */\n+int ewah_iterator_next(eword_t *next, struct ewah_iterator *it);\n+\n+void ewah_or(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out);\n+\n+void ewah_and_not(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out);\n+\n+void ewah_xor(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out);\n+\n+void ewah_and(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out);\n+\n+void ewah_dump(struct ewah_bitmap *self);\n+\n+/**\n+ * Direct word access\n+ */\n+size_t ewah_add_empty_words(struct ewah_bitmap *self, int v, size_t number);\n+void ewah_add_dirty_words(\n+\tstruct ewah_bitmap *self, const eword_t *buffer, size_t number, int negate);\n+size_t ewah_add(struct ewah_bitmap *self, eword_t word);\n+\n+\n+/**\n+ * Uncompressed, old-school bitmap that can be efficiently compressed\n+ * into an `ewah_bitmap`.\n+ */\n+struct bitmap {\n+\teword_t *words;\n+\tsize_t word_alloc;\n+};\n+\n+struct bitmap *bitmap_new(void);\n+void bitmap_set(struct bitmap *self, size_t pos);\n+void bitmap_clear(struct bitmap *self, size_t pos);\n+int bitmap_get(struct bitmap *self, size_t pos);\n+void bitmap_reset(struct bitmap *self);\n+void bitmap_free(struct bitmap *self);\n+int bitmap_equals(struct bitmap *self, struct bitmap *other);\n+int bitmap_is_subset(struct bitmap *self, struct bitmap *super);\n+\n+struct ewah_bitmap * bitmap_to_ewah(struct bitmap *bitmap);\n+struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah);\n+\n+void bitmap_and_not(struct bitmap *self, struct bitmap *other);\n+void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other);\n+void bitmap_or(struct bitmap *self, const struct bitmap *other);\n+\n+void bitmap_each_bit(struct bitmap *self, ewah_callback callback, void *data);\n+size_t bitmap_popcount(struct bitmap *self);\n+\n+#endif\ndiff --git a/ewah/ewok_rlw.h b/ewah/ewok_rlw.h\nnew file mode 100644\nindex 0000000..63efdf9\n--- /dev/null\n+++ b/ewah/ewok_rlw.h\n@@ -0,0 +1,114 @@\n+/**\n+ * Copyright 2013, GitHub, Inc\n+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n+ *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; either version 2\n+ * of the License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+ */\n+#ifndef __EWOK_RLW_H__\n+#define __EWOK_RLW_H__\n+\n+#define RLW_RUNNING_BITS (sizeof(eword_t) * 4)\n+#define RLW_LITERAL_BITS (sizeof(eword_t) * 8 - 1 - RLW_RUNNING_BITS)\n+\n+#define RLW_LARGEST_RUNNING_COUNT (((eword_t)1 << RLW_RUNNING_BITS) - 1)\n+#define RLW_LARGEST_LITERAL_COUNT (((eword_t)1 << RLW_LITERAL_BITS) - 1)\n+\n+#define RLW_LARGEST_RUNNING_COUNT_SHIFT (RLW_LARGEST_RUNNING_COUNT << 1)\n+\n+#define RLW_RUNNING_LEN_PLUS_BIT (((eword_t)1 << (RLW_RUNNING_BITS + 1)) - 1)\n+\n+static int rlw_get_run_bit(const eword_t *word)\n+{\n+\treturn *word & (eword_t)1;\n+}\n+\n+static inline void rlw_set_run_bit(eword_t *word, int b)\n+{\n+\tif (b) {\n+\t\t*word |= (eword_t)1;\n+\t} else {\n+\t\t*word &= (eword_t)(~1);\n+\t}\n+}\n+\n+static inline void rlw_xor_run_bit(eword_t *word)\n+{\n+\tif (*word & 1) {\n+\t\t*word &= (eword_t)(~1);\n+\t} else {\n+\t\t*word |= (eword_t)1;\n+\t}\n+}\n+\n+static inline void rlw_set_running_len(eword_t *word, eword_t l)\n+{\n+\t*word |= RLW_LARGEST_RUNNING_COUNT_SHIFT;\n+\t*word &= (l << 1) | (~RLW_LARGEST_RUNNING_COUNT_SHIFT);\n+}\n+\n+static inline eword_t rlw_get_running_len(const eword_t *word)\n+{\n+\treturn (*word >> 1) & RLW_LARGEST_RUNNING_COUNT;\n+}\n+\n+static inline eword_t rlw_get_literal_words(const eword_t *word)\n+{\n+\treturn *word >> (1 + RLW_RUNNING_BITS);\n+}\n+\n+static inline void rlw_set_literal_words(eword_t *word, eword_t l)\n+{\n+\t*word |= ~RLW_RUNNING_LEN_PLUS_BIT;\n+\t*word &= (l << (RLW_RUNNING_BITS + 1)) | RLW_RUNNING_LEN_PLUS_BIT;\n+}\n+\n+static inline eword_t rlw_size(const eword_t *self)\n+{\n+\treturn rlw_get_running_len(self) + rlw_get_literal_words(self);\n+}\n+\n+struct rlw_iterator {\n+\tconst eword_t *buffer;\n+\tsize_t size;\n+\tsize_t pointer;\n+\tsize_t literal_word_start;\n+\n+\tstruct {\n+\t\tconst eword_t *word;\n+\t\tint literal_words;\n+\t\tint running_len;\n+\t\tint literal_word_offset;\n+\t\tint running_bit;\n+\t} rlw;\n+};\n+\n+void rlwit_init(struct rlw_iterator *it, struct ewah_bitmap *bitmap);\n+void rlwit_discard_first_words(struct rlw_iterator *it, size_t x);\n+size_t rlwit_discharge(\n+\tstruct rlw_iterator *it, struct ewah_bitmap *out, size_t max, int negate);\n+void rlwit_discharge_empty(struct rlw_iterator *it, struct ewah_bitmap *out);\n+\n+static inline size_t rlwit_word_size(struct rlw_iterator *it)\n+{\n+\treturn it->rlw.running_len + it->rlw.literal_words;\n+}\n+\n+static inline size_t rlwit_literal_words(struct rlw_iterator *it)\n+{\n+\treturn it->pointer - it->rlw.literal_words;\n+}\n+\n+#endif\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229410","messageId":"20131024180347.GI24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 09/19] documentation: add documentation for the bitmap format","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:03:48Z","receivedAt":"2013-10-24T18:03:48Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThis is the technical documentation for the JGit-compatible Bitmap v1\non-disk format.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/technical/bitmap-format.txt | 131 ++++++++++++++++++++++++++++++\n 1 file changed, 131 insertions(+)\n create mode 100644 Documentation/technical/bitmap-format.txt\n\ndiff --git a/Documentation/technical/bitmap-format.txt b/Documentation/technical/bitmap-format.txt\nnew file mode 100644\nindex 0000000..c686dd1\n--- /dev/null\n+++ b/Documentation/technical/bitmap-format.txt\n@@ -0,0 +1,131 @@\n+GIT bitmap v1 format\n+====================\n+\n+\t- A header appears at the beginning:\n+\n+\t\t4-byte signature: {'B', 'I', 'T', 'M'}\n+\n+\t\t2-byte version number (network byte order)\n+\t\t\tThe current implementation only supports version 1\n+\t\t\tof the bitmap index (the same one as JGit).\n+\n+\t\t2-byte flags (network byte order)\n+\n+\t\t\tThe following flags are supported:\n+\n+\t\t\t- BITMAP_OPT_FULL_DAG (0x1) REQUIRED\n+\t\t\tThis flag must always be present. It implies that the bitmap\n+\t\t\tindex has been generated for a packfile with full closure\n+\t\t\t(i.e. where every single object in the packfile can find\n+\t\t\t its parent links inside the same packfile). This is a\n+\t\t\trequirement for the bitmap index format, also present in JGit,\n+\t\t\tthat greatly reduces the complexity of the implementation.\n+\n+\t\t4-byte entry count (network byte order)\n+\n+\t\t\tThe total count of entries (bitmapped commits) in this bitmap index.\n+\n+\t\t20-byte checksum\n+\n+\t\t\tThe SHA1 checksum of the pack this bitmap index belongs to.\n+\n+\t- 4 EWAH bitmaps that act as type indexes\n+\n+\t\tType indexes are serialized after the hash cache in the shape\n+\t\tof four EWAH bitmaps stored consecutively (see Appendix A for\n+\t\tthe serialization format of an EWAH bitmap).\n+\n+\t\tThere is a bitmap for each Git object type, stored in the following\n+\t\torder:\n+\n+\t\t\t- Commits\n+\t\t\t- Trees\n+\t\t\t- Blobs\n+\t\t\t- Tags\n+\n+\t\tIn each bitmap, the `n`th bit is set to true if the `n`th object\n+\t\tin the packfile is of that type.\n+\n+\t\tThe obvious consequence is that the OR of all 4 bitmaps will result\n+\t\tin a full set (all bits set), and the AND of all 4 bitmaps will\n+\t\tresult in an empty bitmap (no bits set).\n+\n+\t- N entries with compressed bitmaps, one for each indexed commit\n+\n+\t\tWhere `N` is the total amount of entries in this bitmap index.\n+\t\tEach entry contains the following:\n+\n+\t\t- 4-byte object position (network byte order)\n+\t\t\tThe position **in the index for the packfile** where the\n+\t\t\tbitmap for this commit is found.\n+\n+\t\t- 1-byte XOR-offset\n+\t\t\tThe xor offset used to compress this bitmap. For an entry\n+\t\t\tin position `x`, a XOR offset of `y` means that the actual\n+\t\t\tbitmap representing this commit is composed by XORing the\n+\t\t\tbitmap for this entry with the bitmap in entry `x-y` (i.e.\n+\t\t\tthe bitmap `y` entries before this one).\n+\n+\t\t\tNote that this compression can be recursive. In order to\n+\t\t\tXOR this entry with a previous one, the previous entry needs\n+\t\t\tto be decompressed first, and so on.\n+\n+\t\t\tThe hard-limit for this offset is 160 (an entry can only be\n+\t\t\txor'ed against one of the 160 entries preceding it). This\n+\t\t\tnumber is always positive, and hence entries are always xor'ed\n+\t\t\twith **previous** bitmaps, not bitmaps that will come afterwards\n+\t\t\tin the index.\n+\n+\t\t- 1-byte flags for this bitmap\n+\t\t\tAt the moment the only available flag is `0x1`, which hints\n+\t\t\tthat this bitmap can be re-used when rebuilding bitmap indexes\n+\t\t\tfor the repository.\n+\n+\t\t- The compressed bitmap itself, see Appendix A.\n+\n+== Appendix A: Serialization format for an EWAH bitmap\n+\n+Ewah bitmaps are serialized in the protocol as the JAVAEWAH\n+library, making them backwards compatible with the JGit\n+implementation:\n+\n+\t- 4-byte number of bits of the resulting UNCOMPRESSED bitmap\n+\n+\t- 4-byte number of words of the COMPRESSED bitmap, when stored\n+\n+\t- N x 8-byte words, as specified by the previous field\n+\n+\t\tThis is the actual content of the compressed bitmap.\n+\n+\t- 4-byte position of the current RLW for the compressed\n+\t\tbitmap\n+\n+All words are stored in network byte order for their corresponding\n+sizes.\n+\n+The compressed bitmap is stored in a form of run-length encoding, as\n+follows.  It consists of a concatenation of an arbitrary number of\n+chunks.  Each chunk consists of one or more 64-bit words\n+\n+     H  L_1  L_2  L_3 .... L_M\n+\n+H is called RLW (run length word).  It consists of (from lower to higher\n+order bits):\n+\n+     - 1 bit: the repeated bit B\n+\n+     - 32 bits: repetition count K (unsigned)\n+\n+     - 31 bits: literal word count M (unsigned)\n+\n+The bitstream represented by the above chunk is then:\n+\n+     - K repetitions of B\n+\n+     - The bits stored in `L_1` through `L_M`.  Within a word, bits at\n+       lower order come earlier in the stream than those at higher\n+       order.\n+\n+The next word after `L_M` (if any) must again be a RLW, for the next\n+chunk.  For efficient appending to the bitstream, the EWAH stores a\n+pointer to the last RLW in the stream.\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229411","messageId":"20131024180357.GJ24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:03:57Z","receivedAt":"2013-10-24T18:03:57Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nA bitmap index is a `.bitmap` file that can be found inside\n`$GIT_DIR/objects/pack/`, next to its corresponding packfile, and\ncontains precalculated reachability information for selected commits.\nThe full specification of the format for these bitmap indexes can be found\nin `Documentation/technical/bitmap-format.txt`.\n\nFor a given commit SHA1, if it happens to be available in the bitmap\nindex, its bitmap will represent every single object that is reachable\nfrom the commit itself. The nth bit in the bitmap is the nth object in\nthe packfile; if it's set to 1, the object is reachable.\n\nBy using the bitmaps available in the index, this commit implements\nseveral new functions:\n\n\t- `prepare_bitmap_git`\n\t- `prepare_bitmap_walk`\n\t- `traverse_bitmap_commit_list`\n\t- `reuse_partial_packfile_from_bitmap`\n\nThe `prepare_bitmap_walk` function tries to build a bitmap of all the\nobjects that can be reached from the commit roots of a given `rev_info`\nstruct by using the following algorithm:\n\n- If all the interesting commits for a revision walk are available in\nthe index, the resulting reachability bitmap is the bitwise OR of all\nthe individual bitmaps.\n\n- When the full set of WANTs is not available in the index, we perform a\npartial revision walk using the commits that don't have bitmaps as\nroots, and limiting the revision walk as soon as we reach a commit that\nhas a corresponding bitmap. The earlier OR'ed bitmap with all the\nindexed commits can now be completed as this walk progresses, so the end\nresult is the full reachability list.\n\n- For revision walks with a HAVEs set (a set of commits that are deemed\nuninteresting), first we perform the same method as for the WANTs, but\nusing our HAVEs as roots, in order to obtain a full reachability bitmap\nof all the uninteresting commits. This bitmap then can be used to:\n\n\ta) limit the subsequent walk when building the WANTs bitmap\n\tb) finding the final set of interesting commits by performing an\n\t   AND-NOT of the WANTs and the HAVEs.\n\nIf `prepare_bitmap_walk` runs successfully, the resulting bitmap is\nstored and the equivalent of a `traverse_commit_list` call can be\nperformed by using `traverse_bitmap_commit_list`; the bitmap version\nof this call yields the objects straight from the packfile index\n(without having to look them up or parse them) and hence is several\norders of magnitude faster.\n\nAs an extra optimization, when `prepare_bitmap_walk` succeeds, the\n`reuse_partial_packfile_from_bitmap` call can be attempted: it will find\nthe amount of objects at the beginning of the on-disk packfile that can\nbe reused as-is, and return an offset into the packfile. The source\npackfile can then be loaded and the bytes up to `offset` can be written\ndirectly to the result without having to consider the entires inside the\npackfile individually.\n\nIf the `prepare_bitmap_walk` call fails (e.g. because no bitmap files\nare available), the `rev_info` struct is left untouched, and can be used\nto perform a manual rev-walk using `traverse_commit_list`.\n\nHence, this new set of functions are a generic API that allows to\nperform the equivalent of\n\n\tgit rev-list --objects [roots...] [^uninteresting...]\n\nfor any set of commits, even if they don't have specific bitmaps\ngenerated for them.\n\nIn further patches, we'll use this bitmap traversal optimization to\nspeed up the `pack-objects` and `rev-list` commands.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Makefile      |   2 +\n khash.h       | 342 +++++++++++++++++++++\n pack-bitmap.c | 965 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n pack-bitmap.h |  46 +++\n 4 files changed, 1355 insertions(+)\n create mode 100644 khash.h\n create mode 100644 pack-bitmap.c\n create mode 100644 pack-bitmap.h\n\ndiff --git a/Makefile b/Makefile\nindex eacb940..737766c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -699,6 +699,7 @@ LIB_H += object.h\n LIB_H += pack-objects.h\n LIB_H += pack-revindex.h\n LIB_H += pack.h\n+LIB_H += pack-bitmap.h\n LIB_H += parse-options.h\n LIB_H += patch-ids.h\n LIB_H += pathspec.h\n@@ -837,6 +838,7 @@ LIB_OBJS += notes-cache.o\n LIB_OBJS += notes-merge.o\n LIB_OBJS += notes-utils.o\n LIB_OBJS += object.o\n+LIB_OBJS += pack-bitmap.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-objects.o\n LIB_OBJS += pack-revindex.o\ndiff --git a/khash.h b/khash.h\nnew file mode 100644\nindex 0000000..0fdf39d\n--- /dev/null\n+++ b/khash.h\n@@ -0,0 +1,342 @@\n+/* The MIT License\n+\n+   Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>\n+\n+   Permission is hereby granted, free of charge, to any person obtaining\n+   a copy of this software and associated documentation files (the\n+   \"Software\"), to deal in the Software without restriction, including\n+   without limitation the rights to use, copy, modify, merge, publish,\n+   distribute, sublicense, and/or sell copies of the Software, and to\n+   permit persons to whom the Software is furnished to do so, subject to\n+   the following conditions:\n+\n+   The above copyright notice and this permission notice shall be\n+   included in all copies or substantial portions of the Software.\n+\n+   THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n+   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n+   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n+   SOFTWARE.\n+*/\n+\n+#ifndef __AC_KHASH_H\n+#define __AC_KHASH_H\n+\n+#define AC_VERSION_KHASH_H \"0.2.8\"\n+\n+#include <stdlib.h>\n+#include <string.h>\n+#include <limits.h>\n+\n+typedef uint32_t khint32_t;\n+typedef uint64_t khint64_t;\n+\n+typedef khint32_t khint_t;\n+typedef khint_t khiter_t;\n+\n+#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)\n+#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)\n+#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)\n+#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))\n+#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))\n+#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))\n+#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))\n+\n+#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)\n+\n+#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))\n+\n+static inline khint_t __ac_X31_hash_string(const char *s)\n+{\n+\tkhint_t h = (khint_t)*s;\n+\tif (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;\n+\treturn h;\n+}\n+\n+#define kh_str_hash_func(key) __ac_X31_hash_string(key)\n+#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)\n+\n+static const double __ac_HASH_UPPER = 0.77;\n+\n+#define __KHASH_TYPE(name, khkey_t, khval_t) \\\n+\ttypedef struct { \\\n+\t\tkhint_t n_buckets, size, n_occupied, upper_bound; \\\n+\t\tkhint32_t *flags; \\\n+\t\tkhkey_t *keys; \\\n+\t\tkhval_t *vals; \\\n+\t} kh_##name##_t;\n+\n+#define __KHASH_PROTOTYPES(name, khkey_t, khval_t)\t \t\t\t\t\t\\\n+\textern kh_##name##_t *kh_init_##name(void);\t\t\t\t\t\t\t\\\n+\textern void kh_destroy_##name(kh_##name##_t *h);\t\t\t\t\t\\\n+\textern void kh_clear_##name(kh_##name##_t *h);\t\t\t\t\t\t\\\n+\textern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \t\\\n+\textern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \\\n+\textern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \\\n+\textern void kh_del_##name(kh_##name##_t *h, khint_t x);\n+\n+#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \\\n+\tSCOPE kh_##name##_t *kh_init_##name(void) {\t\t\t\t\t\t\t\\\n+\t\treturn (kh_##name##_t*)xcalloc(1, sizeof(kh_##name##_t));\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\tSCOPE void kh_destroy_##name(kh_##name##_t *h)\t\t\t\t\t\t\\\n+\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (h) {\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tfree((void *)h->keys); free(h->flags);\t\t\t\t\t\\\n+\t\t\tfree((void *)h->vals);\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tfree(h);\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\tSCOPE void kh_clear_##name(kh_##name##_t *h)\t\t\t\t\t\t\\\n+\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (h && h->flags) {\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tmemset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \\\n+\t\t\th->size = h->n_occupied = 0;\t\t\t\t\t\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\tSCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \t\\\n+\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (h->n_buckets) {\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tkhint_t k, i, last, mask, step = 0; \\\n+\t\t\tmask = h->n_buckets - 1;\t\t\t\t\t\t\t\t\t\\\n+\t\t\tk = __hash_func(key); i = k & mask;\t\t\t\t\t\t\t\\\n+\t\t\tlast = i; \\\n+\t\t\twhile (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \\\n+\t\t\t\ti = (i + (++step)) & mask; \\\n+\t\t\t\tif (i == last) return h->n_buckets;\t\t\t\t\t\t\\\n+\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\treturn __ac_iseither(h->flags, i)? h->n_buckets : i;\t\t\\\n+\t\t} else return 0;\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\tSCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \\\n+\t{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \\\n+\t\tkhint32_t *new_flags = 0;\t\t\t\t\t\t\t\t\t\t\\\n+\t\tkhint_t j = 1;\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tkroundup32(new_n_buckets); \t\t\t\t\t\t\t\t\t\\\n+\t\t\tif (new_n_buckets < 4) new_n_buckets = 4;\t\t\t\t\t\\\n+\t\t\tif (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0;\t/* requested size is too small */ \\\n+\t\t\telse { /* hash table size to be changed (shrink or expand); rehash */ \\\n+\t\t\t\tnew_flags = (khint32_t*)xmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t));\t\\\n+\t\t\t\tif (!new_flags) return -1;\t\t\t\t\t\t\t\t\\\n+\t\t\t\tmemset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \\\n+\t\t\t\tif (h->n_buckets < new_n_buckets) {\t/* expand */\t\t\\\n+\t\t\t\t\tkhkey_t *new_keys = (khkey_t*)xrealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \\\n+\t\t\t\t\tif (!new_keys) return -1;\t\t\t\t\t\t\t\\\n+\t\t\t\t\th->keys = new_keys;\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\tif (kh_is_map) {\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\tkhval_t *new_vals = (khval_t*)xrealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \\\n+\t\t\t\t\t\tif (!new_vals) return -1;\t\t\t\t\t\t\\\n+\t\t\t\t\t\th->vals = new_vals;\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t} /* otherwise shrink */\t\t\t\t\t\t\t\t\\\n+\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (j) { /* rehashing is needed */\t\t\t\t\t\t\t\t\\\n+\t\t\tfor (j = 0; j != h->n_buckets; ++j) {\t\t\t\t\t\t\\\n+\t\t\t\tif (__ac_iseither(h->flags, j) == 0) {\t\t\t\t\t\\\n+\t\t\t\t\tkhkey_t key = h->keys[j];\t\t\t\t\t\t\t\\\n+\t\t\t\t\tkhval_t val;\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\tkhint_t new_mask;\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\tnew_mask = new_n_buckets - 1; \t\t\t\t\t\t\\\n+\t\t\t\t\tif (kh_is_map) val = h->vals[j];\t\t\t\t\t\\\n+\t\t\t\t\t__ac_set_isdel_true(h->flags, j);\t\t\t\t\t\\\n+\t\t\t\t\twhile (1) { /* kick-out process; sort of like in Cuckoo hashing */ \\\n+\t\t\t\t\t\tkhint_t k, i, step = 0; \\\n+\t\t\t\t\t\tk = __hash_func(key);\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\ti = k & new_mask;\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\twhile (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \\\n+\t\t\t\t\t\t__ac_set_isempty_false(new_flags, i);\t\t\t\\\n+\t\t\t\t\t\tif (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \\\n+\t\t\t\t\t\t\t{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \\\n+\t\t\t\t\t\t\tif (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \\\n+\t\t\t\t\t\t\t__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \\\n+\t\t\t\t\t\t} else { /* write the element and jump out of the loop */ \\\n+\t\t\t\t\t\t\th->keys[i] = key;\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\tif (kh_is_map) h->vals[i] = val;\t\t\t\\\n+\t\t\t\t\t\t\tbreak;\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tif (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \\\n+\t\t\t\th->keys = (khkey_t*)xrealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \\\n+\t\t\t\tif (kh_is_map) h->vals = (khval_t*)xrealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \\\n+\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tfree(h->flags); /* free the working space */\t\t\t\t\\\n+\t\t\th->flags = new_flags;\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\th->n_buckets = new_n_buckets;\t\t\t\t\t\t\t\t\\\n+\t\t\th->n_occupied = h->size;\t\t\t\t\t\t\t\t\t\\\n+\t\t\th->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \\\n+\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\treturn 0;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\tSCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \\\n+\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tkhint_t x;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (h->n_occupied >= h->upper_bound) { /* update the hash table */ \\\n+\t\t\tif (h->n_buckets > (h->size<<1)) {\t\t\t\t\t\t\t\\\n+\t\t\t\tif (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear \"deleted\" elements */ \\\n+\t\t\t\t\t*ret = -1; return h->n_buckets;\t\t\t\t\t\t\\\n+\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \\\n+\t\t\t\t*ret = -1; return h->n_buckets;\t\t\t\t\t\t\t\\\n+\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \\\n+\t\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tkhint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \\\n+\t\t\tx = site = h->n_buckets; k = __hash_func(key); i = k & mask; \\\n+\t\t\tif (__ac_isempty(h->flags, i)) x = i; /* for speed up */\t\\\n+\t\t\telse {\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\tlast = i; \\\n+\t\t\t\twhile (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \\\n+\t\t\t\t\tif (__ac_isdel(h->flags, i)) site = i;\t\t\t\t\\\n+\t\t\t\t\ti = (i + (++step)) & mask; \\\n+\t\t\t\t\tif (i == last) { x = site; break; }\t\t\t\t\t\\\n+\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\tif (x == h->n_buckets) {\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\tif (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \\\n+\t\t\t\t\telse x = i;\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (__ac_isempty(h->flags, x)) { /* not present at all */\t\t\\\n+\t\t\th->keys[x] = key;\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t__ac_set_isboth_false(h->flags, x);\t\t\t\t\t\t\t\\\n+\t\t\t++h->size; ++h->n_occupied;\t\t\t\t\t\t\t\t\t\\\n+\t\t\t*ret = 1;\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t} else if (__ac_isdel(h->flags, x)) { /* deleted */\t\t\t\t\\\n+\t\t\th->keys[x] = key;\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t__ac_set_isboth_false(h->flags, x);\t\t\t\t\t\t\t\\\n+\t\t\t++h->size;\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t*ret = 2;\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \\\n+\t\treturn x;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\tSCOPE void kh_del_##name(kh_##name##_t *h, khint_t x)\t\t\t\t\\\n+\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (x != h->n_buckets && !__ac_iseither(h->flags, x)) {\t\t\t\\\n+\t\t\t__ac_set_isdel_true(h->flags, x);\t\t\t\t\t\t\t\\\n+\t\t\t--h->size;\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t}\n+\n+#define KHASH_DECLARE(name, khkey_t, khval_t)\t\t \t\t\t\t\t\\\n+\t__KHASH_TYPE(name, khkey_t, khval_t) \t\t\t\t\t\t\t\t\\\n+\t__KHASH_PROTOTYPES(name, khkey_t, khval_t)\n+\n+#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \\\n+\t__KHASH_TYPE(name, khkey_t, khval_t) \t\t\t\t\t\t\t\t\\\n+\t__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)\n+\n+#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \\\n+\tKHASH_INIT2(name, static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)\n+\n+/* Other convenient macros... */\n+\n+/*! @function\n+  @abstract     Test whether a bucket contains data.\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @param  x     Iterator to the bucket [khint_t]\n+  @return       1 if containing data; 0 otherwise [int]\n+ */\n+#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))\n+\n+/*! @function\n+  @abstract     Get key given an iterator\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @param  x     Iterator to the bucket [khint_t]\n+  @return       Key [type of keys]\n+ */\n+#define kh_key(h, x) ((h)->keys[x])\n+\n+/*! @function\n+  @abstract     Get value given an iterator\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @param  x     Iterator to the bucket [khint_t]\n+  @return       Value [type of values]\n+  @discussion   For hash sets, calling this results in segfault.\n+ */\n+#define kh_val(h, x) ((h)->vals[x])\n+\n+/*! @function\n+  @abstract     Alias of kh_val()\n+ */\n+#define kh_value(h, x) ((h)->vals[x])\n+\n+/*! @function\n+  @abstract     Get the start iterator\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @return       The start iterator [khint_t]\n+ */\n+#define kh_begin(h) (khint_t)(0)\n+\n+/*! @function\n+  @abstract     Get the end iterator\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @return       The end iterator [khint_t]\n+ */\n+#define kh_end(h) ((h)->n_buckets)\n+\n+/*! @function\n+  @abstract     Get the number of elements in the hash table\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @return       Number of elements in the hash table [khint_t]\n+ */\n+#define kh_size(h) ((h)->size)\n+\n+/*! @function\n+  @abstract     Get the number of buckets in the hash table\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @return       Number of buckets in the hash table [khint_t]\n+ */\n+#define kh_n_buckets(h) ((h)->n_buckets)\n+\n+/*! @function\n+  @abstract     Iterate over the entries in the hash table\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @param  kvar  Variable to which key will be assigned\n+  @param  vvar  Variable to which value will be assigned\n+  @param  code  Block of code to execute\n+ */\n+#define kh_foreach(h, kvar, vvar, code) { khint_t __i;\t\t\\\n+\tfor (__i = kh_begin(h); __i != kh_end(h); ++__i) {\t\t\\\n+\t\tif (!kh_exist(h,__i)) continue;\t\t\t\t\t\t\\\n+\t\t(kvar) = kh_key(h,__i);\t\t\t\t\t\t\t\t\\\n+\t\t(vvar) = kh_val(h,__i);\t\t\t\t\t\t\t\t\\\n+\t\tcode;\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t} }\n+\n+/*! @function\n+  @abstract     Iterate over the values in the hash table\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @param  vvar  Variable to which value will be assigned\n+  @param  code  Block of code to execute\n+ */\n+#define kh_foreach_value(h, vvar, code) { khint_t __i;\t\t\\\n+\tfor (__i = kh_begin(h); __i != kh_end(h); ++__i) {\t\t\\\n+\t\tif (!kh_exist(h,__i)) continue;\t\t\t\t\t\t\\\n+\t\t(vvar) = kh_val(h,__i);\t\t\t\t\t\t\t\t\\\n+\t\tcode;\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t} }\n+\n+static inline khint_t __kh_oid_hash(const unsigned char *oid)\n+{\n+\tkhint_t hash;\n+\tmemcpy(&hash, oid, sizeof(hash));\n+\treturn hash;\n+}\n+\n+#define __kh_oid_cmp(a, b) (hashcmp(a, b) == 0)\n+\n+KHASH_INIT(sha1, const unsigned char *, void *, 1, __kh_oid_hash, __kh_oid_cmp)\n+typedef kh_sha1_t khash_sha1;\n+\n+KHASH_INIT(sha1_pos, const unsigned char *, int, 1, __kh_oid_hash, __kh_oid_cmp)\n+typedef kh_sha1_pos_t khash_sha1_pos;\n+\n+#endif /* __AC_KHASH_H */\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nnew file mode 100644\nindex 0000000..b800736\n--- /dev/null\n+++ b/pack-bitmap.c\n@@ -0,0 +1,965 @@\n+#include <stdlib.h>\n+\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"tag.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n+#include \"progress.h\"\n+#include \"list-objects.h\"\n+#include \"pack.h\"\n+#include \"pack-bitmap.h\"\n+#include \"pack-revindex.h\"\n+#include \"pack-objects.h\"\n+\n+/*\n+ * An entry on the bitmap index, representing the bitmap for a given\n+ * commit.\n+ */\n+struct stored_bitmap {\n+\tunsigned char sha1[20];\n+\tstruct ewah_bitmap *root;\n+\tstruct stored_bitmap *xor;\n+\tint flags;\n+};\n+\n+/*\n+ * The currently active bitmap index. By design, repositories only have\n+ * a single bitmap index available (the index for the biggest packfile in\n+ * the repository), since bitmap indexes need full closure.\n+ *\n+ * If there is more than one bitmap index available (e.g. because of alternates),\n+ * the active bitmap index is the largest one.\n+ */\n+static struct bitmap_index {\n+\t/* Packfile to which this bitmap index belongs to */\n+\tstruct packed_git *pack;\n+\n+\t/* reverse index for the packfile */\n+\tstruct pack_revindex *reverse_index;\n+\n+\t/*\n+\t * Mark the first `reuse_objects` in the packfile as reused:\n+\t * they will be sent as-is without using them for repacking\n+\t * calculations\n+\t */\n+\tuint32_t reuse_objects;\n+\n+\t/* mmapped buffer of the whole bitmap index */\n+\tvoid *map;\n+\tsize_t map_size; /* size of the mmaped buffer */\n+\tsize_t map_pos; /* current position when loading the index */\n+\n+\t/*\n+\t * Type indexes.\n+\t *\n+\t * Each bitmap marks which objects in the packfile  are of the given\n+\t * type. This provides type information when yielding the objects from\n+\t * the packfile during a walk, which allows for better delta bases.\n+\t */\n+\tstruct ewah_bitmap *commits;\n+\tstruct ewah_bitmap *trees;\n+\tstruct ewah_bitmap *blobs;\n+\tstruct ewah_bitmap *tags;\n+\n+\t/* Map from SHA1 -> `stored_bitmap` for all the bitmapped comits */\n+\tkhash_sha1 *bitmaps;\n+\n+\t/* Number of bitmapped commits */\n+\tuint32_t entry_count;\n+\n+\t/*\n+\t * Extended index.\n+\t *\n+\t * When trying to perform bitmap operations with objects that are not\n+\t * packed in `pack`, these objects are added to this \"fake index\" and\n+\t * are assumed to appear at the end of the packfile for all operations\n+\t */\n+\tstruct eindex {\n+\t\tstruct object **objects;\n+\t\tuint32_t *hashes;\n+\t\tuint32_t count, alloc;\n+\t\tkhash_sha1_pos *positions;\n+\t} ext_index;\n+\n+\t/* Bitmap result of the last performed walk */\n+\tstruct bitmap *result;\n+\n+\t/* Version of the bitmap index */\n+\tunsigned int version;\n+\n+\tunsigned loaded : 1;\n+\n+} bitmap_git;\n+\n+static struct ewah_bitmap *lookup_stored_bitmap(struct stored_bitmap *st)\n+{\n+\tstruct ewah_bitmap *parent;\n+\tstruct ewah_bitmap *composed;\n+\n+\tif (st->xor == NULL)\n+\t\treturn st->root;\n+\n+\tcomposed = ewah_pool_new();\n+\tparent = lookup_stored_bitmap(st->xor);\n+\tewah_xor(st->root, parent, composed);\n+\n+\tewah_pool_free(st->root);\n+\tst->root = composed;\n+\tst->xor = NULL;\n+\n+\treturn composed;\n+}\n+\n+/*\n+ * Read a bitmap from the current read position on the mmaped\n+ * index, and increase the read position accordingly\n+ */\n+static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)\n+{\n+\tstruct ewah_bitmap *b = ewah_pool_new();\n+\n+\tint bitmap_size = ewah_read_mmap(b,\n+\t\tindex->map + index->map_pos,\n+\t\tindex->map_size - index->map_pos);\n+\n+\tif (bitmap_size < 0) {\n+\t\terror(\"Failed to load bitmap index (corrupted?)\");\n+\t\tewah_pool_free(b);\n+\t\treturn NULL;\n+\t}\n+\n+\tindex->map_pos += bitmap_size;\n+\treturn b;\n+}\n+\n+static int load_bitmap_header(struct bitmap_index *index)\n+{\n+\tstruct bitmap_disk_header *header = (void *)index->map;\n+\n+\tif (index->map_size < sizeof(*header) + 20)\n+\t\treturn error(\"Corrupted bitmap index (missing header data)\");\n+\n+\tif (memcmp(header->magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE)) != 0)\n+\t\treturn error(\"Corrupted bitmap index file (wrong header)\");\n+\n+\tindex->version = ntohs(header->version);\n+\tif (index->version != 1)\n+\t\treturn error(\"Unsupported version for bitmap index file (%d)\", index->version);\n+\n+\t/* Parse known bitmap format options */\n+\t{\n+\t\tuint32_t flags = ntohs(header->options);\n+\n+\t\tif ((flags & BITMAP_OPT_FULL_DAG) == 0)\n+\t\t\treturn error(\"Unsupported options for bitmap index file \"\n+\t\t\t\t\"(Git requires BITMAP_OPT_FULL_DAG)\");\n+\t}\n+\n+\tindex->entry_count = ntohl(header->entry_count);\n+\tindex->map_pos += sizeof(*header);\n+\treturn 0;\n+}\n+\n+static struct stored_bitmap *store_bitmap(struct bitmap_index *index,\n+\t\t\t\t\t  struct ewah_bitmap *root,\n+\t\t\t\t\t  const unsigned char *sha1,\n+\t\t\t\t\t  struct stored_bitmap *xor_with,\n+\t\t\t\t\t  int flags)\n+{\n+\tstruct stored_bitmap *stored;\n+\tkhiter_t hash_pos;\n+\tint ret;\n+\n+\tstored = xmalloc(sizeof(struct stored_bitmap));\n+\tstored->root = root;\n+\tstored->xor = xor_with;\n+\tstored->flags = flags;\n+\thashcpy(stored->sha1, sha1);\n+\n+\thash_pos = kh_put_sha1(index->bitmaps, stored->sha1, &ret);\n+\n+\t/* a 0 return code means the insertion succeeded with no changes,\n+\t * because the SHA1 already existed on the map. this is bad, there\n+\t * shouldn't be duplicated commits in the index */\n+\tif (ret == 0) {\n+\t\terror(\"Duplicate entry in bitmap index: %s\", sha1_to_hex(sha1));\n+\t\treturn NULL;\n+\t}\n+\n+\tkh_value(index->bitmaps, hash_pos) = stored;\n+\treturn stored;\n+}\n+\n+static int load_bitmap_entries_v1(struct bitmap_index *index)\n+{\n+\tstatic const size_t MAX_XOR_OFFSET = 160;\n+\n+\tuint32_t i;\n+\tstruct stored_bitmap **recent_bitmaps;\n+\tstruct bitmap_disk_entry *entry;\n+\n+\trecent_bitmaps = xcalloc(MAX_XOR_OFFSET, sizeof(struct stored_bitmap));\n+\n+\tfor (i = 0; i < index->entry_count; ++i) {\n+\t\tint xor_offset, flags;\n+\t\tstruct ewah_bitmap *bitmap = NULL;\n+\t\tstruct stored_bitmap *xor_bitmap = NULL;\n+\t\tuint32_t commit_idx_pos;\n+\t\tconst unsigned char *sha1;\n+\n+\t\tentry = index->map + index->map_pos;\n+\t\tindex->map_pos += sizeof(struct bitmap_disk_entry);\n+\n+\t\tcommit_idx_pos = ntohl(entry->object_pos);\n+\t\tsha1 = nth_packed_object_sha1(index->pack, commit_idx_pos);\n+\n+\t\txor_offset = (int)entry->xor_offset;\n+\t\tflags = (int)entry->flags;\n+\n+\t\tbitmap = read_bitmap_1(index);\n+\t\tif (!bitmap)\n+\t\t\treturn -1;\n+\n+\t\tif (xor_offset > MAX_XOR_OFFSET || xor_offset > i)\n+\t\t\treturn error(\"Corrupted bitmap pack index\");\n+\n+\t\tif (xor_offset > 0) {\n+\t\t\txor_bitmap = recent_bitmaps[(i - xor_offset) % MAX_XOR_OFFSET];\n+\n+\t\t\tif (xor_bitmap == NULL)\n+\t\t\t\treturn error(\"Invalid XOR offset in bitmap pack index\");\n+\t\t}\n+\n+\t\trecent_bitmaps[i % MAX_XOR_OFFSET] = store_bitmap(\n+\t\t\tindex, bitmap, sha1, xor_bitmap, flags);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int open_pack_bitmap_1(struct packed_git *packfile)\n+{\n+\tint fd;\n+\tstruct stat st;\n+\tchar *idx_name;\n+\n+\tif (open_pack_index(packfile))\n+\t\treturn -1;\n+\n+\tidx_name = pack_bitmap_filename(packfile);\n+\tfd = git_open_noatime(idx_name);\n+\tfree(idx_name);\n+\n+\tif (fd < 0)\n+\t\treturn -1;\n+\n+\tif (fstat(fd, &st)) {\n+\t\tclose(fd);\n+\t\treturn -1;\n+\t}\n+\n+\tbitmap_git.pack = packfile;\n+\tbitmap_git.map_size = xsize_t(st.st_size);\n+\tbitmap_git.map = xmmap(NULL, bitmap_git.map_size, PROT_READ, MAP_PRIVATE, fd, 0);\n+\tbitmap_git.map_pos = 0;\n+\tclose(fd);\n+\n+\tif (load_bitmap_header(&bitmap_git) < 0) {\n+\t\tmunmap(bitmap_git.map, bitmap_git.map_size);\n+\t\tbitmap_git.map = NULL;\n+\t\tbitmap_git.map_size = 0;\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int load_pack_bitmap(void)\n+{\n+\tassert(bitmap_git.map && !bitmap_git.loaded);\n+\n+\tbitmap_git.bitmaps = kh_init_sha1();\n+\tbitmap_git.ext_index.positions = kh_init_sha1_pos();\n+\tbitmap_git.reverse_index = revindex_for_pack(bitmap_git.pack);\n+\n+\tif (!(bitmap_git.commits = read_bitmap_1(&bitmap_git)) ||\n+\t\t!(bitmap_git.trees = read_bitmap_1(&bitmap_git)) ||\n+\t\t!(bitmap_git.blobs = read_bitmap_1(&bitmap_git)) ||\n+\t\t!(bitmap_git.tags = read_bitmap_1(&bitmap_git)))\n+\t\tgoto failed;\n+\n+\tif (load_bitmap_entries_v1(&bitmap_git) < 0)\n+\t\tgoto failed;\n+\n+\tbitmap_git.loaded = 1;\n+\treturn 0;\n+\n+failed:\n+\tmunmap(bitmap_git.map, bitmap_git.map_size);\n+\tbitmap_git.map = NULL;\n+\tbitmap_git.map_size = 0;\n+\treturn -1;\n+}\n+\n+char *pack_bitmap_filename(struct packed_git *p)\n+{\n+\tchar *idx_name;\n+\tint len;\n+\n+\tlen = strlen(p->pack_name) - strlen(\".pack\");\n+\tidx_name = xmalloc(len + strlen(\".bitmap\") + 1);\n+\n+\tmemcpy(idx_name, p->pack_name, len);\n+\tmemcpy(idx_name + len, \".bitmap\", strlen(\".bitmap\") + 1);\n+\n+\treturn idx_name;\n+}\n+\n+static int open_pack_bitmap(void)\n+{\n+\tstruct packed_git *p;\n+\n+\tassert(!bitmap_git.map && !bitmap_git.loaded);\n+\n+\tprepare_packed_git();\n+\tfor (p = packed_git; p; p = p->next) {\n+\t\tif (open_pack_bitmap_1(p) == 0)\n+\t\t\treturn 0;\n+\t}\n+\n+\treturn -1;\n+}\n+\n+int prepare_bitmap_git(void)\n+{\n+\tif (bitmap_git.loaded)\n+\t\treturn 0;\n+\n+\tif (!open_pack_bitmap())\n+\t\treturn load_pack_bitmap();\n+\n+\treturn -1;\n+}\n+\n+struct include_data {\n+\tstruct bitmap *base;\n+\tstruct bitmap *seen;\n+};\n+\n+static inline int bitmap_position_extended(const unsigned char *sha1)\n+{\n+\tkhash_sha1_pos *positions = bitmap_git.ext_index.positions;\n+\tkhiter_t pos = kh_get_sha1_pos(positions, sha1);\n+\n+\tif (pos < kh_end(positions)) {\n+\t\tint bitmap_pos = kh_value(positions, pos);\n+\t\treturn bitmap_pos + bitmap_git.pack->num_objects;\n+\t}\n+\n+\treturn -1;\n+}\n+\n+static inline int bitmap_position_packfile(const unsigned char *sha1)\n+{\n+\toff_t offset = find_pack_entry_one(sha1, bitmap_git.pack);\n+\tif (!offset)\n+\t\treturn -1;\n+\n+\treturn find_revindex_position(bitmap_git.reverse_index, offset);\n+}\n+\n+static int bitmap_position(const unsigned char *sha1)\n+{\n+\tint pos = bitmap_position_packfile(sha1);\n+\treturn (pos >= 0) ? pos : bitmap_position_extended(sha1);\n+}\n+\n+static int ext_index_add_object(struct object *object, const char *name)\n+{\n+\tstruct eindex *eindex = &bitmap_git.ext_index;\n+\n+\tkhiter_t hash_pos;\n+\tint hash_ret;\n+\tint bitmap_pos;\n+\n+\thash_pos = kh_put_sha1_pos(eindex->positions, object->sha1, &hash_ret);\n+\tif (hash_ret > 0) {\n+\t\tif (eindex->count >= eindex->alloc) {\n+\t\t\teindex->alloc = (eindex->alloc + 16) * 3 / 2;\n+\t\t\teindex->objects = xrealloc(eindex->objects,\n+\t\t\t\teindex->alloc * sizeof(struct object *));\n+\t\t\teindex->hashes = xrealloc(eindex->hashes,\n+\t\t\t\teindex->alloc * sizeof(uint32_t));\n+\t\t}\n+\n+\t\tbitmap_pos = eindex->count;\n+\t\teindex->objects[eindex->count] = object;\n+\t\teindex->hashes[eindex->count] = pack_name_hash(name);\n+\t\tkh_value(eindex->positions, hash_pos) = bitmap_pos;\n+\t\teindex->count++;\n+\t} else {\n+\t\tbitmap_pos = kh_value(eindex->positions, hash_pos);\n+\t}\n+\n+\treturn bitmap_pos + bitmap_git.pack->num_objects;\n+}\n+\n+static void show_object(struct object *object, const struct name_path *path,\n+\t\t\tconst char *last, void *data)\n+{\n+\tstruct bitmap *base = data;\n+\tint bitmap_pos;\n+\n+\tbitmap_pos = bitmap_position(object->sha1);\n+\n+\tif (bitmap_pos < 0) {\n+\t\tchar *name = path_name(path, last);\n+\t\tbitmap_pos = ext_index_add_object(object, name);\n+\t\tfree(name);\n+\t}\n+\n+\tbitmap_set(base, bitmap_pos);\n+}\n+\n+static void show_commit(struct commit *commit, void *data)\n+{\n+}\n+\n+static int add_to_include_set(struct include_data *data,\n+\t\t\t      const unsigned char *sha1,\n+\t\t\t      int bitmap_pos)\n+{\n+\tkhiter_t hash_pos;\n+\n+\tif (data->seen && bitmap_get(data->seen, bitmap_pos))\n+\t\treturn 0;\n+\n+\tif (bitmap_get(data->base, bitmap_pos))\n+\t\treturn 0;\n+\n+\thash_pos = kh_get_sha1(bitmap_git.bitmaps, sha1);\n+\tif (hash_pos < kh_end(bitmap_git.bitmaps)) {\n+\t\tstruct stored_bitmap *st = kh_value(bitmap_git.bitmaps, hash_pos);\n+\t\tbitmap_or_ewah(data->base, lookup_stored_bitmap(st));\n+\t\treturn 0;\n+\t}\n+\n+\tbitmap_set(data->base, bitmap_pos);\n+\treturn 1;\n+}\n+\n+static int should_include(struct commit *commit, void *_data)\n+{\n+\tstruct include_data *data = _data;\n+\tint bitmap_pos;\n+\n+\tbitmap_pos = bitmap_position(commit->object.sha1);\n+\tif (bitmap_pos < 0)\n+\t\tbitmap_pos = ext_index_add_object((struct object *)commit, NULL);\n+\n+\tif (!add_to_include_set(data, commit->object.sha1, bitmap_pos)) {\n+\t\tstruct commit_list *parent = commit->parents;\n+\n+\t\twhile (parent) {\n+\t\t\tparent->item->object.flags |= SEEN;\n+\t\t\tparent = parent->next;\n+\t\t}\n+\n+\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+static struct bitmap *find_objects(struct rev_info *revs,\n+\t\t\t\t   struct object_list *roots,\n+\t\t\t\t   struct bitmap *seen)\n+{\n+\tstruct bitmap *base = NULL;\n+\tint needs_walk = 0;\n+\n+\tstruct object_list *not_mapped = NULL;\n+\n+\t/*\n+\t * Go through all the roots for the walk. The ones that have bitmaps\n+\t * on the bitmap index will be `or`ed together to form an initial\n+\t * global reachability analysis.\n+\t *\n+\t * The ones without bitmaps in the index will be stored in the\n+\t * `not_mapped_list` for further processing.\n+\t */\n+\twhile (roots) {\n+\t\tstruct object *object = roots->item;\n+\t\troots = roots->next;\n+\n+\t\tif (object->type == OBJ_COMMIT) {\n+\t\t\tkhiter_t pos = kh_get_sha1(bitmap_git.bitmaps, object->sha1);\n+\n+\t\t\tif (pos < kh_end(bitmap_git.bitmaps)) {\n+\t\t\t\tstruct stored_bitmap *st = kh_value(bitmap_git.bitmaps, pos);\n+\t\t\t\tstruct ewah_bitmap *or_with = lookup_stored_bitmap(st);\n+\n+\t\t\t\tif (base == NULL)\n+\t\t\t\t\tbase = ewah_to_bitmap(or_with);\n+\t\t\t\telse\n+\t\t\t\t\tbitmap_or_ewah(base, or_with);\n+\n+\t\t\t\tobject->flags |= SEEN;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+\n+\t\tobject_list_insert(object, &not_mapped);\n+\t}\n+\n+\t/*\n+\t * Best case scenario: We found bitmaps for all the roots,\n+\t * so the resulting `or` bitmap has the full reachability analysis\n+\t */\n+\tif (not_mapped == NULL)\n+\t\treturn base;\n+\n+\troots = not_mapped;\n+\n+\t/*\n+\t * Let's iterate through all the roots that don't have bitmaps to\n+\t * check if we can determine them to be reachable from the existing\n+\t * global bitmap.\n+\t *\n+\t * If we cannot find them in the existing global bitmap, we'll need\n+\t * to push them to an actual walk and run it until we can confirm\n+\t * they are reachable\n+\t */\n+\twhile (roots) {\n+\t\tstruct object *object = roots->item;\n+\t\tint pos;\n+\n+\t\troots = roots->next;\n+\t\tpos = bitmap_position(object->sha1);\n+\n+\t\tif (pos < 0 || base == NULL || !bitmap_get(base, pos)) {\n+\t\t\tobject->flags &= ~UNINTERESTING;\n+\t\t\tadd_pending_object(revs, object, \"\");\n+\t\t\tneeds_walk = 1;\n+\t\t} else {\n+\t\t\tobject->flags |= SEEN;\n+\t\t}\n+\t}\n+\n+\tif (needs_walk) {\n+\t\tstruct include_data incdata;\n+\n+\t\tif (base == NULL)\n+\t\t\tbase = bitmap_new();\n+\n+\t\tincdata.base = base;\n+\t\tincdata.seen = seen;\n+\n+\t\trevs->include_check = should_include;\n+\t\trevs->include_check_data = &incdata;\n+\n+\t\tif (prepare_revision_walk(revs))\n+\t\t\tdie(\"revision walk setup failed\");\n+\n+\t\ttraverse_commit_list(revs, show_commit, show_object, base);\n+\t}\n+\n+\treturn base;\n+}\n+\n+static void show_extended_objects(struct bitmap *objects,\n+\t\t\t\t  show_reachable_fn show_reach)\n+{\n+\tstruct eindex *eindex = &bitmap_git.ext_index;\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < eindex->count; ++i) {\n+\t\tstruct object *obj;\n+\n+\t\tif (!bitmap_get(objects, bitmap_git.pack->num_objects + i))\n+\t\t\tcontinue;\n+\n+\t\tobj = eindex->objects[i];\n+\t\tshow_reach(obj->sha1, obj->type, 0, eindex->hashes[i], NULL, 0);\n+\t}\n+}\n+\n+static void show_objects_for_type(\n+\tstruct bitmap *objects,\n+\tstruct ewah_bitmap *type_filter,\n+\tenum object_type object_type,\n+\tshow_reachable_fn show_reach)\n+{\n+\tsize_t pos = 0, i = 0;\n+\tuint32_t offset;\n+\n+\tstruct ewah_iterator it;\n+\teword_t filter;\n+\n+\tif (bitmap_git.reuse_objects == bitmap_git.pack->num_objects)\n+\t\treturn;\n+\n+\tewah_iterator_init(&it, type_filter);\n+\n+\twhile (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) {\n+\t\teword_t word = objects->words[i] & filter;\n+\n+\t\tfor (offset = 0; offset < BITS_IN_WORD; ++offset) {\n+\t\t\tconst unsigned char *sha1;\n+\t\t\tstruct revindex_entry *entry;\n+\t\t\tuint32_t hash = 0;\n+\n+\t\t\tif ((word >> offset) == 0)\n+\t\t\t\tbreak;\n+\n+\t\t\toffset += ewah_bit_ctz64(word >> offset);\n+\n+\t\t\tif (pos + offset < bitmap_git.reuse_objects)\n+\t\t\t\tcontinue;\n+\n+\t\t\tentry = &bitmap_git.reverse_index->revindex[pos + offset];\n+\t\t\tsha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);\n+\n+\t\t\tshow_reach(sha1, object_type, 0, hash, bitmap_git.pack, entry->offset);\n+\t\t}\n+\n+\t\tpos += BITS_IN_WORD;\n+\t\ti++;\n+\t}\n+}\n+\n+static int in_bitmapped_pack(struct object_list *roots)\n+{\n+\twhile (roots) {\n+\t\tstruct object *object = roots->item;\n+\t\troots = roots->next;\n+\n+\t\tif (find_pack_entry_one(object->sha1, bitmap_git.pack) > 0)\n+\t\t\treturn 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int prepare_bitmap_walk(struct rev_info *revs)\n+{\n+\tunsigned int i;\n+\tunsigned int pending_nr = revs->pending.nr;\n+\tstruct object_array_entry *pending_e = revs->pending.objects;\n+\n+\tstruct object_list *wants = NULL;\n+\tstruct object_list *haves = NULL;\n+\n+\tstruct bitmap *wants_bitmap = NULL;\n+\tstruct bitmap *haves_bitmap = NULL;\n+\n+\tif (!bitmap_git.loaded) {\n+\t\t/* try to open a bitmapped pack, but don't parse it yet\n+\t\t * because we may not need to use it */\n+\t\tif (open_pack_bitmap() < 0)\n+\t\t\treturn -1;\n+\t}\n+\n+\tfor (i = 0; i < pending_nr; ++i) {\n+\t\tstruct object *object = pending_e[i].item;\n+\n+\t\tif (object->type == OBJ_NONE)\n+\t\t\tparse_object_or_die(object->sha1, NULL);\n+\n+\t\twhile (object->type == OBJ_TAG) {\n+\t\t\tstruct tag *tag = (struct tag *) object;\n+\n+\t\t\tif (object->flags & UNINTERESTING)\n+\t\t\t\tobject_list_insert(object, &haves);\n+\t\t\telse\n+\t\t\t\tobject_list_insert(object, &wants);\n+\n+\t\t\tif (!tag->tagged)\n+\t\t\t\tdie(\"bad tag\");\n+\t\t\tobject = parse_object_or_die(tag->tagged->sha1, NULL);\n+\t\t}\n+\n+\t\tif (object->flags & UNINTERESTING)\n+\t\t\tobject_list_insert(object, &haves);\n+\t\telse\n+\t\t\tobject_list_insert(object, &wants);\n+\t}\n+\n+\t/*\n+\t * if we have a HAVES list, but none of those haves is contained\n+\t * in the packfile that has a bitmap, we don't have anything to\n+\t * optimize here\n+\t */\n+\tif (haves && !in_bitmapped_pack(haves))\n+\t\treturn -1;\n+\n+\t/* if we don't want anything, we're done here */\n+\tif (!wants)\n+\t\treturn -1;\n+\n+\t/*\n+\t * now we're going to use bitmaps, so load the actual bitmap entries\n+\t * from disk. this is the point of no return; after this the rev_list\n+\t * becomes invalidated and we must perform the revwalk through bitmaps\n+\t */\n+\tif (!bitmap_git.loaded && load_pack_bitmap() < 0)\n+\t\treturn -1;\n+\n+\trevs->pending.nr = 0;\n+\trevs->pending.alloc = 0;\n+\trevs->pending.objects = NULL;\n+\n+\tif (haves) {\n+\t\thaves_bitmap = find_objects(revs, haves, NULL);\n+\t\treset_revision_walk();\n+\n+\t\tif (haves_bitmap == NULL)\n+\t\t\tdie(\"BUG: failed to perform bitmap walk\");\n+\t}\n+\n+\twants_bitmap = find_objects(revs, wants, haves_bitmap);\n+\n+\tif (!wants_bitmap)\n+\t\tdie(\"BUG: failed to perform bitmap walk\");\n+\n+\tif (haves_bitmap)\n+\t\tbitmap_and_not(wants_bitmap, haves_bitmap);\n+\n+\tbitmap_git.result = wants_bitmap;\n+\n+\tbitmap_free(haves_bitmap);\n+\treturn 0;\n+}\n+\n+int reuse_partial_packfile_from_bitmap(struct packed_git **packfile,\n+\t\t\t\t       uint32_t *entries,\n+\t\t\t\t       off_t *up_to)\n+{\n+\t/*\n+\t * Reuse the packfile content if we need more than\n+\t * 90% of its objects\n+\t */\n+\tstatic const double REUSE_PERCENT = 0.9;\n+\n+\tstruct bitmap *result = bitmap_git.result;\n+\tuint32_t reuse_threshold;\n+\tuint32_t i, reuse_objects = 0;\n+\n+\tassert(result);\n+\n+\tfor (i = 0; i < result->word_alloc; ++i) {\n+\t\tif (result->words[i] != (eword_t)~0) {\n+\t\t\treuse_objects += ewah_bit_ctz64(~result->words[i]);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\treuse_objects += BITS_IN_WORD;\n+\t}\n+\n+#ifdef GIT_BITMAP_DEBUG\n+\t{\n+\t\tconst unsigned char *sha1;\n+\t\tstruct revindex_entry *entry;\n+\n+\t\tentry = &bitmap_git.reverse_index->revindex[reuse_objects];\n+\t\tsha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);\n+\n+\t\tfprintf(stderr, \"Failed to reuse at %d (%016llx)\\n\",\n+\t\t\treuse_objects, result->words[i]);\n+\t\tfprintf(stderr, \" %s\\n\", sha1_to_hex(sha1));\n+\t}\n+#endif\n+\n+\tif (!reuse_objects)\n+\t\treturn -1;\n+\n+\tif (reuse_objects >= bitmap_git.pack->num_objects) {\n+\t\tbitmap_git.reuse_objects = *entries = bitmap_git.pack->num_objects;\n+\t\t*up_to = -1; /* reuse the full pack */\n+\t\t*packfile = bitmap_git.pack;\n+\t\treturn 0;\n+\t}\n+\n+\treuse_threshold = bitmap_popcount(bitmap_git.result) * REUSE_PERCENT;\n+\n+\tif (reuse_objects < reuse_threshold)\n+\t\treturn -1;\n+\n+\tbitmap_git.reuse_objects = *entries = reuse_objects;\n+\t*up_to = bitmap_git.reverse_index->revindex[reuse_objects].offset;\n+\t*packfile = bitmap_git.pack;\n+\n+\treturn 0;\n+}\n+\n+void traverse_bitmap_commit_list(show_reachable_fn show_reachable)\n+{\n+\tassert(bitmap_git.result);\n+\n+\tshow_objects_for_type(bitmap_git.result, bitmap_git.commits,\n+\t\tOBJ_COMMIT, show_reachable);\n+\tshow_objects_for_type(bitmap_git.result, bitmap_git.trees,\n+\t\tOBJ_TREE, show_reachable);\n+\tshow_objects_for_type(bitmap_git.result, bitmap_git.blobs,\n+\t\tOBJ_BLOB, show_reachable);\n+\tshow_objects_for_type(bitmap_git.result, bitmap_git.tags,\n+\t\tOBJ_TAG, show_reachable);\n+\n+\tshow_extended_objects(bitmap_git.result, show_reachable);\n+\n+\tbitmap_free(bitmap_git.result);\n+\tbitmap_git.result = NULL;\n+}\n+\n+static uint32_t count_object_type(struct bitmap *objects,\n+\t\t\t\t  enum object_type type)\n+{\n+\tstruct eindex *eindex = &bitmap_git.ext_index;\n+\n+\tuint32_t i = 0, count = 0;\n+\tstruct ewah_iterator it;\n+\teword_t filter;\n+\n+\tswitch (type) {\n+\tcase OBJ_COMMIT:\n+\t\tewah_iterator_init(&it, bitmap_git.commits);\n+\t\tbreak;\n+\n+\tcase OBJ_TREE:\n+\t\tewah_iterator_init(&it, bitmap_git.trees);\n+\t\tbreak;\n+\n+\tcase OBJ_BLOB:\n+\t\tewah_iterator_init(&it, bitmap_git.blobs);\n+\t\tbreak;\n+\n+\tcase OBJ_TAG:\n+\t\tewah_iterator_init(&it, bitmap_git.tags);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\n+\twhile (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) {\n+\t\teword_t word = objects->words[i++] & filter;\n+\t\tcount += ewah_bit_popcount64(word);\n+\t}\n+\n+\tfor (i = 0; i < eindex->count; ++i) {\n+\t\tif (eindex->objects[i]->type == type &&\n+\t\t\tbitmap_get(objects, bitmap_git.pack->num_objects + i))\n+\t\t\tcount++;\n+\t}\n+\n+\treturn count;\n+}\n+\n+void count_bitmap_commit_list(uint32_t *commits, uint32_t *trees,\n+\t\t\t      uint32_t *blobs, uint32_t *tags)\n+{\n+\tassert(bitmap_git.result);\n+\n+\tif (commits)\n+\t\t*commits = count_object_type(bitmap_git.result, OBJ_COMMIT);\n+\n+\tif (trees)\n+\t\t*trees = count_object_type(bitmap_git.result, OBJ_TREE);\n+\n+\tif (blobs)\n+\t\t*blobs = count_object_type(bitmap_git.result, OBJ_BLOB);\n+\n+\tif (tags)\n+\t\t*tags = count_object_type(bitmap_git.result, OBJ_TAG);\n+}\n+\n+struct bitmap_test_data {\n+\tstruct bitmap *base;\n+\tstruct progress *prg;\n+\tsize_t seen;\n+};\n+\n+static void test_show_object(struct object *object,\n+\t\t\t     const struct name_path *path,\n+\t\t\t     const char *last, void *data)\n+{\n+\tstruct bitmap_test_data *tdata = data;\n+\tint bitmap_pos;\n+\n+\tbitmap_pos = bitmap_position(object->sha1);\n+\tif (bitmap_pos < 0)\n+\t\tdie(\"Object not in bitmap: %s\\n\", sha1_to_hex(object->sha1));\n+\n+\tbitmap_set(tdata->base, bitmap_pos);\n+\tdisplay_progress(tdata->prg, ++tdata->seen);\n+}\n+\n+static void test_show_commit(struct commit *commit, void *data)\n+{\n+\tstruct bitmap_test_data *tdata = data;\n+\tint bitmap_pos;\n+\n+\tbitmap_pos = bitmap_position(commit->object.sha1);\n+\tif (bitmap_pos < 0)\n+\t\tdie(\"Object not in bitmap: %s\\n\", sha1_to_hex(commit->object.sha1));\n+\n+\tbitmap_set(tdata->base, bitmap_pos);\n+\tdisplay_progress(tdata->prg, ++tdata->seen);\n+}\n+\n+void test_bitmap_walk(struct rev_info *revs)\n+{\n+\tstruct object *root;\n+\tstruct bitmap *result = NULL;\n+\tkhiter_t pos;\n+\tsize_t result_popcnt;\n+\tstruct bitmap_test_data tdata;\n+\n+\tif (prepare_bitmap_git())\n+\t\tdie(\"failed to load bitmap indexes\");\n+\n+\tif (revs->pending.nr != 1)\n+\t\tdie(\"only one bitmap can be tested at a time\");\n+\n+\tfprintf(stderr, \"Bitmap v%d test (%d entries loaded)\\n\",\n+\t\tbitmap_git.version, bitmap_git.entry_count);\n+\n+\troot = revs->pending.objects[0].item;\n+\tpos = kh_get_sha1(bitmap_git.bitmaps, root->sha1);\n+\n+\tif (pos < kh_end(bitmap_git.bitmaps)) {\n+\t\tstruct stored_bitmap *st = kh_value(bitmap_git.bitmaps, pos);\n+\t\tstruct ewah_bitmap *bm = lookup_stored_bitmap(st);\n+\n+\t\tfprintf(stderr, \"Found bitmap for %s. %d bits / %08x checksum\\n\",\n+\t\t\tsha1_to_hex(root->sha1), (int)bm->bit_size, ewah_checksum(bm));\n+\n+\t\tresult = ewah_to_bitmap(bm);\n+\t}\n+\n+\tif (result == NULL)\n+\t\tdie(\"Commit %s doesn't have an indexed bitmap\", sha1_to_hex(root->sha1));\n+\n+\trevs->tag_objects = 1;\n+\trevs->tree_objects = 1;\n+\trevs->blob_objects = 1;\n+\n+\tresult_popcnt = bitmap_popcount(result);\n+\n+\tif (prepare_revision_walk(revs))\n+\t\tdie(\"revision walk setup failed\");\n+\n+\ttdata.base = bitmap_new();\n+\ttdata.prg = start_progress(\"Verifying bitmap entries\", result_popcnt);\n+\ttdata.seen = 0;\n+\n+\ttraverse_commit_list(revs, &test_show_commit, &test_show_object, &tdata);\n+\n+\tstop_progress(&tdata.prg);\n+\n+\tif (bitmap_equals(result, tdata.base))\n+\t\tfprintf(stderr, \"OK!\\n\");\n+\telse\n+\t\tfprintf(stderr, \"Mismatch!\\n\");\n+}\ndiff --git a/pack-bitmap.h b/pack-bitmap.h\nnew file mode 100644\nindex 0000000..df45578\n--- /dev/null\n+++ b/pack-bitmap.h\n@@ -0,0 +1,46 @@\n+#ifndef PACK_BITMAP_H\n+#define PACK_BITMAP_H\n+\n+#define ewah_malloc xmalloc\n+#define ewah_calloc xcalloc\n+#define ewah_realloc xrealloc\n+#include \"ewah/ewok.h\"\n+#include \"khash.h\"\n+\n+struct bitmap_disk_entry {\n+\tuint32_t object_pos;\n+\tuint8_t xor_offset;\n+\tuint8_t flags;\n+} __attribute__((packed));\n+\n+struct bitmap_disk_header {\n+\tchar magic[4];\n+\tuint16_t version;\n+\tuint16_t options;\n+\tuint32_t entry_count;\n+\tunsigned char checksum[20];\n+};\n+\n+static const char BITMAP_IDX_SIGNATURE[] = {'B', 'I', 'T', 'M'};;\n+\n+enum pack_bitmap_opts {\n+\tBITMAP_OPT_FULL_DAG = 1,\n+};\n+\n+typedef int (*show_reachable_fn)(\n+\tconst unsigned char *sha1,\n+\tenum object_type type,\n+\tint flags,\n+\tuint32_t hash,\n+\tstruct packed_git *found_pack,\n+\toff_t found_offset);\n+\n+int prepare_bitmap_git(void);\n+void count_bitmap_commit_list(uint32_t *commits, uint32_t *trees, uint32_t *blobs, uint32_t *tags);\n+void traverse_bitmap_commit_list(show_reachable_fn show_reachable);\n+void test_bitmap_walk(struct rev_info *revs);\n+char *pack_bitmap_filename(struct packed_git *p);\n+int prepare_bitmap_walk(struct rev_info *revs);\n+int reuse_partial_packfile_from_bitmap(struct packed_git **packfile, uint32_t *entries, off_t *up_to);\n+\n+#endif\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229412","messageId":"20131024180419.GK24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 11/19] pack-objects: use bitmaps when packing objects","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:04:19Z","receivedAt":"2013-10-24T18:04:19Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nIn this patch, we use the bitmap API to perform the `Counting Objects`\nphase in pack-objects, rather than a traditional walk through the object\ngraph. For a reasonably-packed large repo, the time to fetch and clone\nis often dominated by the full-object revision walk during the Counting\nObjects phase. Using bitmaps can reduce the CPU time required on the\nserver (and therefore start sending the actual pack data with less\ndelay).\n\nFor bitmaps to be used, the following must be true:\n\n  1. We must be packing to stdout (as a normal `pack-objects` from\n     `upload-pack` would do).\n\n  2. There must be a .bitmap index containing at least one of the\n     \"have\" objects that the client is asking for.\n\n  3. Bitmaps must be enabled (they are enabled by default, but can be\n     disabled by setting `pack.usebitmaps` to false, or by using\n     `--no-use-bitmap-index` on the command-line).\n\nIf any of these is not true, we fall back to doing a normal walk of the\nobject graph.\n\nHere are some sample timings from a full pack of `torvalds/linux` (i.e.\nsomething very similar to what would be generated for a clone of the\nrepository) that show the speedup produced by various\nmethods:\n\n    [existing graph traversal]\n    $ time git pack-objects --all --stdout --no-use-bitmap-index \\\n\t\t\t    </dev/null >/dev/null\n    Counting objects: 3237103, done.\n    Compressing objects: 100% (508752/508752), done.\n    Total 3237103 (delta 2699584), reused 3237103 (delta 2699584)\n\n    real    0m44.111s\n    user    0m42.396s\n    sys     0m3.544s\n\n    [bitmaps only, without partial pack reuse; note that\n     pack reuse is automatic, so timing this required a\n     patch to disable it]\n    $ time git pack-objects --all --stdout </dev/null >/dev/null\n    Counting objects: 3237103, done.\n    Compressing objects: 100% (508752/508752), done.\n    Total 3237103 (delta 2699584), reused 3237103 (delta 2699584)\n\n    real    0m5.413s\n    user    0m5.604s\n    sys     0m1.804s\n\n    [bitmaps with pack reuse (what you get with this patch)]\n    $ time git pack-objects --all --stdout </dev/null >/dev/null\n    Reusing existing pack: 3237103, done.\n    Total 3237103 (delta 0), reused 0 (delta 0)\n\n    real    0m1.636s\n    user    0m1.460s\n    sys     0m0.172s\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/pack-objects.c | 169 +++++++++++++++++++++++++++++++++++++++++--------\n 1 file changed, 144 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex faf746b..d15b9db 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -19,6 +19,7 @@\n #include \"refs.h\"\n #include \"streaming.h\"\n #include \"thread-utils.h\"\n+#include \"pack-bitmap.h\"\n \n static const char *pack_usage[] = {\n \tN_(\"git pack-objects --stdout [options...] [< ref-list | < object-list]\"),\n@@ -57,12 +58,23 @@ static struct progress *progress_state;\n static int pack_compression_level = Z_DEFAULT_COMPRESSION;\n static int pack_compression_seen;\n \n+static struct packed_git *reuse_packfile;\n+static uint32_t reuse_packfile_objects;\n+static off_t reuse_packfile_offset;\n+\n+static int use_bitmap_index = 1;\n+\n static unsigned long delta_cache_size = 0;\n static unsigned long max_delta_cache_size = 256 * 1024 * 1024;\n static unsigned long cache_max_small_delta_size = 1000;\n \n static unsigned long window_memory_limit = 0;\n \n+enum {\n+\tOBJECT_ENTRY_EXCLUDE = (1 << 0),\n+\tOBJECT_ENTRY_NO_TRY_DELTA = (1 << 1)\n+};\n+\n /*\n  * stats\n  */\n@@ -678,6 +690,49 @@ static struct object_entry **compute_write_order(void)\n \treturn wo;\n }\n \n+static off_t write_reused_pack(struct sha1file *f)\n+{\n+\tuint8_t buffer[8192];\n+\toff_t to_write;\n+\tint fd;\n+\n+\tif (!is_pack_valid(reuse_packfile))\n+\t\treturn 0;\n+\n+\tfd = git_open_noatime(reuse_packfile->pack_name);\n+\tif (fd < 0)\n+\t\treturn 0;\n+\n+\tif (lseek(fd, sizeof(struct pack_header), SEEK_SET) == -1) {\n+\t\tclose(fd);\n+\t\treturn 0;\n+\t}\n+\n+\tif (reuse_packfile_offset < 0)\n+\t\treuse_packfile_offset = reuse_packfile->pack_size - 20;\n+\n+\tto_write = reuse_packfile_offset - sizeof(struct pack_header);\n+\n+\twhile (to_write) {\n+\t\tint read_pack = xread(fd, buffer, sizeof(buffer));\n+\n+\t\tif (read_pack <= 0) {\n+\t\t\tclose(fd);\n+\t\t\treturn 0;\n+\t\t}\n+\n+\t\tif (read_pack > to_write)\n+\t\t\tread_pack = to_write;\n+\n+\t\tsha1write(f, buffer, read_pack);\n+\t\tto_write -= read_pack;\n+\t}\n+\n+\tclose(fd);\n+\twritten += reuse_packfile_objects;\n+\treturn reuse_packfile_offset - sizeof(struct pack_header);\n+}\n+\n static void write_pack_file(void)\n {\n \tuint32_t i = 0, j;\n@@ -704,6 +759,18 @@ static void write_pack_file(void)\n \t\toffset = write_pack_header(f, nr_remaining);\n \t\tif (!offset)\n \t\t\tdie_errno(\"unable to write pack header\");\n+\n+\t\tif (reuse_packfile) {\n+\t\t\toff_t packfile_size;\n+\t\t\tassert(pack_to_stdout);\n+\n+\t\t\tpackfile_size = write_reused_pack(f);\n+\t\t\tif (!packfile_size)\n+\t\t\t\tdie_errno(\"failed to re-use existing pack\");\n+\n+\t\t\toffset += packfile_size;\n+\t\t}\n+\n \t\tnr_written = 0;\n \t\tfor (; i < to_pack.nr_objects; i++) {\n \t\t\tstruct object_entry *e = write_order[i];\n@@ -800,14 +867,14 @@ static int no_try_delta(const char *path)\n \treturn 0;\n }\n \n-static int add_object_entry(const unsigned char *sha1, enum object_type type,\n-\t\t\t    const char *name, int exclude)\n+static int add_object_entry_1(const unsigned char *sha1, enum object_type type,\n+\t\t\t      int flags, uint32_t name_hash,\n+\t\t\t      struct packed_git *found_pack, off_t found_offset)\n {\n \tstruct object_entry *entry;\n-\tstruct packed_git *p, *found_pack = NULL;\n-\toff_t found_offset = 0;\n-\tuint32_t hash = pack_name_hash(name);\n+\tstruct packed_git *p;\n \tuint32_t index_pos;\n+\tint exclude = (flags & OBJECT_ENTRY_EXCLUDE);\n \n \tentry = packlist_find(&to_pack, sha1, &index_pos);\n \tif (entry) {\n@@ -822,36 +889,42 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,\n \tif (!exclude && local && has_loose_object_nonlocal(sha1))\n \t\treturn 0;\n \n-\tfor (p = packed_git; p; p = p->next) {\n-\t\toff_t offset = find_pack_entry_one(sha1, p);\n-\t\tif (offset) {\n-\t\t\tif (!found_pack) {\n-\t\t\t\tif (!is_pack_valid(p)) {\n-\t\t\t\t\twarning(\"packfile %s cannot be accessed\", p->pack_name);\n-\t\t\t\t\tcontinue;\n+\tif (!found_pack) {\n+\t\tfor (p = packed_git; p; p = p->next) {\n+\t\t\toff_t offset = find_pack_entry_one(sha1, p);\n+\t\t\tif (offset) {\n+\t\t\t\tif (!found_pack) {\n+\t\t\t\t\tif (!is_pack_valid(p)) {\n+\t\t\t\t\t\twarning(\"packfile %s cannot be accessed\", p->pack_name);\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\t}\n+\t\t\t\t\tfound_offset = offset;\n+\t\t\t\t\tfound_pack = p;\n \t\t\t\t}\n-\t\t\t\tfound_offset = offset;\n-\t\t\t\tfound_pack = p;\n+\t\t\t\tif (exclude)\n+\t\t\t\t\tbreak;\n+\t\t\t\tif (incremental)\n+\t\t\t\t\treturn 0;\n+\t\t\t\tif (local && !p->pack_local)\n+\t\t\t\t\treturn 0;\n+\t\t\t\tif (ignore_packed_keep && p->pack_local && p->pack_keep)\n+\t\t\t\t\treturn 0;\n \t\t\t}\n-\t\t\tif (exclude)\n-\t\t\t\tbreak;\n-\t\t\tif (incremental)\n-\t\t\t\treturn 0;\n-\t\t\tif (local && !p->pack_local)\n-\t\t\t\treturn 0;\n-\t\t\tif (ignore_packed_keep && p->pack_local && p->pack_keep)\n-\t\t\t\treturn 0;\n \t\t}\n \t}\n \n \tentry = packlist_alloc(&to_pack, sha1, index_pos);\n-\tentry->hash = hash;\n+\tentry->hash = name_hash;\n \tif (type)\n \t\tentry->type = type;\n \tif (exclude)\n \t\tentry->preferred_base = 1;\n \telse\n \t\tnr_result++;\n+\n+\tif (flags & OBJECT_ENTRY_NO_TRY_DELTA)\n+\t\tentry->no_try_delta = 1;\n+\n \tif (found_pack) {\n \t\tentry->in_pack = found_pack;\n \t\tentry->in_pack_offset = found_offset;\n@@ -859,10 +932,21 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,\n \n \tdisplay_progress(progress_state, to_pack.nr_objects);\n \n+\treturn 1;\n+}\n+\n+static int add_object_entry(const unsigned char *sha1, enum object_type type,\n+\t\t\t    const char *name, int exclude)\n+{\n+\tint flags = 0;\n+\n+\tif (exclude)\n+\t\tflags |= OBJECT_ENTRY_EXCLUDE;\n+\n \tif (name && no_try_delta(name))\n-\t\tentry->no_try_delta = 1;\n+\t\tflags |= OBJECT_ENTRY_NO_TRY_DELTA;\n \n-\treturn 1;\n+\treturn add_object_entry_1(sha1, type, flags, pack_name_hash(name), NULL, 0);\n }\n \n struct pbase_tree_cache {\n@@ -2027,6 +2111,10 @@ static int git_pack_config(const char *k, const char *v, void *cb)\n \t\tcache_max_small_delta_size = git_config_int(k, v);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(k, \"pack.usebitmaps\")) {\n+\t\tuse_bitmap_index = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(k, \"pack.threads\")) {\n \t\tdelta_search_threads = git_config_int(k, v);\n \t\tif (delta_search_threads < 0)\n@@ -2235,6 +2323,29 @@ static void loosen_unused_packed_objects(struct rev_info *revs)\n \t}\n }\n \n+static int get_object_list_from_bitmap(struct rev_info *revs)\n+{\n+\tif (prepare_bitmap_walk(revs) < 0)\n+\t\treturn -1;\n+\n+\tif (!reuse_partial_packfile_from_bitmap(\n+\t\t\t&reuse_packfile,\n+\t\t\t&reuse_packfile_objects,\n+\t\t\t&reuse_packfile_offset)) {\n+\t\tassert(reuse_packfile_objects);\n+\t\tnr_result += reuse_packfile_objects;\n+\n+\t\tif (progress) {\n+\t\t\tfprintf(stderr, \"Reusing existing pack: %d, done.\\n\",\n+\t\t\t\treuse_packfile_objects);\n+\t\t\tfflush(stderr);\n+\t\t}\n+\t}\n+\n+\ttraverse_bitmap_commit_list(&add_object_entry_1);\n+\treturn 0;\n+}\n+\n static void get_object_list(int ac, const char **av)\n {\n \tstruct rev_info revs;\n@@ -2262,6 +2373,9 @@ static void get_object_list(int ac, const char **av)\n \t\t\tdie(\"bad revision '%s'\", line);\n \t}\n \n+\tif (use_bitmap_index && !get_object_list_from_bitmap(&revs))\n+\t\treturn;\n+\n \tif (prepare_revision_walk(&revs))\n \t\tdie(\"revision walk setup failed\");\n \tmark_edges_uninteresting(&revs, show_edge);\n@@ -2391,6 +2505,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t    N_(\"pack compression level\")),\n \t\tOPT_SET_INT(0, \"keep-true-parents\", &grafts_replace_parents,\n \t\t\t    N_(\"do not hide commits by grafts\"), 0),\n+\t\tOPT_BOOL(0, \"use-bitmap-index\", &use_bitmap_index,\n+\t\t\t N_(\"use a bitmap index if available to speed up counting objects\")),\n \t\tOPT_END(),\n \t};\n \n@@ -2457,6 +2573,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \tif (keep_unreachable && unpack_unreachable)\n \t\tdie(\"--keep-unreachable and --unpack-unreachable are incompatible.\");\n \n+\tif (!use_internal_rev_list || !pack_to_stdout)\n+\t\tuse_bitmap_index = 0;\n+\n \tif (progress && all_progress_implied)\n \t\tprogress = 2;\n \n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229413","messageId":"20131024180627.GL24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 12/19] rev-list: add bitmap mode to speed up object lists","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:06:27Z","receivedAt":"2013-10-24T18:06:27Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThe bitmap reachability index used to speed up the counting objects\nphase during `pack-objects` can also be used to optimize a normal\nrev-list if the only thing required are the SHA1s of the objects during\nthe list (i.e., not the path names at which trees and blobs were found).\n\nCalling `git rev-list --objects --use-bitmap-index [committish]` will\nperform an object iteration based on a bitmap result instead of actually\nwalking the object graph.\n\nThese are some example timings for `torvalds/linux` (warm cache,\nbest-of-five):\n\n    $ time git rev-list --objects master > /dev/null\n\n    real    0m34.191s\n    user    0m33.904s\n    sys     0m0.268s\n\n    $ time git rev-list --objects --use-bitmap-index master > /dev/null\n\n    real    0m1.041s\n    user    0m0.976s\n    sys     0m0.064s\n\nLikewise, using `git rev-list --count --use-bitmap-index` will speed up\nthe counting operation by building the resulting bitmap and performing a\nfast popcount (number of bits set on the bitmap) on the result.\n\nHere are some sample timings of different ways to count commits in\n`torvalds/linux`:\n\n    $ time git rev-list master | wc -l\n        399882\n\n        real    0m6.524s\n        user    0m6.060s\n        sys     0m3.284s\n\n    $ time git rev-list --count master\n        399882\n\n        real    0m4.318s\n        user    0m4.236s\n        sys     0m0.076s\n\n    $ time git rev-list --use-bitmap-index --count master\n        399882\n\n        real    0m0.217s\n        user    0m0.176s\n        sys     0m0.040s\n\nThis also respects negative refs, so you can use it to count\na slice of history:\n\n        $ time git rev-list --count v3.0..master\n        144843\n\n        real    0m1.971s\n        user    0m1.932s\n        sys     0m0.036s\n\n        $ time git rev-list --use-bitmap-index --count v3.0..master\n        real    0m0.280s\n        user    0m0.220s\n        sys     0m0.056s\n\nThough note that the closer the endpoints, the less it helps. In the\ntraversal case, we have fewer commits to cross, so we take less time.\nBut the bitmap time is dominated by generating the pack revindex, which\nis constant with respect to the refs given.\n\nNote that you cannot yet get a fast --left-right count of a symmetric\ndifference (e.g., \"--count --left-right master...topic\"). The slow part\nof that walk actually happens during the merge-base determination when\nwe parse \"master...topic\". Even though a count does not actually need to\nknow the real merge base (it only needs to take the symmetric difference\nof the bitmaps), the revision code would require some refactoring to\nhandle this case.\n\nAdditionally, a `--test-bitmap` flag has been added that will perform\nthe same rev-list manually (i.e. using a normal revwalk) and using\nbitmaps, and verify that the results are the same. This can be used to\nexercise the bitmap code, and also to verify that the contents of the\n.bitmap file are sane.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\nNote that most of the time we spend for --count invocations is on\ngenerating the pack revindex. It may be worth storing a revidx (either\nin a separate file, as part of the .idx, or as an optional section in\nthe .bitmap file).\n\n builtin/rev-list.c | 39 +++++++++++++++++++++++++++++++++++++++\n pack-bitmap.c      |  2 +-\n 2 files changed, 40 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 4fc1616..5209255 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -3,6 +3,8 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"list-objects.h\"\n+#include \"pack.h\"\n+#include \"pack-bitmap.h\"\n #include \"builtin.h\"\n #include \"log-tree.h\"\n #include \"graph.h\"\n@@ -257,6 +259,18 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)\n \treturn 0;\n }\n \n+static int show_object_fast(\n+\tconst unsigned char *sha1,\n+\tenum object_type type,\n+\tint exclude,\n+\tuint32_t name_hash,\n+\tstruct packed_git *found_pack,\n+\toff_t found_offset)\n+{\n+\tfprintf(stdout, \"%s\\n\", sha1_to_hex(sha1));\n+\treturn 1;\n+}\n+\n int cmd_rev_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct rev_info revs;\n@@ -265,6 +279,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tint bisect_list = 0;\n \tint bisect_show_vars = 0;\n \tint bisect_find_all = 0;\n+\tint use_bitmap_index = 0;\n \n \tgit_config(git_default_config, NULL);\n \tinit_revisions(&revs, prefix);\n@@ -306,6 +321,14 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \t\t\tbisect_show_vars = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--use-bitmap-index\")) {\n+\t\t\tuse_bitmap_index = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--test-bitmap\")) {\n+\t\t\ttest_bitmap_walk(&revs);\n+\t\t\treturn 0;\n+\t\t}\n \t\tusage(rev_list_usage);\n \n \t}\n@@ -333,6 +356,22 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tif (bisect_list)\n \t\trevs.limited = 1;\n \n+\tif (use_bitmap_index) {\n+\t\tif (revs.count && !revs.left_right && !revs.cherry_mark) {\n+\t\t\tuint32_t commit_count;\n+\t\t\tif (!prepare_bitmap_walk(&revs)) {\n+\t\t\t\tcount_bitmap_commit_list(&commit_count, NULL, NULL, NULL);\n+\t\t\t\tprintf(\"%d\\n\", commit_count);\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t} else if (revs.tag_objects && revs.tree_objects && revs.blob_objects) {\n+\t\t\tif (!prepare_bitmap_walk(&revs)) {\n+\t\t\t\ttraverse_bitmap_commit_list(&show_object_fast);\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n \tif (prepare_revision_walk(&revs))\n \t\tdie(\"revision walk setup failed\");\n \tif (revs.tree_objects)\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex b800736..cec56b2 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -920,7 +920,7 @@ void test_bitmap_walk(struct rev_info *revs)\n \t\tdie(\"failed to load bitmap indexes\");\n \n \tif (revs->pending.nr != 1)\n-\t\tdie(\"only one bitmap can be tested at a time\");\n+\t\tdie(\"you must specify exactly one commit to test\");\n \n \tfprintf(stderr, \"Bitmap v%d test (%d entries loaded)\\n\",\n \t\tbitmap_git.version, bitmap_git.entry_count);\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229414","messageId":"20131024180642.GM24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 13/19] pack-objects: implement bitmap writing","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:06:42Z","receivedAt":"2013-10-24T18:06:42Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThis commit extends more the functionality of `pack-objects` by allowing\nit to write out a `.bitmap` index next to any written packs, together\nwith the `.idx` index that currently gets written.\n\nIf bitmap writing is enabled for a given repository (either by calling\n`pack-objects` with the `--write-bitmap-index` flag or by having\n`pack.writebitmaps` set to `true` in the config) and pack-objects is\nwriting a packfile that would normally be indexed (i.e. not piping to\nstdout), we will attempt to write the corresponding bitmap index for the\npackfile.\n\nBitmap index writing happens after the packfile and its index has been\nsuccessfully written to disk (`finish_tmp_packfile`). The process is\nperformed in several steps:\n\n    1. `bitmap_writer_set_checksum`: this call stores the partial\n       checksum for the packfile being written; the checksum will be\n       written in the resulting bitmap index to verify its integrity\n\n    2. `bitmap_writer_build_type_index`: this call uses the array of\n       `struct object_entry` that has just been sorted when writing out\n       the actual packfile index to disk to generate 4 type-index bitmaps\n       (one for each object type).\n\n       These bitmaps have their nth bit set if the given object is of\n       the bitmap's type. E.g. the nth bit of the Commits bitmap will be\n       1 if the nth object in the packfile index is a commit.\n\n       This is a very cheap operation because the bitmap writing code has\n       access to the metadata stored in the `struct object_entry` array,\n       and hence the real type for each object in the packfile.\n\n    3. `bitmap_writer_reuse_bitmaps`: if there exists an existing bitmap\n       index for one of the packfiles we're trying to repack, this call\n       will efficiently rebuild the existing bitmaps so they can be\n       reused on the new index. All the existing bitmaps will be stored\n       in a `reuse` hash table, and the commit selection phase will\n       prioritize these when selecting, as they can be written directly\n       to the new index without having to perform a revision walk to\n       fill the bitmap. This can greatly speed up the repack of a\n       repository that already has bitmaps.\n\n    4. `bitmap_writer_select_commits`: if bitmap writing is enabled for\n       a given `pack-objects` run, the sequence of commits generated\n       during the Counting Objects phase will be stored in an array.\n\n       We then use that array to build up the list of selected commits.\n       Writing a bitmap in the index for each object in the repository\n       would be cost-prohibitive, so we use a simple heuristic to pick\n       the commits that will be indexed with bitmaps.\n\n       The current heuristics are a simplified version of JGit's\n       original implementation. We select a higher density of commits\n       depending on their age: the 100 most recent commits are always\n       selected, after that we pick 1 commit of each 100, and the gap\n       increases as the commits grow older. On top of that, we make sure\n       that every single branch that has not been merged (all the tips\n       that would be required from a clone) gets their own bitmap, and\n       when selecting commits between a gap, we tend to prioritize the\n       commit with the most parents.\n\n       Do note that there is no right/wrong way to perform commit\n       selection; different selection algorithms will result in\n       different commits being selected, but there's no such thing as\n       \"missing a commit\". The bitmap walker algorithm implemented in\n       `prepare_bitmap_walk` is able to adapt to missing bitmaps by\n       performing manual walks that complete the bitmap: the ideal\n       selection algorithm, however, would select the commits that are\n       more likely to be used as roots for a walk in the future (e.g.\n       the tips of each branch, and so on) to ensure a bitmap for them\n       is always available.\n\n    5. `bitmap_writer_build`: this is the computationally expensive part\n       of bitmap generation. Based on the list of commits that were\n       selected in the previous step, we perform several incremental\n       walks to generate the bitmap for each commit.\n\n       The walks begin from the oldest commit, and are built up\n       incrementally for each branch. E.g. consider this dag where A, B,\n       C, D, E, F are the selected commits, and a, b, c, e are a chunk\n       of simplified history that will not receive bitmaps.\n\n            A---a---B--b--C--c--D\n                     \\\n                      E--e--F\n\n       We start by building the bitmap for A, using A as the root for a\n       revision walk and marking all the objects that are reachable\n       until the walk is over. Once this bitmap is stored, we reuse the\n       bitmap walker to perform the walk for B, assuming that once we\n       reach A again, the walk will be terminated because A has already\n       been SEEN on the previous walk.\n\n       This process is repeated for C, and D, but when we try to\n       generate the bitmaps for E, we can reuse neither the current walk\n       nor the bitmap we have generated so far.\n\n       What we do now is resetting both the walk and clearing the\n       bitmap, and performing the walk from scratch using E as the\n       origin. This new walk, however, does not need to be completed.\n       Once we hit B, we can lookup the bitmap we have already stored\n       for that commit and OR it with the existing bitmap we've composed\n       so far, allowing us to limit the walk early.\n\n       After all the bitmaps have been generated, another iteration\n       through the list of commits is performed to find the best XOR\n       offsets for compression before writing them to disk. Because of\n       the incremental nature of these bitmaps, XORing one of them with\n       its predecesor results in a minimal \"bitmap delta\" most of the\n       time. We can write this delta to the on-disk bitmap index, and\n       then re-compose the original bitmaps by XORing them again when\n       loaded.\n\n       This is a phase very similar to pack-object's `find_delta` (using\n       bitmaps instead of objects, of course), except the heuristics\n       have been greatly simplified: we only check the 10 bitmaps before\n       any given one to find best compressing one. This gives good\n       results in practice, because there is locality in the ordering of\n       the objects (and therefore bitmaps) in the packfile.\n\n     6. `bitmap_writer_finish`: the last step in the process is\n\tserializing to disk all the bitmap data that has been generated\n\tin the two previous steps.\n\n\tThe bitmap is written to a tmp file and then moved atomically to\n\tits final destination, using the same process as\n\t`pack-write.c:write_idx_file`.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Makefile               |   1 +\n builtin/pack-objects.c |  53 +++++\n pack-bitmap-write.c    | 537 +++++++++++++++++++++++++++++++++++++++++++++++++\n pack-bitmap.c          |  92 +++++++++\n pack-bitmap.h          |  18 ++\n pack-objects.h         |   1 +\n pack-write.c           |   2 +\n 7 files changed, 704 insertions(+)\n create mode 100644 pack-bitmap-write.c\n\ndiff --git a/Makefile b/Makefile\nindex 737766c..9ece13b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -839,6 +839,7 @@ LIB_OBJS += notes-merge.o\n LIB_OBJS += notes-utils.o\n LIB_OBJS += object.o\n LIB_OBJS += pack-bitmap.o\n+LIB_OBJS += pack-bitmap-write.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-objects.o\n LIB_OBJS += pack-revindex.o\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex d15b9db..423e85a 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -63,6 +63,7 @@ static uint32_t reuse_packfile_objects;\n static off_t reuse_packfile_offset;\n \n static int use_bitmap_index = 1;\n+static int write_bitmap_index;\n \n static unsigned long delta_cache_size = 0;\n static unsigned long max_delta_cache_size = 256 * 1024 * 1024;\n@@ -81,6 +82,24 @@ enum {\n static uint32_t written, written_delta;\n static uint32_t reused, reused_delta;\n \n+/*\n+ * Indexed commits\n+ */\n+struct commit **indexed_commits;\n+unsigned int indexed_commits_nr;\n+unsigned int indexed_commits_alloc;\n+\n+static void index_commit_for_bitmap(struct commit *commit)\n+{\n+\tif (indexed_commits_nr >= indexed_commits_alloc) {\n+\t\tindexed_commits_alloc = (indexed_commits_alloc + 32) * 2;\n+\t\tindexed_commits = xrealloc(indexed_commits,\n+\t\t\tindexed_commits_alloc * sizeof(struct commit *));\n+\t}\n+\n+\tindexed_commits[indexed_commits_nr++] = commit;\n+}\n+\n static void *get_delta(struct object_entry *entry)\n {\n \tunsigned long size, base_size, delta_size;\n@@ -823,9 +842,30 @@ static void write_pack_file(void)\n \t\t\tif (sizeof(tmpname) <= strlen(base_name) + 50)\n \t\t\t\tdie(\"pack base name '%s' too long\", base_name);\n \t\t\tsnprintf(tmpname, sizeof(tmpname), \"%s-\", base_name);\n+\n+\t\t\tif (write_bitmap_index) {\n+\t\t\t\tbitmap_writer_set_checksum(sha1);\n+\t\t\t\tbitmap_writer_build_type_index(written_list, nr_written);\n+\t\t\t}\n+\n \t\t\tfinish_tmp_packfile(tmpname, pack_tmp_name,\n \t\t\t\t\t    written_list, nr_written,\n \t\t\t\t\t    &pack_idx_opts, sha1);\n+\n+\t\t\tif (write_bitmap_index) {\n+\t\t\t\tchar *end_of_name_prefix = strrchr(tmpname, 0);\n+\t\t\t\tsprintf(end_of_name_prefix, \"%s.bitmap\", sha1_to_hex(sha1));\n+\n+\t\t\t\tstop_progress(&progress_state);\n+\n+\t\t\t\tbitmap_writer_show_progress(progress);\n+\t\t\t\tbitmap_writer_reuse_bitmaps(&to_pack);\n+\t\t\t\tbitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1);\n+\t\t\t\tbitmap_writer_build(&to_pack);\n+\t\t\t\tbitmap_writer_finish(written_list, nr_written, tmpname);\n+\t\t\t\twrite_bitmap_index = 0;\n+\t\t\t}\n+\n \t\t\tfree(pack_tmp_name);\n \t\t\tputs(sha1_to_hex(sha1));\n \t\t}\n@@ -2111,6 +2151,10 @@ static int git_pack_config(const char *k, const char *v, void *cb)\n \t\tcache_max_small_delta_size = git_config_int(k, v);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(k, \"pack.writebitmaps\")) {\n+\t\twrite_bitmap_index = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(k, \"pack.usebitmaps\")) {\n \t\tuse_bitmap_index = git_config_bool(k, v);\n \t\treturn 0;\n@@ -2173,6 +2217,9 @@ static void show_commit(struct commit *commit, void *data)\n {\n \tadd_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);\n \tcommit->object.flags |= OBJECT_ADDED;\n+\n+\tif (write_bitmap_index)\n+\t\tindex_commit_for_bitmap(commit);\n }\n \n static void show_object(struct object *obj,\n@@ -2365,6 +2412,7 @@ static void get_object_list(int ac, const char **av)\n \t\tif (*line == '-') {\n \t\t\tif (!strcmp(line, \"--not\")) {\n \t\t\t\tflags ^= UNINTERESTING;\n+\t\t\t\twrite_bitmap_index = 0;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tdie(\"not a rev '%s'\", line);\n@@ -2507,6 +2555,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t    N_(\"do not hide commits by grafts\"), 0),\n \t\tOPT_BOOL(0, \"use-bitmap-index\", &use_bitmap_index,\n \t\t\t N_(\"use a bitmap index if available to speed up counting objects\")),\n+\t\tOPT_BOOL(0, \"write-bitmap-index\", &write_bitmap_index,\n+\t\t\t N_(\"write a bitmap index together with the pack index\")),\n \t\tOPT_END(),\n \t};\n \n@@ -2576,6 +2626,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \tif (!use_internal_rev_list || !pack_to_stdout)\n \t\tuse_bitmap_index = 0;\n \n+\tif (pack_to_stdout || !rev_list_all)\n+\t\twrite_bitmap_index = 0;\n+\n \tif (progress && all_progress_implied)\n \t\tprogress = 2;\n \ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nnew file mode 100644\nindex 0000000..6a589c3\n--- /dev/null\n+++ b/pack-bitmap-write.c\n@@ -0,0 +1,537 @@\n+#include <stdlib.h>\n+\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"tag.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n+#include \"list-objects.h\"\n+#include \"progress.h\"\n+#include \"pack-revindex.h\"\n+#include \"pack.h\"\n+#include \"pack-bitmap.h\"\n+#include \"sha1-lookup.h\"\n+#include \"pack-objects.h\"\n+\n+struct bitmapped_commit {\n+\tstruct commit *commit;\n+\tstruct ewah_bitmap *bitmap;\n+\tstruct ewah_bitmap *write_as;\n+\tint flags;\n+\tint xor_offset;\n+\tuint32_t commit_pos;\n+};\n+\n+struct bitmap_writer {\n+\tstruct ewah_bitmap *commits;\n+\tstruct ewah_bitmap *trees;\n+\tstruct ewah_bitmap *blobs;\n+\tstruct ewah_bitmap *tags;\n+\n+\tkhash_sha1 *bitmaps;\n+\tkhash_sha1 *reused;\n+\tstruct packing_data *to_pack;\n+\n+\tstruct bitmapped_commit *selected;\n+\tunsigned int selected_nr, selected_alloc;\n+\n+\tstruct progress *progress;\n+\tint show_progress;\n+\tunsigned char pack_checksum[20];\n+};\n+\n+static struct bitmap_writer writer;\n+\n+void bitmap_writer_show_progress(int show)\n+{\n+\twriter.show_progress = show;\n+}\n+\n+/**\n+ * Build the initial type index for the packfile\n+ */\n+void bitmap_writer_build_type_index(struct pack_idx_entry **index,\n+\t\t\t\t    uint32_t index_nr)\n+{\n+\tuint32_t i;\n+\n+\twriter.commits = ewah_new();\n+\twriter.trees = ewah_new();\n+\twriter.blobs = ewah_new();\n+\twriter.tags = ewah_new();\n+\n+\tfor (i = 0; i < index_nr; ++i) {\n+\t\tstruct object_entry *entry = (struct object_entry *)index[i];\n+\t\tenum object_type real_type;\n+\n+\t\tentry->in_pack_pos = i;\n+\n+\t\tswitch (entry->type) {\n+\t\tcase OBJ_COMMIT:\n+\t\tcase OBJ_TREE:\n+\t\tcase OBJ_BLOB:\n+\t\tcase OBJ_TAG:\n+\t\t\treal_type = entry->type;\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\treal_type = sha1_object_info(entry->idx.sha1, NULL);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tswitch (real_type) {\n+\t\tcase OBJ_COMMIT:\n+\t\t\tewah_set(writer.commits, i);\n+\t\t\tbreak;\n+\n+\t\tcase OBJ_TREE:\n+\t\t\tewah_set(writer.trees, i);\n+\t\t\tbreak;\n+\n+\t\tcase OBJ_BLOB:\n+\t\t\tewah_set(writer.blobs, i);\n+\t\t\tbreak;\n+\n+\t\tcase OBJ_TAG:\n+\t\t\tewah_set(writer.tags, i);\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tdie(\"Missing type information for %s (%d/%d)\",\n+\t\t\t    sha1_to_hex(entry->idx.sha1), real_type, entry->type);\n+\t\t}\n+\t}\n+}\n+\n+/**\n+ * Compute the actual bitmaps\n+ */\n+static struct object **seen_objects;\n+static unsigned int seen_objects_nr, seen_objects_alloc;\n+\n+static inline void push_bitmapped_commit(struct commit *commit, struct ewah_bitmap *reused)\n+{\n+\tif (writer.selected_nr >= writer.selected_alloc) {\n+\t\twriter.selected_alloc = (writer.selected_alloc + 32) * 2;\n+\t\twriter.selected = xrealloc(writer.selected,\n+\t\t\t\t\t   writer.selected_alloc * sizeof(struct bitmapped_commit));\n+\t}\n+\n+\twriter.selected[writer.selected_nr].commit = commit;\n+\twriter.selected[writer.selected_nr].bitmap = reused;\n+\twriter.selected[writer.selected_nr].flags = 0;\n+\n+\twriter.selected_nr++;\n+}\n+\n+static inline void mark_as_seen(struct object *object)\n+{\n+\tALLOC_GROW(seen_objects, seen_objects_nr + 1, seen_objects_alloc);\n+\tseen_objects[seen_objects_nr++] = object;\n+}\n+\n+static inline void reset_all_seen(void)\n+{\n+\tunsigned int i;\n+\tfor (i = 0; i < seen_objects_nr; ++i) {\n+\t\tseen_objects[i]->flags &= ~(SEEN | ADDED | SHOWN);\n+\t}\n+\tseen_objects_nr = 0;\n+}\n+\n+static uint32_t find_object_pos(const unsigned char *sha1)\n+{\n+\tstruct object_entry *entry = packlist_find(writer.to_pack, sha1, NULL);\n+\n+\tif (!entry) {\n+\t\tdie(\"Failed to write bitmap index. Packfile doesn't have full closure \"\n+\t\t\t\"(object %s is missing)\", sha1_to_hex(sha1));\n+\t}\n+\n+\treturn entry->in_pack_pos;\n+}\n+\n+static void show_object(struct object *object, const struct name_path *path,\n+\t\t\tconst char *last, void *data)\n+{\n+\tstruct bitmap *base = data;\n+\tbitmap_set(base, find_object_pos(object->sha1));\n+\tmark_as_seen(object);\n+}\n+\n+static void show_commit(struct commit *commit, void *data)\n+{\n+\tmark_as_seen((struct object *)commit);\n+}\n+\n+static int\n+add_to_include_set(struct bitmap *base, struct commit *commit)\n+{\n+\tkhiter_t hash_pos;\n+\tuint32_t bitmap_pos = find_object_pos(commit->object.sha1);\n+\n+\tif (bitmap_get(base, bitmap_pos))\n+\t\treturn 0;\n+\n+\thash_pos = kh_get_sha1(writer.bitmaps, commit->object.sha1);\n+\tif (hash_pos < kh_end(writer.bitmaps)) {\n+\t\tstruct bitmapped_commit *bc = kh_value(writer.bitmaps, hash_pos);\n+\t\tbitmap_or_ewah(base, bc->bitmap);\n+\t\treturn 0;\n+\t}\n+\n+\tbitmap_set(base, bitmap_pos);\n+\treturn 1;\n+}\n+\n+static int\n+should_include(struct commit *commit, void *_data)\n+{\n+\tstruct bitmap *base = _data;\n+\n+\tif (!add_to_include_set(base, commit)) {\n+\t\tstruct commit_list *parent = commit->parents;\n+\n+\t\tmark_as_seen((struct object *)commit);\n+\n+\t\twhile (parent) {\n+\t\t\tparent->item->object.flags |= SEEN;\n+\t\t\tmark_as_seen((struct object *)parent->item);\n+\t\t\tparent = parent->next;\n+\t\t}\n+\n+\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+static void compute_xor_offsets(void)\n+{\n+\tstatic const int MAX_XOR_OFFSET_SEARCH = 10;\n+\n+\tint i, next = 0;\n+\n+\twhile (next < writer.selected_nr) {\n+\t\tstruct bitmapped_commit *stored = &writer.selected[next];\n+\n+\t\tint best_offset = 0;\n+\t\tstruct ewah_bitmap *best_bitmap = stored->bitmap;\n+\t\tstruct ewah_bitmap *test_xor;\n+\n+\t\tfor (i = 1; i <= MAX_XOR_OFFSET_SEARCH; ++i) {\n+\t\t\tint curr = next - i;\n+\n+\t\t\tif (curr < 0)\n+\t\t\t\tbreak;\n+\n+\t\t\ttest_xor = ewah_pool_new();\n+\t\t\tewah_xor(writer.selected[curr].bitmap, stored->bitmap, test_xor);\n+\n+\t\t\tif (test_xor->buffer_size < best_bitmap->buffer_size) {\n+\t\t\t\tif (best_bitmap != stored->bitmap)\n+\t\t\t\t\tewah_pool_free(best_bitmap);\n+\n+\t\t\t\tbest_bitmap = test_xor;\n+\t\t\t\tbest_offset = i;\n+\t\t\t} else {\n+\t\t\t\tewah_pool_free(test_xor);\n+\t\t\t}\n+\t\t}\n+\n+\t\tstored->xor_offset = best_offset;\n+\t\tstored->write_as = best_bitmap;\n+\n+\t\tnext++;\n+\t}\n+}\n+\n+void bitmap_writer_build(struct packing_data *to_pack)\n+{\n+\tstatic const double REUSE_BITMAP_THRESHOLD = 0.2;\n+\n+\tint i, reuse_after, need_reset;\n+\tstruct bitmap *base = bitmap_new();\n+\tstruct rev_info revs;\n+\n+\twriter.bitmaps = kh_init_sha1();\n+\twriter.to_pack = to_pack;\n+\n+\tif (writer.show_progress)\n+\t\twriter.progress = start_progress(\"Building bitmaps\", writer.selected_nr);\n+\n+\tinit_revisions(&revs, NULL);\n+\trevs.tag_objects = 1;\n+\trevs.tree_objects = 1;\n+\trevs.blob_objects = 1;\n+\trevs.no_walk = 0;\n+\n+\trevs.include_check = should_include;\n+\treset_revision_walk();\n+\n+\treuse_after = writer.selected_nr * REUSE_BITMAP_THRESHOLD;\n+\tneed_reset = 0;\n+\n+\tfor (i = writer.selected_nr - 1; i >= 0; --i) {\n+\t\tstruct bitmapped_commit *stored;\n+\t\tstruct object *object;\n+\n+\t\tkhiter_t hash_pos;\n+\t\tint hash_ret;\n+\n+\t\tstored = &writer.selected[i];\n+\t\tobject = (struct object *)stored->commit;\n+\n+\t\tif (stored->bitmap == NULL) {\n+\t\t\tif (i < writer.selected_nr - 1 &&\n+\t\t\t    (need_reset ||\n+\t\t\t     !in_merge_bases(writer.selected[i + 1].commit,\n+\t\t\t\t\t     stored->commit))) {\n+\t\t\t    bitmap_reset(base);\n+\t\t\t    reset_all_seen();\n+\t\t\t}\n+\n+\t\t\tadd_pending_object(&revs, object, \"\");\n+\t\t\trevs.include_check_data = base;\n+\n+\t\t\tif (prepare_revision_walk(&revs))\n+\t\t\t\tdie(\"revision walk setup failed\");\n+\n+\t\t\ttraverse_commit_list(&revs, show_commit, show_object, base);\n+\n+\t\t\trevs.pending.nr = 0;\n+\t\t\trevs.pending.alloc = 0;\n+\t\t\trevs.pending.objects = NULL;\n+\n+\t\t\tstored->bitmap = bitmap_to_ewah(base);\n+\t\t\tneed_reset = 0;\n+\t\t} else\n+\t\t\tneed_reset = 1;\n+\n+\t\tif (i >= reuse_after)\n+\t\t\tstored->flags |= BITMAP_FLAG_REUSE;\n+\n+\t\thash_pos = kh_put_sha1(writer.bitmaps, object->sha1, &hash_ret);\n+\t\tif (hash_ret == 0)\n+\t\t\tdie(\"Duplicate entry when writing index: %s\",\n+\t\t\t    sha1_to_hex(object->sha1));\n+\n+\t\tkh_value(writer.bitmaps, hash_pos) = stored;\n+\t\tdisplay_progress(writer.progress, writer.selected_nr - i);\n+\t}\n+\n+\tbitmap_free(base);\n+\tstop_progress(&writer.progress);\n+\n+\tcompute_xor_offsets();\n+}\n+\n+/**\n+ * Select the commits that will be bitmapped\n+ */\n+static inline unsigned int next_commit_index(unsigned int idx)\n+{\n+\tstatic const unsigned int MIN_COMMITS = 100;\n+\tstatic const unsigned int MAX_COMMITS = 5000;\n+\n+\tstatic const unsigned int MUST_REGION = 100;\n+\tstatic const unsigned int MIN_REGION = 20000;\n+\n+\tunsigned int offset, next;\n+\n+\tif (idx <= MUST_REGION)\n+\t\treturn 0;\n+\n+\tif (idx <= MIN_REGION) {\n+\t\toffset = idx - MUST_REGION;\n+\t\treturn (offset < MIN_COMMITS) ? offset : MIN_COMMITS;\n+\t}\n+\n+\toffset = idx - MIN_REGION;\n+\tnext = (offset < MAX_COMMITS) ? offset : MAX_COMMITS;\n+\n+\treturn (next > MIN_COMMITS) ? next : MIN_COMMITS;\n+}\n+\n+static int date_compare(const void *_a, const void *_b)\n+{\n+\tstruct commit *a = *(struct commit **)_a;\n+\tstruct commit *b = *(struct commit **)_b;\n+\treturn (long)b->date - (long)a->date;\n+}\n+\n+void bitmap_writer_reuse_bitmaps(struct packing_data *to_pack)\n+{\n+\tif (prepare_bitmap_git() < 0)\n+\t\treturn;\n+\n+\twriter.reused = kh_init_sha1();\n+\trebuild_existing_bitmaps(to_pack, writer.reused, writer.show_progress);\n+}\n+\n+static struct ewah_bitmap *find_reused_bitmap(const unsigned char *sha1)\n+{\n+\tkhiter_t hash_pos;\n+\n+\tif (!writer.reused)\n+\t\treturn NULL;\n+\n+\thash_pos = kh_get_sha1(writer.reused, sha1);\n+\tif (hash_pos >= kh_end(writer.reused))\n+\t\treturn NULL;\n+\n+\treturn kh_value(writer.reused, hash_pos);\n+}\n+\n+void bitmap_writer_select_commits(struct commit **indexed_commits,\n+\t\t\t\t  unsigned int indexed_commits_nr,\n+\t\t\t\t  int max_bitmaps)\n+{\n+\tunsigned int i = 0, j, next;\n+\n+\tqsort(indexed_commits, indexed_commits_nr, sizeof(indexed_commits[0]),\n+\t      date_compare);\n+\n+\tif (writer.show_progress)\n+\t\twriter.progress = start_progress(\"Selecting bitmap commits\", 0);\n+\n+\tif (indexed_commits_nr < 100) {\n+\t\tfor (i = 0; i < indexed_commits_nr; ++i)\n+\t\t\tpush_bitmapped_commit(indexed_commits[i], NULL);\n+\t\treturn;\n+\t}\n+\n+\tfor (;;) {\n+\t\tstruct ewah_bitmap *reused_bitmap = NULL;\n+\t\tstruct commit *chosen = NULL;\n+\n+\t\tnext = next_commit_index(i);\n+\n+\t\tif (i + next >= indexed_commits_nr)\n+\t\t\tbreak;\n+\n+\t\tif (max_bitmaps > 0 && writer.selected_nr >= max_bitmaps) {\n+\t\t\twriter.selected_nr = max_bitmaps;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (next == 0) {\n+\t\t\tchosen = indexed_commits[i];\n+\t\t\treused_bitmap = find_reused_bitmap(chosen->object.sha1);\n+\t\t} else {\n+\t\t\tchosen = indexed_commits[i + next];\n+\n+\t\t\tfor (j = 0; j <= next; ++j) {\n+\t\t\t\tstruct commit *cm = indexed_commits[i + j];\n+\n+\t\t\t\treused_bitmap = find_reused_bitmap(cm->object.sha1);\n+\t\t\t\tif (reused_bitmap || (cm->object.flags & NEEDS_BITMAP) != 0) {\n+\t\t\t\t\tchosen = cm;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\n+\t\t\t\tif (cm->parents && cm->parents->next)\n+\t\t\t\t\tchosen = cm;\n+\t\t\t}\n+\t\t}\n+\n+\t\tpush_bitmapped_commit(chosen, reused_bitmap);\n+\n+\t\ti += next + 1;\n+\t\tdisplay_progress(writer.progress, i);\n+\t}\n+\n+\tstop_progress(&writer.progress);\n+}\n+\n+\n+static int sha1write_ewah_helper(void *f, const void *buf, size_t len)\n+{\n+\t/* sha1write will die on error */\n+\tsha1write(f, buf, len);\n+\treturn len;\n+}\n+\n+/**\n+ * Write the bitmap index to disk\n+ */\n+static inline void dump_bitmap(struct sha1file *f, struct ewah_bitmap *bitmap)\n+{\n+\tif (ewah_serialize_to(bitmap, sha1write_ewah_helper, f) < 0)\n+\t\tdie(\"Failed to write bitmap index\");\n+}\n+\n+static const unsigned char *sha1_access(size_t pos, void *table)\n+{\n+\tstruct pack_idx_entry **index = table;\n+\treturn index[pos]->sha1;\n+}\n+\n+static void write_selected_commits_v1(struct sha1file *f,\n+\t\t\t\t      struct pack_idx_entry **index,\n+\t\t\t\t      uint32_t index_nr)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < writer.selected_nr; ++i) {\n+\t\tstruct bitmapped_commit *stored = &writer.selected[i];\n+\t\tstruct bitmap_disk_entry on_disk;\n+\n+\t\tint commit_pos =\n+\t\t\tsha1_pos(stored->commit->object.sha1, index, index_nr, sha1_access);\n+\n+\t\tif (commit_pos < 0)\n+\t\t\tdie(\"BUG: trying to write commit not in index\");\n+\n+\t\ton_disk.object_pos = htonl(commit_pos);\n+\t\ton_disk.xor_offset = stored->xor_offset;\n+\t\ton_disk.flags = stored->flags;\n+\n+\t\tsha1write(f, &on_disk, sizeof(on_disk));\n+\t\tdump_bitmap(f, stored->write_as);\n+\t}\n+}\n+\n+void bitmap_writer_set_checksum(unsigned char *sha1)\n+{\n+\thashcpy(writer.pack_checksum, sha1);\n+}\n+\n+void bitmap_writer_finish(struct pack_idx_entry **index,\n+\t\t\t  uint32_t index_nr,\n+\t\t\t  const char *filename)\n+{\n+\tstatic char tmp_file[PATH_MAX];\n+\tstatic uint16_t default_version = 1;\n+\tstatic uint16_t flags = BITMAP_OPT_FULL_DAG;\n+\tstruct sha1file *f;\n+\n+\tstruct bitmap_disk_header header;\n+\n+\tint fd = odb_mkstemp(tmp_file, sizeof(tmp_file), \"pack/tmp_bitmap_XXXXXX\");\n+\n+\tif (fd < 0)\n+\t\tdie_errno(\"unable to create '%s'\", tmp_file);\n+\tf = sha1fd(fd, tmp_file);\n+\n+\tmemcpy(header.magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE));\n+\theader.version = htons(default_version);\n+\theader.options = htons(flags);\n+\theader.entry_count = htonl(writer.selected_nr);\n+\tmemcpy(header.checksum, writer.pack_checksum, 20);\n+\n+\tsha1write(f, &header, sizeof(header));\n+\tdump_bitmap(f, writer.commits);\n+\tdump_bitmap(f, writer.trees);\n+\tdump_bitmap(f, writer.blobs);\n+\tdump_bitmap(f, writer.tags);\n+\twrite_selected_commits_v1(f, index, index_nr);\n+\n+\tsha1close(f, NULL, CSUM_FSYNC);\n+\n+\tif (adjust_shared_perm(tmp_file))\n+\t\tdie_errno(\"unable to make temporary bitmap file readable\");\n+\n+\tif (rename(tmp_file, filename))\n+\t\tdie_errno(\"unable to rename temporary bitmap file to '%s'\", filename);\n+}\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex cec56b2..86ce677 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -963,3 +963,95 @@ void test_bitmap_walk(struct rev_info *revs)\n \telse\n \t\tfprintf(stderr, \"Mismatch!\\n\");\n }\n+\n+static int rebuild_bitmap(uint32_t *reposition,\n+\t\t\t  struct ewah_bitmap *source,\n+\t\t\t  struct bitmap *dest)\n+{\n+\tuint32_t pos = 0;\n+\tstruct ewah_iterator it;\n+\teword_t word;\n+\n+\tewah_iterator_init(&it, source);\n+\n+\twhile (ewah_iterator_next(&word, &it)) {\n+\t\tuint32_t offset, bit_pos;\n+\n+\t\tfor (offset = 0; offset < BITS_IN_WORD; ++offset) {\n+\t\t\tif ((word >> offset) == 0)\n+\t\t\t\tbreak;\n+\n+\t\t\toffset += ewah_bit_ctz64(word >> offset);\n+\n+\t\t\tbit_pos = reposition[pos + offset];\n+\t\t\tif (bit_pos > 0)\n+\t\t\t\tbitmap_set(dest, bit_pos - 1);\n+\t\t\telse /* can't reuse, we don't have the object */\n+\t\t\t\treturn -1;\n+\t\t}\n+\n+\t\tpos += BITS_IN_WORD;\n+\t}\n+\treturn 0;\n+}\n+\n+int rebuild_existing_bitmaps(struct packing_data *mapping,\n+\t\t\t     khash_sha1 *reused_bitmaps,\n+\t\t\t     int show_progress)\n+{\n+\tuint32_t i, num_objects;\n+\tuint32_t *reposition;\n+\tstruct bitmap *rebuild;\n+\tstruct stored_bitmap *stored;\n+\tstruct progress *progress = NULL;\n+\n+\tkhiter_t hash_pos;\n+\tint hash_ret;\n+\n+\tif (prepare_bitmap_git() < 0)\n+\t\treturn -1;\n+\n+\tnum_objects = bitmap_git.pack->num_objects;\n+\treposition = xcalloc(num_objects, sizeof(uint32_t));\n+\n+\tfor (i = 0; i < num_objects; ++i) {\n+\t\tconst unsigned char *sha1;\n+\t\tstruct revindex_entry *entry;\n+\t\tstruct object_entry *oe;\n+\n+\t\tentry = &bitmap_git.reverse_index->revindex[i];\n+\t\tsha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);\n+\t\toe = packlist_find(mapping, sha1, NULL);\n+\n+\t\tif (oe)\n+\t\t\treposition[i] = oe->in_pack_pos + 1;\n+\t}\n+\n+\trebuild = bitmap_new();\n+\ti = 0;\n+\n+\tif (show_progress)\n+\t\tprogress = start_progress(\"Reusing bitmaps\", 0);\n+\n+\tkh_foreach_value(bitmap_git.bitmaps, stored, {\n+\t\tif (stored->flags & BITMAP_FLAG_REUSE) {\n+\t\t\tif (!rebuild_bitmap(reposition,\n+\t\t\t\t\t    lookup_stored_bitmap(stored),\n+\t\t\t\t\t    rebuild)) {\n+\t\t\t\thash_pos = kh_put_sha1(reused_bitmaps,\n+\t\t\t\t\t\t       stored->sha1,\n+\t\t\t\t\t\t       &hash_ret);\n+\t\t\t\tkh_value(reused_bitmaps, hash_pos) =\n+\t\t\t\t\tbitmap_to_ewah(rebuild);\n+\t\t\t}\n+\t\t\tbitmap_reset(rebuild);\n+\t\t\tdisplay_progress(progress, ++i);\n+\t\t}\n+\t});\n+\n+\tstop_progress(&progress);\n+\n+\tfree(reposition);\n+\tbitmap_free(rebuild);\n+\treturn 0;\n+}\ndiff --git a/pack-bitmap.h b/pack-bitmap.h\nindex df45578..18f4d4c 100644\n--- a/pack-bitmap.h\n+++ b/pack-bitmap.h\n@@ -6,6 +6,7 @@\n #define ewah_realloc xrealloc\n #include \"ewah/ewok.h\"\n #include \"khash.h\"\n+#include \"pack-objects.h\"\n \n struct bitmap_disk_entry {\n \tuint32_t object_pos;\n@@ -23,10 +24,16 @@ struct bitmap_disk_header {\n \n static const char BITMAP_IDX_SIGNATURE[] = {'B', 'I', 'T', 'M'};;\n \n+#define NEEDS_BITMAP (1u<<22)\n+\n enum pack_bitmap_opts {\n \tBITMAP_OPT_FULL_DAG = 1,\n };\n \n+enum pack_bitmap_flags {\n+\tBITMAP_FLAG_REUSE = 0x1\n+};\n+\n typedef int (*show_reachable_fn)(\n \tconst unsigned char *sha1,\n \tenum object_type type,\n@@ -42,5 +49,16 @@ void test_bitmap_walk(struct rev_info *revs);\n char *pack_bitmap_filename(struct packed_git *p);\n int prepare_bitmap_walk(struct rev_info *revs);\n int reuse_partial_packfile_from_bitmap(struct packed_git **packfile, uint32_t *entries, off_t *up_to);\n+int rebuild_existing_bitmaps(struct packing_data *mapping, khash_sha1 *reused_bitmaps, int show_progress);\n+\n+void bitmap_writer_show_progress(int show);\n+void bitmap_writer_set_checksum(unsigned char *sha1);\n+void bitmap_writer_build_type_index(struct pack_idx_entry **index, uint32_t index_nr);\n+void bitmap_writer_reuse_bitmaps(struct packing_data *to_pack);\n+void bitmap_writer_select_commits(struct commit **indexed_commits,\n+\t\tunsigned int indexed_commits_nr, int max_bitmaps);\n+void bitmap_writer_build(struct packing_data *to_pack);\n+void bitmap_writer_finish(\n+\t struct pack_idx_entry **index, uint32_t index_nr, const char *filename);\n \n #endif\ndiff --git a/pack-objects.h b/pack-objects.h\nindex 90ad0a8..d1b98b3 100644\n--- a/pack-objects.h\n+++ b/pack-objects.h\n@@ -17,6 +17,7 @@ struct object_entry {\n \tenum object_type type;\n \tenum object_type in_pack_type;\t/* could be delta */\n \tuint32_t hash;\t\t\t/* name hint hash */\n+\tunsigned int in_pack_pos;\n \tunsigned char in_pack_header_size;\n \tunsigned preferred_base:1; /*\n \t\t\t\t    * we do not pack this, but is available\ndiff --git a/pack-write.c b/pack-write.c\nindex ca9e63b..6203d37 100644\n--- a/pack-write.c\n+++ b/pack-write.c\n@@ -371,5 +371,7 @@ void finish_tmp_packfile(char *name_buffer,\n \tif (rename(idx_tmp_name, name_buffer))\n \t\tdie_errno(\"unable to rename temporary index file\");\n \n+\t*end_of_name_prefix = '\\0';\n+\n \tfree((void *)idx_tmp_name);\n }\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229415","messageId":"20131024180654.GN24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 14/19] repack: stop using magic number for ARRAY_SIZE(exts)","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:06:54Z","receivedAt":"2013-10-24T18:06:54Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We have a static array of extensions, but hardcode the size\nof the array in our loops. Let's pull out this magic number,\nwhich will make it easier to change.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/repack.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex a0ff5c7..2e88975 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -115,7 +115,7 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name)\n \n int cmd_repack(int argc, const char **argv, const char *prefix)\n {\n-\tconst char *exts[2] = {\".pack\", \".idx\"};\n+\tconst char *exts[] = {\".pack\", \".idx\"};\n \tstruct child_process cmd;\n \tstruct string_list_item *item;\n \tstruct argv_array cmd_args = ARGV_ARRAY_INIT;\n@@ -258,7 +258,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t */\n \tfailed = 0;\n \tfor_each_string_list_item(item, &names) {\n-\t\tfor (ext = 0; ext < 2; ext++) {\n+\t\tfor (ext = 0; ext < ARRAY_SIZE(exts); ext++) {\n \t\t\tchar *fname, *fname_old;\n \t\t\tfname = mkpathdup(\"%s/%s%s\", packdir,\n \t\t\t\t\t\titem->string, exts[ext]);\n@@ -315,7 +315,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \n \t/* Now the ones with the same name are out of the way... */\n \tfor_each_string_list_item(item, &names) {\n-\t\tfor (ext = 0; ext < 2; ext++) {\n+\t\tfor (ext = 0; ext < ARRAY_SIZE(exts); ext++) {\n \t\t\tchar *fname, *fname_old;\n \t\t\tstruct stat statbuffer;\n \t\t\tfname = mkpathdup(\"%s/pack-%s%s\",\n@@ -335,7 +335,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \n \t/* Remove the \"old-\" files */\n \tfor_each_string_list_item(item, &names) {\n-\t\tfor (ext = 0; ext < 2; ext++) {\n+\t\tfor (ext = 0; ext < ARRAY_SIZE(exts); ext++) {\n \t\t\tchar *fname;\n \t\t\tfname = mkpath(\"%s/old-pack-%s%s\",\n \t\t\t\t\tpackdir,\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229417","messageId":"20131024180710.GO24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 15/19] repack: turn exts array into array-of-struct","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:07:11Z","receivedAt":"2013-10-24T18:07:11Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This is slightly more verbose, but will let us annotate the\nextensions with further options in future commits.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/repack.c | 17 +++++++++++------\n 1 file changed, 11 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex 2e88975..a176de2 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -115,7 +115,12 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name)\n \n int cmd_repack(int argc, const char **argv, const char *prefix)\n {\n-\tconst char *exts[] = {\".pack\", \".idx\"};\n+\tstruct {\n+\t\tconst char *name;\n+\t} exts[] = {\n+\t\t{\".pack\"},\n+\t\t{\".idx\"},\n+\t};\n \tstruct child_process cmd;\n \tstruct string_list_item *item;\n \tstruct argv_array cmd_args = ARGV_ARRAY_INIT;\n@@ -261,14 +266,14 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\tfor (ext = 0; ext < ARRAY_SIZE(exts); ext++) {\n \t\t\tchar *fname, *fname_old;\n \t\t\tfname = mkpathdup(\"%s/%s%s\", packdir,\n-\t\t\t\t\t\titem->string, exts[ext]);\n+\t\t\t\t\t\titem->string, exts[ext].name);\n \t\t\tif (!file_exists(fname)) {\n \t\t\t\tfree(fname);\n \t\t\t\tcontinue;\n \t\t\t}\n \n \t\t\tfname_old = mkpath(\"%s/old-%s%s\", packdir,\n-\t\t\t\t\t\titem->string, exts[ext]);\n+\t\t\t\t\t\titem->string, exts[ext].name);\n \t\t\tif (file_exists(fname_old))\n \t\t\t\tif (unlink(fname_old))\n \t\t\t\t\tfailed = 1;\n@@ -319,9 +324,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\t\tchar *fname, *fname_old;\n \t\t\tstruct stat statbuffer;\n \t\t\tfname = mkpathdup(\"%s/pack-%s%s\",\n-\t\t\t\t\tpackdir, item->string, exts[ext]);\n+\t\t\t\t\tpackdir, item->string, exts[ext].name);\n \t\t\tfname_old = mkpathdup(\"%s-%s%s\",\n-\t\t\t\t\tpacktmp, item->string, exts[ext]);\n+\t\t\t\t\tpacktmp, item->string, exts[ext].name);\n \t\t\tif (!stat(fname_old, &statbuffer)) {\n \t\t\t\tstatbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);\n \t\t\t\tchmod(fname_old, statbuffer.st_mode);\n@@ -340,7 +345,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\t\tfname = mkpath(\"%s/old-pack-%s%s\",\n \t\t\t\t\tpackdir,\n \t\t\t\t\titem->string,\n-\t\t\t\t\texts[ext]);\n+\t\t\t\t\texts[ext].name);\n \t\t\tif (remove_path(fname))\n \t\t\t\twarning(_(\"removing '%s' failed\"), fname);\n \t\t}\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229418","messageId":"20131024180731.GP24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 16/19] repack: handle optional files created by pack-objects","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:07:31Z","receivedAt":"2013-10-24T18:07:31Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We ask pack-objects to pack to a set of temporary files, and\nthen rename them into place. Some files that pack-objects\ncreates may be optional (like a .bitmap file), in which case\nwe would not want to call rename(). We already call stat()\nand make the chmod optional if the file cannot be accessed.\nWe could simply skip the rename step in this case, but that\nwould be a minor regression in noticing problems with\nnon-optional files (like the .pack and .idx files).\n\nInstead, we can now annotate extensions as optional, and\nskip them if they don't exist (and otherwise rely on\nrename() to barf).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/repack.c | 9 +++++++--\n 1 file changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex a176de2..8b7dfd0 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -117,6 +117,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n {\n \tstruct {\n \t\tconst char *name;\n+\t\tunsigned optional:1;\n \t} exts[] = {\n \t\t{\".pack\"},\n \t\t{\".idx\"},\n@@ -323,6 +324,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\tfor (ext = 0; ext < ARRAY_SIZE(exts); ext++) {\n \t\t\tchar *fname, *fname_old;\n \t\t\tstruct stat statbuffer;\n+\t\t\tint exists = 0;\n \t\t\tfname = mkpathdup(\"%s/pack-%s%s\",\n \t\t\t\t\tpackdir, item->string, exts[ext].name);\n \t\t\tfname_old = mkpathdup(\"%s-%s%s\",\n@@ -330,9 +332,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\t\tif (!stat(fname_old, &statbuffer)) {\n \t\t\t\tstatbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);\n \t\t\t\tchmod(fname_old, statbuffer.st_mode);\n+\t\t\t\texists = 1;\n+\t\t\t}\n+\t\t\tif (exists || !exts[ext].optional) {\n+\t\t\t\tif (rename(fname_old, fname))\n+\t\t\t\t\tdie_errno(_(\"renaming '%s' failed\"), fname_old);\n \t\t\t}\n-\t\t\tif (rename(fname_old, fname))\n-\t\t\t\tdie_errno(_(\"renaming '%s' failed\"), fname_old);\n \t\t\tfree(fname);\n \t\t\tfree(fname_old);\n \t\t}\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229419","messageId":"20131024180811.GQ24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 17/19] repack: consider bitmaps when performing repacks","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:08:11Z","receivedAt":"2013-10-24T18:08:11Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nSince `pack-objects` will write a `.bitmap` file next to the `.pack` and\n`.idx` files, this commit teaches `git-repack` to consider the new\nbitmap indexes (if they exist) when performing repack operations.\n\nThis implies moving old bitmap indexes out of the way if we are\nrepacking a repository that already has them, and moving the newly\ngenerated bitmap indexes into the `objects/pack` directory, next to\ntheir corresponding packfiles.\n\nSince `git repack` is now capable of handling these `.bitmap` files,\na normal `git gc` run on a repository that has `pack.writebitmaps` set\nto true in its config file will generate bitmap indexes as part of the\ngarbage collection process.\n\nAlternatively, `git repack` can be called with the `-b` switch to\nexplicitly generate bitmap indexes if you are experimenting\nand don't want them on all the time.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/repack.c | 9 ++++++++-\n 1 file changed, 8 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex 8b7dfd0..239f278 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -94,7 +94,7 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)\n \n static void remove_redundant_pack(const char *dir_name, const char *base_name)\n {\n-\tconst char *exts[] = {\".pack\", \".idx\", \".keep\"};\n+\tconst char *exts[] = {\".pack\", \".idx\", \".keep\", \".bitmap\"};\n \tint i;\n \tstruct strbuf buf = STRBUF_INIT;\n \tsize_t plen;\n@@ -121,6 +121,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t} exts[] = {\n \t\t{\".pack\"},\n \t\t{\".idx\"},\n+\t\t{\".bitmap\", 1},\n \t};\n \tstruct child_process cmd;\n \tstruct string_list_item *item;\n@@ -143,6 +144,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \tint no_update_server_info = 0;\n \tint quiet = 0;\n \tint local = 0;\n+\tint write_bitmap = -1;\n \n \tstruct option builtin_repack_options[] = {\n \t\tOPT_BIT('a', NULL, &pack_everything,\n@@ -161,6 +163,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\tOPT__QUIET(&quiet, N_(\"be quiet\")),\n \t\tOPT_BOOL('l', \"local\", &local,\n \t\t\t\tN_(\"pass --local to git-pack-objects\")),\n+\t\tOPT_BOOL('b', \"write-bitmap-index\", &write_bitmap,\n+\t\t\t\tN_(\"write bitmap index\")),\n \t\tOPT_STRING(0, \"unpack-unreachable\", &unpack_unreachable, N_(\"approxidate\"),\n \t\t\t\tN_(\"with -A, do not loosen objects older than this\")),\n \t\tOPT_INTEGER(0, \"window\", &window,\n@@ -202,6 +206,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\targv_array_pushf(&cmd_args, \"--no-reuse-delta\");\n \tif (no_reuse_object)\n \t\targv_array_pushf(&cmd_args, \"--no-reuse-object\");\n+\tif (write_bitmap >= 0)\n+\t\targv_array_pushf(&cmd_args, \"--%swrite-bitmap-index\",\n+\t\t\t\t write_bitmap ? \"\" : \"no-\");\n \n \tif (pack_everything & ALL_INTO_ONE) {\n \t\tget_non_kept_pack_filenames(&existing_packs);\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229420","messageId":"20131024180831.GR24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 18/19] t: add basic bitmap functionality tests","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:08:31Z","receivedAt":"2013-10-24T18:08:31Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Now that we can read and write bitmaps, we can exercise them\nwith some basic functionality tests. These tests aren't\nparticularly useful for seeing the benefit, as the test\nrepo is too small for it to make a difference. However, we\ncan at least check that using bitmaps does not break anything.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n t/t5310-pack-bitmaps.sh | 114 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 114 insertions(+)\n create mode 100755 t/t5310-pack-bitmaps.sh\n\ndiff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh\nnew file mode 100755\nindex 0000000..0868725\n--- /dev/null\n+++ b/t/t5310-pack-bitmaps.sh\n@@ -0,0 +1,114 @@\n+#!/bin/sh\n+\n+test_description='exercise basic bitmap functionality'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup repo with moderate-sized history' '\n+\tfor i in $(test_seq 1 10); do\n+\t\ttest_commit $i\n+\tdone &&\n+\tgit checkout -b other HEAD~5 &&\n+\tfor i in `test_seq 1 10`; do\n+\t\ttest_commit side-$i\n+\tdone &&\n+\tgit checkout master &&\n+\tblob=$(echo tagged-blob | git hash-object -w --stdin) &&\n+\tgit tag tagged-blob $blob &&\n+\tgit config pack.writebitmaps true\n+'\n+\n+test_expect_success 'full repack creates bitmaps' '\n+\tgit repack -ad &&\n+\tls .git/objects/pack/ | grep bitmap >output &&\n+\ttest_line_count = 1 output\n+'\n+\n+test_expect_success 'rev-list --test-bitmap verifies bitmaps' '\n+\tgit rev-list --test-bitmap HEAD\n+'\n+\n+rev_list_tests() {\n+\tstate=$1\n+\n+\ttest_expect_success \"counting commits via bitmap ($state)\" '\n+\t\tgit rev-list --count HEAD >expect &&\n+\t\tgit rev-list --use-bitmap-index --count HEAD >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+\n+\ttest_expect_success \"counting partial commits via bitmap ($state)\" '\n+\t\tgit rev-list --count HEAD~5..HEAD >expect &&\n+\t\tgit rev-list --use-bitmap-index --count HEAD~5..HEAD >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+\n+\ttest_expect_success \"counting non-linear history ($state)\" '\n+\t\tgit rev-list --count other...master >expect &&\n+\t\tgit rev-list --use-bitmap-index --count other...master >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+\n+\ttest_expect_success \"enumerate --objects ($state)\" '\n+\t\tgit rev-list --objects --use-bitmap-index HEAD >tmp &&\n+\t\tcut -d\" \" -f1 <tmp >tmp2 &&\n+\t\tsort <tmp2 >actual &&\n+\t\tgit rev-list --objects HEAD >tmp &&\n+\t\tcut -d\" \" -f1 <tmp >tmp2 &&\n+\t\tsort <tmp2 >expect &&\n+\t\ttest_cmp expect actual\n+\t'\n+\n+\ttest_expect_success \"bitmap --objects handles non-commit objects ($state)\" '\n+\t\tgit rev-list --objects --use-bitmap-index HEAD tagged-blob >actual &&\n+\t\tgrep $blob actual\n+\t'\n+}\n+\n+rev_list_tests 'full bitmap'\n+\n+test_expect_success 'clone from bitmapped repository' '\n+\tgit clone --no-local --bare . clone.git &&\n+\tgit rev-parse HEAD >expect &&\n+\tgit --git-dir=clone.git rev-parse HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'setup further non-bitmapped commits' '\n+\tfor i in `test_seq 1 10`; do\n+\t\ttest_commit further-$i\n+\tdone\n+'\n+\n+rev_list_tests 'partial bitmap'\n+\n+test_expect_success 'fetch (partial bitmap)' '\n+\tgit --git-dir=clone.git fetch origin master:master &&\n+\tgit rev-parse HEAD >expect &&\n+\tgit --git-dir=clone.git rev-parse HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'incremental repack cannot create bitmaps' '\n+\ttest_commit more-1 &&\n+\ttest_must_fail git repack -d\n+'\n+\n+test_expect_success 'incremental repack can disable bitmaps' '\n+\ttest_commit more-2 &&\n+\tgit repack -d --no-write-bitmap-index\n+'\n+\n+test_expect_success 'full repack, reusing previous bitmaps' '\n+\tgit repack -ad &&\n+\tls .git/objects/pack/ | grep bitmap >output &&\n+\ttest_line_count = 1 output\n+'\n+\n+test_expect_success 'fetch (full bitmap)' '\n+\tgit --git-dir=clone.git fetch origin master:master &&\n+\tgit rev-parse HEAD >expect &&\n+\tgit --git-dir=clone.git rev-parse HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229421","messageId":"20131024180850.GS24180@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCH 19/19] pack-bitmap: implement optional name_hash cache","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-24T18:08:50Z","receivedAt":"2013-10-24T18:08:50Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nWhen we use pack bitmaps rather than walking the object\ngraph, we end up with the list of objects to include in the\npackfile, but we do not know the path at which any tree or\nblob objects would be found.\n\nIn a recently packed repository, this is fine. A fetch would\nuse the paths only as a heuristic in the delta compression\nphase, and a fully packed repository should not need to do\nmuch delta compression.\n\nAs time passes, though, we may acquire more objects on top\nof our large bitmapped pack. If clients fetch frequently,\nthen they never even look at the bitmapped history, and all\nworks as usual. However, a client who has not fetched since\nthe last bitmap repack will have \"have\" tips in the\nbitmapped history, but \"want\" newer objects.\n\nThe bitmaps themselves degrade gracefully in this\ncircumstance. We manually walk the more recent bits of\nhistory, and then use bitmaps when we hit them.\n\nBut we would also like to perform delta compression between\nthe newer objects and the bitmapped objects (both to delta\nagainst what we know the user already has, but also between\n\"new\" and \"old\" objects that the user is fetching). The lack\nof pathnames makes our delta heuristics much less effective.\n\nThis patch adds an optional cache of the 32-bit name_hash\nvalues to the end of the bitmap file. If present, a reader\ncan use it to match bitmapped and non-bitmapped names during\ndelta compression.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/technical/bitmap-format.txt | 33 +++++++++++++++++++++++++++++++\n pack-bitmap-write.c                       | 18 ++++++++++++++++-\n pack-bitmap.c                             | 11 +++++++++++\n pack-bitmap.h                             |  1 +\n 4 files changed, 62 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/technical/bitmap-format.txt b/Documentation/technical/bitmap-format.txt\nindex c686dd1..36a511c 100644\n--- a/Documentation/technical/bitmap-format.txt\n+++ b/Documentation/technical/bitmap-format.txt\n@@ -21,6 +21,12 @@ GIT bitmap v1 format\n \t\t\trequirement for the bitmap index format, also present in JGit,\n \t\t\tthat greatly reduces the complexity of the implementation.\n \n+\t\t\t- BITMAP_OPT_HASH_CACHE (0x4)\n+\t\t\tIf present, the end of the bitmap file contains\n+\t\t\t`N` 32-bit name-hash values, one per object in the\n+\t\t\tpack. The format and meaning of the name-hash is\n+\t\t\tdescribed below.\n+\n \t\t4-byte entry count (network byte order)\n \n \t\t\tThe total count of entries (bitmapped commits) in this bitmap index.\n@@ -129,3 +135,30 @@ The bitstream represented by the above chunk is then:\n The next word after `L_M` (if any) must again be a RLW, for the next\n chunk.  For efficient appending to the bitstream, the EWAH stores a\n pointer to the last RLW in the stream.\n+\n+\n+== Appendix B: Optional Bitmap Sections\n+\n+These sections may or may not be present in the `.bitmap` file; their\n+presence is indicated by the header flags section described above.\n+\n+Name-hash cache\n+---------------\n+\n+If the BITMAP_OPT_HASH_CACHE flag is set, the end of the bitmap contains\n+a cache of 32-bit values, one per object in the pack. The value at\n+position `i` is the hash of the pathname at which the `i`th object\n+(counting in index order) in the pack can be found.  This can be fed\n+into the delta heuristics to compare objects with similar pathnames.\n+\n+The hash algorithm used is:\n+\n+    hash = 0;\n+    while ((c = *name++))\n+\t    if (!isspace(c))\n+\t\t    hash = (hash >> 2) + (c << 24);\n+\n+Note that this hashing scheme is tied to the BITMAP_OPT_HASH_CACHE flag.\n+If implementations want to choose a different hashing scheme, they are\n+free to do so, but MUST allocate a new header flag (because comparing\n+hashes made under two different schemes would be pointless).\ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex 6a589c3..c44874a 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -492,6 +492,19 @@ static void write_selected_commits_v1(struct sha1file *f,\n \t}\n }\n \n+static void write_hash_cache(struct sha1file *f,\n+\t\t\t     struct pack_idx_entry **index,\n+\t\t\t     uint32_t index_nr)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < index_nr; ++i) {\n+\t\tstruct object_entry *entry = (struct object_entry *)index[i];\n+\t\tuint32_t hash_value = htonl(entry->hash);\n+\t\tsha1write(f, &hash_value, sizeof(hash_value));\n+\t}\n+}\n+\n void bitmap_writer_set_checksum(unsigned char *sha1)\n {\n \thashcpy(writer.pack_checksum, sha1);\n@@ -503,7 +516,7 @@ void bitmap_writer_finish(struct pack_idx_entry **index,\n {\n \tstatic char tmp_file[PATH_MAX];\n \tstatic uint16_t default_version = 1;\n-\tstatic uint16_t flags = BITMAP_OPT_FULL_DAG;\n+\tstatic uint16_t flags = BITMAP_OPT_FULL_DAG | BITMAP_OPT_HASH_CACHE;\n \tstruct sha1file *f;\n \n \tstruct bitmap_disk_header header;\n@@ -527,6 +540,9 @@ void bitmap_writer_finish(struct pack_idx_entry **index,\n \tdump_bitmap(f, writer.tags);\n \twrite_selected_commits_v1(f, index, index_nr);\n \n+\tif (flags & BITMAP_OPT_HASH_CACHE)\n+\t\twrite_hash_cache(f, index, index_nr);\n+\n \tsha1close(f, NULL, CSUM_FSYNC);\n \n \tif (adjust_shared_perm(tmp_file))\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex 86ce677..a7c553d 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -68,6 +68,9 @@ static struct bitmap_index {\n \t/* Number of bitmapped commits */\n \tuint32_t entry_count;\n \n+\t/* Name-hash cache (or NULL if not present). */\n+\tuint32_t *hashes;\n+\n \t/*\n \t * Extended index.\n \t *\n@@ -154,6 +157,11 @@ static int load_bitmap_header(struct bitmap_index *index)\n \t\tif ((flags & BITMAP_OPT_FULL_DAG) == 0)\n \t\t\treturn error(\"Unsupported options for bitmap index file \"\n \t\t\t\t\"(Git requires BITMAP_OPT_FULL_DAG)\");\n+\n+\t\tif (flags & BITMAP_OPT_HASH_CACHE) {\n+\t\t\tindex->hashes = index->map + index->map_size - 20 -\n+\t\t\t\t(sizeof(uint32_t) * index->pack->num_objects);\n+\t\t}\n \t}\n \n \tindex->entry_count = ntohl(header->entry_count);\n@@ -621,6 +629,9 @@ static void show_objects_for_type(\n \t\t\tentry = &bitmap_git.reverse_index->revindex[pos + offset];\n \t\t\tsha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);\n \n+\t\t\tif (bitmap_git.hashes)\n+\t\t\t\thash = ntohl(bitmap_git.hashes[entry->nr]);\n+\n \t\t\tshow_reach(sha1, object_type, 0, hash, bitmap_git.pack, entry->offset);\n \t\t}\n \ndiff --git a/pack-bitmap.h b/pack-bitmap.h\nindex 18f4d4c..6053453 100644\n--- a/pack-bitmap.h\n+++ b/pack-bitmap.h\n@@ -28,6 +28,7 @@ static const char BITMAP_IDX_SIGNATURE[] = {'B', 'I', 'T', 'M'};;\n \n enum pack_bitmap_opts {\n \tBITMAP_OPT_FULL_DAG = 1,\n+\tBITMAP_OPT_HASH_CACHE = 4,\n };\n \n enum pack_bitmap_flags {\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229431","messageId":"xmqqvc0mmn5s.fsf@gitster.dls.corp.google.com","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"Re: [PATCH 0/19] pack bitmaps","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-10-24T20:25:35Z","receivedAt":"2013-10-24T20:25:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> A similar series has been running on github.com for the past couple of\n> months, though not every repository has had bitmaps turned on (but some\n> very busy ones have).  We've hopefully squeezed out all of the bugs and\n> corner cases over that time. However, I did rebase this on a more modern\n> version of \"master\"; among other conflicts, this required porting the\n> git-repack changes from shell to C. So it's entirely possible I've\n> introduced new bugs. :)\n>\n> The idea and original implementation for bitmaps comes from Shawn and\n> Colby, of course. The hard work in this series was done by Vicent Marti,\n> and he is credited as the author in most of the patches. I've added some\n> window dressing and helped a little with debugging and review. But along\n> with Vicent, I should be able to help with answering questions for\n> review, and as time goes on, I'm familiar enough with the code to deal\n> with bugs and reviewing future changes.\n\nWoo-hoo.\n"},{"id":"229452","messageId":"xmqqiowmkztz.fsf@gitster.dls.corp.google.com","threadId":"35192","inReplyTo":"20131024180217.GH24180@sigill.intra.peff.net","subject":"Re: [PATCH 08/19] ewah: compressed bitmap implementation","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-10-24T23:34:48Z","receivedAt":"2013-10-24T23:34:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> From: Vicent Marti <tanoku@gmail.com>\n>\n> EWAH is a word-aligned compressed variant of a bitset (i.e. a data\n> structure that acts as a 0-indexed boolean array for many entries).\n\nI think I've already pointed out some when the original series was\nposted, but this does not seem to compile with -Wdecl-after-stmt\n(possibly other violations may exist; I haven't looked very closely\nyet).\n\n>\n> It uses a 64-bit run-length encoding (RLE) compression scheme,\n> trading some compression for better processing speed.\n>\n> The goal of this word-aligned implementation is not to achieve\n> the best compression, but rather to improve query processing time.\n> As it stands right now, this EWAH implementation will always be more\n> efficient storage-wise than its uncompressed alternative.\n>\n> EWAH arrays will be used as the on-disk format to store reachability\n> bitmaps for all objects in a repository while keeping reasonable sizes,\n> in the same way that JGit does.\n>\n> This EWAH implementation is a mostly straightforward port of the\n> original `javaewah` library that JGit currently uses. The library is\n> self-contained and has been embedded whole (4 files) inside the `ewah`\n> folder to ease redistribution.\n>\n> The library is re-licensed under the GPLv2 with the permission of Daniel\n> Lemire, the original author. The source code for the C version can\n> be found on GitHub:\n>\n> \thttps://github.com/vmg/libewok\n>\n> The original Java implementation can also be found on GitHub:\n>\n> \thttps://github.com/lemire/javaewah\n>\n> Signed-off-by: Vicent Marti <tanoku@gmail.com>\n> Signed-off-by: Jeff King <peff@peff.net>\n> ---\n>  Makefile           |   6 +\n>  ewah/bitmap.c      | 226 +++++++++++++++++\n>  ewah/ewah_bitmap.c | 701 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n>  ewah/ewah_io.c     | 192 +++++++++++++++\n>  ewah/ewah_rlw.c    | 120 +++++++++\n>  ewah/ewok.h        | 223 +++++++++++++++++\n>  ewah/ewok_rlw.h    | 114 +++++++++\n>  7 files changed, 1582 insertions(+)\n>  create mode 100644 ewah/bitmap.c\n>  create mode 100644 ewah/ewah_bitmap.c\n>  create mode 100644 ewah/ewah_io.c\n>  create mode 100644 ewah/ewah_rlw.c\n>  create mode 100644 ewah/ewok.h\n>  create mode 100644 ewah/ewok_rlw.h\n>\n> diff --git a/Makefile b/Makefile\n> index 48ff0bd..eacb940 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -667,6 +667,8 @@ LIB_H += diff.h\n>  LIB_H += diffcore.h\n>  LIB_H += dir.h\n>  LIB_H += exec_cmd.h\n> +LIB_H += ewah/ewok.h\n> +LIB_H += ewah/ewok_rlw.h\n>  LIB_H += fetch-pack.h\n>  LIB_H += fmt-merge-msg.h\n>  LIB_H += fsck.h\n> @@ -800,6 +802,10 @@ LIB_OBJS += dir.o\n>  LIB_OBJS += editor.o\n>  LIB_OBJS += entry.o\n>  LIB_OBJS += environment.o\n> +LIB_OBJS += ewah/bitmap.o\n> +LIB_OBJS += ewah/ewah_bitmap.o\n> +LIB_OBJS += ewah/ewah_io.o\n> +LIB_OBJS += ewah/ewah_rlw.o\n>  LIB_OBJS += exec_cmd.o\n>  LIB_OBJS += fetch-pack.o\n>  LIB_OBJS += fsck.o\n> diff --git a/ewah/bitmap.c b/ewah/bitmap.c\n> new file mode 100644\n> index 0000000..d248577\n> --- /dev/null\n> +++ b/ewah/bitmap.c\n> @@ -0,0 +1,226 @@\n> +/**\n> + * Copyright 2013, GitHub, Inc\n> + * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n> + *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n> + *\n> + * This program is free software; you can redistribute it and/or\n> + * modify it under the terms of the GNU General Public License\n> + * as published by the Free Software Foundation; either version 2\n> + * of the License, or (at your option) any later version.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + *\n> + * You should have received a copy of the GNU General Public License\n> + * along with this program; if not, write to the Free Software\n> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n> + */\n> +#include <assert.h>\n> +#include <stdlib.h>\n> +#include <string.h>\n> +\n> +#include \"ewok.h\"\n> +\n> +#define MASK(x) ((eword_t)1 << (x % BITS_IN_WORD))\n> +#define BLOCK(x) (x / BITS_IN_WORD)\n> +\n> +struct bitmap *bitmap_new(void)\n> +{\n> +\tstruct bitmap *bitmap = ewah_malloc(sizeof(struct bitmap));\n> +\tbitmap->words = ewah_calloc(32, sizeof(eword_t));\n> +\tbitmap->word_alloc = 32;\n> +\treturn bitmap;\n> +}\n> +\n> +void bitmap_set(struct bitmap *self, size_t pos)\n> +{\n> +\tsize_t block = BLOCK(pos);\n> +\n> +\tif (block >= self->word_alloc) {\n> +\t\tsize_t old_size = self->word_alloc;\n> +\t\tself->word_alloc = block * 2;\n> +\t\tself->words = ewah_realloc(self->words, self->word_alloc * sizeof(eword_t));\n> +\n> +\t\tmemset(self->words + old_size, 0x0,\n> +\t\t\t(self->word_alloc - old_size) * sizeof(eword_t));\n> +\t}\n> +\n> +\tself->words[block] |= MASK(pos);\n> +}\n> +\n> +void bitmap_clear(struct bitmap *self, size_t pos)\n> +{\n> +\tsize_t block = BLOCK(pos);\n> +\n> +\tif (block < self->word_alloc)\n> +\t\tself->words[block] &= ~MASK(pos);\n> +}\n> +\n> +int bitmap_get(struct bitmap *self, size_t pos)\n> +{\n> +\tsize_t block = BLOCK(pos);\n> +\treturn block < self->word_alloc && (self->words[block] & MASK(pos)) != 0;\n> +}\n> +\n> +struct ewah_bitmap *bitmap_to_ewah(struct bitmap *bitmap)\n> +{\n> +\tstruct ewah_bitmap *ewah = ewah_new();\n> +\tsize_t i, running_empty_words = 0;\n> +\teword_t last_word = 0;\n> +\n> +\tfor (i = 0; i < bitmap->word_alloc; ++i) {\n> +\t\tif (bitmap->words[i] == 0) {\n> +\t\t\trunning_empty_words++;\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tif (last_word != 0) {\n> +\t\t\tewah_add(ewah, last_word);\n> +\t\t}\n> +\n> +\t\tif (running_empty_words > 0) {\n> +\t\t\tewah_add_empty_words(ewah, 0, running_empty_words);\n> +\t\t\trunning_empty_words = 0;\n> +\t\t}\n> +\n> +\t\tlast_word = bitmap->words[i];\n> +\t}\n> +\n> +\tewah_add(ewah, last_word);\n> +\treturn ewah;\n> +}\n> +\n> +struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah)\n> +{\n> +\tstruct bitmap *bitmap = bitmap_new();\n> +\tstruct ewah_iterator it;\n> +\teword_t blowup;\n> +\tsize_t i = 0;\n> +\n> +\tewah_iterator_init(&it, ewah);\n> +\n> +\twhile (ewah_iterator_next(&blowup, &it)) {\n> +\t\tif (i >= bitmap->word_alloc) {\n> +\t\t\tbitmap->word_alloc *= 1.5;\n> +\t\t\tbitmap->words = ewah_realloc(\n> +\t\t\t\tbitmap->words, bitmap->word_alloc * sizeof(eword_t));\n> +\t\t}\n> +\n> +\t\tbitmap->words[i++] = blowup;\n> +\t}\n> +\n> +\tbitmap->word_alloc = i;\n> +\treturn bitmap;\n> +}\n> +\n> +void bitmap_and_not(struct bitmap *self, struct bitmap *other)\n> +{\n> +\tconst size_t count = (self->word_alloc < other->word_alloc) ?\n> +\t\tself->word_alloc : other->word_alloc;\n> +\n> +\tsize_t i;\n> +\n> +\tfor (i = 0; i < count; ++i) {\n> +\t\tself->words[i] &= ~other->words[i];\n> +\t}\n> +}\n> +\n> +void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)\n> +{\n> +\tsize_t original_size = self->word_alloc;\n> +\tsize_t other_final = (other->bit_size / BITS_IN_WORD) + 1;\n> +\tsize_t i = 0;\n> +\tstruct ewah_iterator it;\n> +\teword_t word;\n> +\n> +\tif (self->word_alloc < other_final) {\n> +\t\tself->word_alloc = other_final;\n> +\t\tself->words = ewah_realloc(self->words, self->word_alloc * sizeof(eword_t));\n> +\t\tmemset(self->words + original_size, 0x0,\n> +\t\t\t(self->word_alloc - original_size) * sizeof(eword_t));\n> +\t}\n> +\n> +\tewah_iterator_init(&it, other);\n> +\n> +\twhile (ewah_iterator_next(&word, &it)) {\n> +\t\tself->words[i++] |= word;\n> +\t}\n> +}\n> +\n> +void bitmap_each_bit(struct bitmap *self, ewah_callback callback, void *data)\n> +{\n> +\tsize_t pos = 0, i;\n> +\n> +\tfor (i = 0; i < self->word_alloc; ++i) {\n> +\t\teword_t word = self->words[i];\n> +\t\tuint32_t offset;\n> +\n> +\t\tif (word == (eword_t)~0) {\n> +\t\t\tfor (offset = 0; offset < BITS_IN_WORD; ++offset) {\n> +\t\t\t\tcallback(pos++, data);\n> +\t\t\t}\n> +\t\t} else {\n> +\t\t\tfor (offset = 0; offset < BITS_IN_WORD; ++offset) {\n> +\t\t\t\tif ((word >> offset) == 0)\n> +\t\t\t\t\tbreak;\n> +\n> +\t\t\t\toffset += ewah_bit_ctz64(word >> offset);\n> +\t\t\t\tcallback(pos + offset, data);\n> +\t\t\t}\n> +\t\t\tpos += BITS_IN_WORD;\n> +\t\t}\n> +\t}\n> +}\n> +\n> +size_t bitmap_popcount(struct bitmap *self)\n> +{\n> +\tsize_t i, count = 0;\n> +\n> +\tfor (i = 0; i < self->word_alloc; ++i) {\n> +\t\tcount += ewah_bit_popcount64(self->words[i]);\n> +\t}\n> +\n> +\treturn count;\n> +}\n> +\n> +int bitmap_equals(struct bitmap *self, struct bitmap *other)\n> +{\n> +\tstruct bitmap *big, *small;\n> +\tsize_t i;\n> +\n> +\tif (self->word_alloc < other->word_alloc) {\n> +\t\tsmall = self;\n> +\t\tbig = other;\n> +\t} else {\n> +\t\tsmall = other;\n> +\t\tbig = self;\n> +\t}\n> +\n> +\tfor (i = 0; i < small->word_alloc; ++i) {\n> +\t\tif (small->words[i] != big->words[i])\n> +\t\t\treturn 0;\n> +\t}\n> +\n> +\tfor (; i < big->word_alloc; ++i) {\n> +\t\tif (big->words[i] != 0)\n> +\t\t\treturn 0;\n> +\t}\n> +\n> +\treturn 1;\n> +}\n> +\n> +void bitmap_reset(struct bitmap *bitmap)\n> +{\n> +\tmemset(bitmap->words, 0x0, bitmap->word_alloc * sizeof(eword_t));\n> +}\n> +\n> +void bitmap_free(struct bitmap *bitmap)\n> +{\n> +\tif (bitmap == NULL)\n> +\t\treturn;\n> +\n> +\tfree(bitmap->words);\n> +\tfree(bitmap);\n> +}\n> diff --git a/ewah/ewah_bitmap.c b/ewah/ewah_bitmap.c\n> new file mode 100644\n> index 0000000..b74a1eb\n> --- /dev/null\n> +++ b/ewah/ewah_bitmap.c\n> @@ -0,0 +1,701 @@\n> +/**\n> + * Copyright 2013, GitHub, Inc\n> + * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n> + *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n> + *\n> + * This program is free software; you can redistribute it and/or\n> + * modify it under the terms of the GNU General Public License\n> + * as published by the Free Software Foundation; either version 2\n> + * of the License, or (at your option) any later version.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + *\n> + * You should have received a copy of the GNU General Public License\n> + * along with this program; if not, write to the Free Software\n> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n> + */\n> +#include <assert.h>\n> +#include <stdlib.h>\n> +#include <unistd.h>\n> +#include <string.h>\n> +#include <stdio.h>\n> +\n> +#include \"ewok.h\"\n> +#include \"ewok_rlw.h\"\n> +\n> +static inline size_t min_size(size_t a, size_t b)\n> +{\n> +\treturn a < b ? a : b;\n> +}\n> +\n> +static inline size_t max_size(size_t a, size_t b)\n> +{\n> +\treturn a > b ? a : b;\n> +}\n> +\n> +static inline void buffer_grow(struct ewah_bitmap *self, size_t new_size)\n> +{\n> +\tsize_t rlw_offset = (uint8_t *)self->rlw - (uint8_t *)self->buffer;\n> +\n> +\tif (self->alloc_size >= new_size)\n> +\t\treturn;\n> +\n> +\tself->alloc_size = new_size;\n> +\tself->buffer = ewah_realloc(self->buffer, self->alloc_size * sizeof(eword_t));\n> +\tself->rlw = self->buffer + (rlw_offset / sizeof(size_t));\n> +}\n> +\n> +static inline void buffer_push(struct ewah_bitmap *self, eword_t value)\n> +{\n> +\tif (self->buffer_size + 1 >= self->alloc_size) {\n> +\t\tbuffer_grow(self, self->buffer_size * 3 / 2);\n> +\t}\n> +\n> +\tself->buffer[self->buffer_size++] = value;\n> +}\n> +\n> +static void buffer_push_rlw(struct ewah_bitmap *self, eword_t value)\n> +{\n> +\tbuffer_push(self, value);\n> +\tself->rlw = self->buffer + self->buffer_size - 1;\n> +}\n> +\n> +static size_t add_empty_words(struct ewah_bitmap *self, int v, size_t number)\n> +{\n> +\tsize_t added = 0;\n> +\n> +\tif (rlw_get_run_bit(self->rlw) != v && rlw_size(self->rlw) == 0) {\n> +\t\trlw_set_run_bit(self->rlw, v);\n> +\t}\n> +\telse if (rlw_get_literal_words(self->rlw) != 0 || rlw_get_run_bit(self->rlw) != v) {\n> +\t\tbuffer_push_rlw(self, 0);\n> +\t\tif (v) rlw_set_run_bit(self->rlw, v);\n> +\t\tadded++;\n> +\t}\n> +\n> +\teword_t runlen = rlw_get_running_len(self->rlw);\n> +\teword_t can_add = min_size(number, RLW_LARGEST_RUNNING_COUNT - runlen);\n> +\n> +\trlw_set_running_len(self->rlw, runlen + can_add);\n> +\tnumber -= can_add;\n> +\n> +\twhile (number >= RLW_LARGEST_RUNNING_COUNT) {\n> +\t\tbuffer_push_rlw(self, 0);\n> +\t\tadded++;\n> +\n> +\t\tif (v) rlw_set_run_bit(self->rlw, v);\n> +\t\trlw_set_running_len(self->rlw, RLW_LARGEST_RUNNING_COUNT);\n> +\n> +\t\tnumber -= RLW_LARGEST_RUNNING_COUNT;\n> +\t}\n> +\n> +\tif (number > 0) {\n> +\t\tbuffer_push_rlw(self, 0);\n> +\t\tadded++;\n> +\n> +\t\tif (v) rlw_set_run_bit(self->rlw, v);\n> +\t\trlw_set_running_len(self->rlw, number);\n> +\t}\n> +\n> +\treturn added;\n> +}\n> +\n> +size_t ewah_add_empty_words(struct ewah_bitmap *self, int v, size_t number)\n> +{\n> +\tif (number == 0)\n> +\t\treturn 0;\n> +\n> +\tself->bit_size += number * BITS_IN_WORD;\n> +\treturn add_empty_words(self, v, number);\n> +}\n> +\n> +static size_t add_literal(struct ewah_bitmap *self, eword_t new_data)\n> +{\n> +\teword_t current_num = rlw_get_literal_words(self->rlw);\n> +\n> +\tif (current_num >= RLW_LARGEST_LITERAL_COUNT) {\n> +\t\tbuffer_push_rlw(self, 0);\n> +\n> +\t\trlw_set_literal_words(self->rlw, 1);\n> +\t\tbuffer_push(self, new_data);\n> +\t\treturn 2;\n> +\t}\n> +\n> +\trlw_set_literal_words(self->rlw, current_num + 1);\n> +\n> +\t/* sanity check */\n> +\tassert(rlw_get_literal_words(self->rlw) == current_num + 1);\n> +\n> +\tbuffer_push(self, new_data);\n> +\treturn 1;\n> +}\n> +\n> +void ewah_add_dirty_words(\n> +\tstruct ewah_bitmap *self, const eword_t *buffer, size_t number, int negate)\n> +{\n> +\tsize_t literals, can_add;\n> +\n> +\twhile (1) {\n> +\t\tliterals = rlw_get_literal_words(self->rlw);\n> +\t\tcan_add = min_size(number, RLW_LARGEST_LITERAL_COUNT - literals);\n> +\n> +\t\trlw_set_literal_words(self->rlw, literals + can_add);\n> +\n> +\t\tif (self->buffer_size + can_add >= self->alloc_size) {\n> +\t\t\tbuffer_grow(self, (self->buffer_size + can_add) * 3 / 2);\n> +\t\t}\n> +\n> +\t\tif (negate) {\n> +\t\t\tsize_t i;\n> +\t\t\tfor (i = 0; i < can_add; ++i)\n> +\t\t\t\tself->buffer[self->buffer_size++] = ~buffer[i];\n> +\t\t} else {\n> +\t\t\tmemcpy(self->buffer + self->buffer_size, buffer, can_add * sizeof(eword_t));\n> +\t\t\tself->buffer_size += can_add;\n> +\t\t}\n> +\n> +\t\tself->bit_size += can_add * BITS_IN_WORD;\n> +\n> +\t\tif (number - can_add == 0)\n> +\t\t\tbreak;\n> +\n> +\t\tbuffer_push_rlw(self, 0);\n> +\t\tbuffer += can_add;\n> +\t\tnumber -= can_add;\n> +\t}\n> +}\n> +\n> +static size_t add_empty_word(struct ewah_bitmap *self, int v)\n> +{\n> +\tint no_literal = (rlw_get_literal_words(self->rlw) == 0);\n> +\teword_t run_len = rlw_get_running_len(self->rlw);\n> +\n> +\tif (no_literal && run_len == 0) {\n> +\t\trlw_set_run_bit(self->rlw, v);\n> +\t\tassert(rlw_get_run_bit(self->rlw) == v);\n> +\t}\n> +\n> +\tif (no_literal && rlw_get_run_bit(self->rlw) == v &&\n> +\t\trun_len < RLW_LARGEST_RUNNING_COUNT) {\n> +\t\trlw_set_running_len(self->rlw, run_len + 1);\n> +\t\tassert(rlw_get_running_len(self->rlw) == run_len + 1);\n> +\t\treturn 0;\n> +\t}\n> +\n> +\telse {\n> +\t\tbuffer_push_rlw(self, 0);\n> +\n> +\t\tassert(rlw_get_running_len(self->rlw) == 0);\n> +\t\tassert(rlw_get_run_bit(self->rlw) == 0);\n> +\t\tassert(rlw_get_literal_words(self->rlw) == 0);\n> +\n> +\t\trlw_set_run_bit(self->rlw, v);\n> +\t\tassert(rlw_get_run_bit(self->rlw) == v);\n> +\n> +\t\trlw_set_running_len(self->rlw, 1);\n> +\t\tassert(rlw_get_running_len(self->rlw) == 1);\n> +\t\tassert(rlw_get_literal_words(self->rlw) == 0);\n> +\t\treturn 1;\n> +\t}\n> +}\n> +\n> +size_t ewah_add(struct ewah_bitmap *self, eword_t word)\n> +{\n> +\tself->bit_size += BITS_IN_WORD;\n> +\n> +\tif (word == 0)\n> +\t\treturn add_empty_word(self, 0);\n> +\n> +\tif (word == (eword_t)(~0))\n> +\t\treturn add_empty_word(self, 1);\n> +\n> +\treturn add_literal(self, word);\n> +}\n> +\n> +void ewah_set(struct ewah_bitmap *self, size_t i)\n> +{\n> +\tconst size_t dist =\n> +\t\t(i + BITS_IN_WORD) / BITS_IN_WORD -\n> +\t\t(self->bit_size + BITS_IN_WORD - 1) / BITS_IN_WORD;\n> +\n> +\tassert(i >= self->bit_size);\n> +\n> +\tself->bit_size = i + 1;\n> +\n> +\tif (dist > 0) {\n> +\t\tif (dist > 1)\n> +\t\t\tadd_empty_words(self, 0, dist - 1);\n> +\n> +\t\tadd_literal(self, (eword_t)1 << (i % BITS_IN_WORD));\n> +\t\treturn;\n> +\t}\n> +\n> +\tif (rlw_get_literal_words(self->rlw) == 0) {\n> +\t\trlw_set_running_len(self->rlw, rlw_get_running_len(self->rlw) - 1);\n> +\t\tadd_literal(self, (eword_t)1 << (i % BITS_IN_WORD));\n> +\t\treturn;\n> +\t}\n> +\n> +\tself->buffer[self->buffer_size - 1] |= ((eword_t)1 << (i % BITS_IN_WORD));\n> +\n> +\t/* check if we just completed a stream of 1s */\n> +\tif (self->buffer[self->buffer_size - 1] == (eword_t)(~0)) {\n> +\t\tself->buffer[--self->buffer_size] = 0;\n> +\t\trlw_set_literal_words(self->rlw, rlw_get_literal_words(self->rlw) - 1);\n> +\t\tadd_empty_word(self, 1);\n> +\t}\n> +}\n> +\n> +void ewah_each_bit(struct ewah_bitmap *self, void (*callback)(size_t, void*), void *payload)\n> +{\n> +\tsize_t pos = 0;\n> +\tsize_t pointer = 0;\n> +\tsize_t k;\n> +\n> +\twhile (pointer < self->buffer_size) {\n> +\t\teword_t *word = &self->buffer[pointer];\n> +\n> +\t\tif (rlw_get_run_bit(word)) {\n> +\t\t\tsize_t len = rlw_get_running_len(word) * BITS_IN_WORD;\n> +\t\t\tfor (k = 0; k < len; ++k, ++pos) {\n> +\t\t\t\tcallback(pos, payload);\n> +\t\t\t}\n> +\t\t} else {\n> +\t\t\tpos += rlw_get_running_len(word) * BITS_IN_WORD;\n> +\t\t}\n> +\n> +\t\t++pointer;\n> +\n> +\t\tfor (k = 0; k < rlw_get_literal_words(word); ++k) {\n> +\t\t\tint c;\n> +\n> +\t\t\t/* todo: zero count optimization */\n> +\t\t\tfor (c = 0; c < BITS_IN_WORD; ++c, ++pos) {\n> +\t\t\t\tif ((self->buffer[pointer] & ((eword_t)1 << c)) != 0) {\n> +\t\t\t\t\tcallback(pos, payload);\n> +\t\t\t\t}\n> +\t\t\t}\n> +\n> +\t\t\t++pointer;\n> +\t\t}\n> +\t}\n> +}\n> +\n> +struct ewah_bitmap *ewah_new(void)\n> +{\n> +\tstruct ewah_bitmap *self;\n> +\n> +\tself = ewah_malloc(sizeof(struct ewah_bitmap));\n> +\tif (self == NULL)\n> +\t\treturn NULL;\n> +\n> +\tself->buffer = ewah_malloc(32 * sizeof(eword_t));\n> +\tself->alloc_size = 32;\n> +\n> +\tewah_clear(self);\n> +\treturn self;\n> +}\n> +\n> +void ewah_clear(struct ewah_bitmap *self)\n> +{\n> +\tself->buffer_size = 1;\n> +\tself->buffer[0] = 0;\n> +\tself->bit_size = 0;\n> +\tself->rlw = self->buffer;\n> +}\n> +\n> +void ewah_free(struct ewah_bitmap *self)\n> +{\n> +\tif (self->alloc_size)\n> +\t\tfree(self->buffer);\n> +\n> +\tfree(self);\n> +}\n> +\n> +static void read_new_rlw(struct ewah_iterator *it)\n> +{\n> +\tconst eword_t *word = NULL;\n> +\n> +\tit->literals = 0;\n> +\tit->compressed = 0;\n> +\n> +\twhile (1) {\n> +\t\tword = &it->buffer[it->pointer];\n> +\n> +\t\tit->rl = rlw_get_running_len(word);\n> +\t\tit->lw = rlw_get_literal_words(word);\n> +\t\tit->b = rlw_get_run_bit(word);\n> +\n> +\t\tif (it->rl || it->lw)\n> +\t\t\treturn;\n> +\n> +\t\tif (it->pointer < it->buffer_size - 1) {\n> +\t\t\tit->pointer++;\n> +\t\t} else {\n> +\t\t\tit->pointer = it->buffer_size;\n> +\t\t\treturn;\n> +\t\t}\n> +\t}\n> +}\n> +\n> +int ewah_iterator_next(eword_t *next, struct ewah_iterator *it)\n> +{\n> +\tif (it->pointer >= it->buffer_size)\n> +\t\treturn 0;\n> +\n> +\tif (it->compressed < it->rl) {\n> +\t\tit->compressed++;\n> +\t\t*next = it->b ? (eword_t)(~0) : 0;\n> +\t} else {\n> +\t\tassert(it->literals < it->lw);\n> +\n> +\t\tit->literals++;\n> +\t\tit->pointer++;\n> +\n> +\t\tassert(it->pointer < it->buffer_size);\n> +\n> +\t\t*next = it->buffer[it->pointer];\n> +\t}\n> +\n> +\tif (it->compressed == it->rl && it->literals == it->lw) {\n> +\t\tif (++it->pointer < it->buffer_size)\n> +\t\t\tread_new_rlw(it);\n> +\t}\n> +\n> +\treturn 1;\n> +}\n> +\n> +void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent)\n> +{\n> +\tit->buffer = parent->buffer;\n> +\tit->buffer_size = parent->buffer_size;\n> +\tit->pointer = 0;\n> +\n> +\tit->lw = 0;\n> +\tit->rl = 0;\n> +\tit->compressed = 0;\n> +\tit->literals = 0;\n> +\tit->b = 0;\n> +\n> +\tif (it->pointer < it->buffer_size)\n> +\t\tread_new_rlw(it);\n> +}\n> +\n> +void ewah_dump(struct ewah_bitmap *self)\n> +{\n> +\tsize_t i;\n> +\tfprintf(stderr, \"%zu bits | %zu words | \", self->bit_size, self->buffer_size);\n> +\n> +\tfor (i = 0; i < self->buffer_size; ++i)\n> +\t\tfprintf(stderr, \"%016llx \", (unsigned long long)self->buffer[i]);\n> +\n> +\tfprintf(stderr, \"\\n\");\n> +}\n> +\n> +void ewah_not(struct ewah_bitmap *self)\n> +{\n> +\tsize_t pointer = 0;\n> +\n> +\twhile (pointer < self->buffer_size) {\n> +\t\teword_t *word = &self->buffer[pointer];\n> +\t\tsize_t literals, k;\n> +\n> +\t\trlw_xor_run_bit(word);\n> +\t\t++pointer;\n> +\n> +\t\tliterals = rlw_get_literal_words(word);\n> +\t\tfor (k = 0; k < literals; ++k) {\n> +\t\t\tself->buffer[pointer] = ~self->buffer[pointer];\n> +\t\t\t++pointer;\n> +\t\t}\n> +\t}\n> +}\n> +\n> +void ewah_xor(\n> +\tstruct ewah_bitmap *ewah_i,\n> +\tstruct ewah_bitmap *ewah_j,\n> +\tstruct ewah_bitmap *out)\n> +{\n> +\tstruct rlw_iterator rlw_i;\n> +\tstruct rlw_iterator rlw_j;\n> +\n> +\trlwit_init(&rlw_i, ewah_i);\n> +\trlwit_init(&rlw_j, ewah_j);\n> +\n> +\twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n> +\t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n> +\t\t\tstruct rlw_iterator *prey, *predator;\n> +\t\t\tsize_t index;\n> +\t\t\tint negate_words;\n> +\n> +\t\t\tif (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {\n> +\t\t\t\tprey = &rlw_i;\n> +\t\t\t\tpredator = &rlw_j;\n> +\t\t\t} else {\n> +\t\t\t\tprey = &rlw_j;\n> +\t\t\t\tpredator = &rlw_i;\n> +\t\t\t}\n> +\n> +\t\t\tnegate_words = !!predator->rlw.running_bit;\n> +\t\t\tindex = rlwit_discharge(prey, out, predator->rlw.running_len, negate_words);\n> +\n> +\t\t\tewah_add_empty_words(out, negate_words, predator->rlw.running_len - index);\n> +\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n> +\t\t}\n> +\n> +\t\tsize_t literals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n> +\n> +\t\tif (literals) {\n> +\t\t\tsize_t k;\n> +\n> +\t\t\tfor (k = 0; k < literals; ++k) {\n> +\t\t\t\tewah_add(out,\n> +\t\t\t\t\trlw_i.buffer[rlw_i.literal_word_start + k] ^\n> +\t\t\t\t\trlw_j.buffer[rlw_j.literal_word_start + k]\n> +\t\t\t\t);\n> +\t\t\t}\n> +\n> +\t\t\trlwit_discard_first_words(&rlw_i, literals);\n> +\t\t\trlwit_discard_first_words(&rlw_j, literals);\n> +\t\t}\n> +\t}\n> +\n> +\tif (rlwit_word_size(&rlw_i) > 0) {\n> +\t\trlwit_discharge(&rlw_i, out, ~0, 0);\n> +\t} else {\n> +\t\trlwit_discharge(&rlw_j, out, ~0, 0);\n> +\t}\n> +\n> +\tout->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);\n> +}\n> +\n> +void ewah_and(\n> +\tstruct ewah_bitmap *ewah_i,\n> +\tstruct ewah_bitmap *ewah_j,\n> +\tstruct ewah_bitmap *out)\n> +{\n> +\tstruct rlw_iterator rlw_i;\n> +\tstruct rlw_iterator rlw_j;\n> +\n> +\trlwit_init(&rlw_i, ewah_i);\n> +\trlwit_init(&rlw_j, ewah_j);\n> +\n> +\twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n> +\t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n> +\t\t\tstruct rlw_iterator *prey, *predator;\n> +\n> +\t\t\tif (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {\n> +\t\t\t\tprey = &rlw_i;\n> +\t\t\t\tpredator = &rlw_j;\n> +\t\t\t} else {\n> +\t\t\t\tprey = &rlw_j;\n> +\t\t\t\tpredator = &rlw_i;\n> +\t\t\t}\n> +\n> +\t\t\tif (predator->rlw.running_bit == 0) {\n> +\t\t\t\tewah_add_empty_words(out, 0, predator->rlw.running_len);\n> +\t\t\t\trlwit_discard_first_words(prey, predator->rlw.running_len);\n> +\t\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n> +\t\t\t} else {\n> +\t\t\t\tsize_t index;\n> +\t\t\t\tindex = rlwit_discharge(prey, out, predator->rlw.running_len, 0);\n> +\t\t\t\tewah_add_empty_words(out, 0, predator->rlw.running_len - index);\n> +\t\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tsize_t literals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n> +\n> +\t\tif (literals) {\n> +\t\t\tsize_t k;\n> +\n> +\t\t\tfor (k = 0; k < literals; ++k) {\n> +\t\t\t\tewah_add(out,\n> +\t\t\t\t\trlw_i.buffer[rlw_i.literal_word_start + k] &\n> +\t\t\t\t\trlw_j.buffer[rlw_j.literal_word_start + k]\n> +\t\t\t\t);\n> +\t\t\t}\n> +\n> +\t\t\trlwit_discard_first_words(&rlw_i, literals);\n> +\t\t\trlwit_discard_first_words(&rlw_j, literals);\n> +\t\t}\n> +\t}\n> +\n> +\tif (rlwit_word_size(&rlw_i) > 0) {\n> +\t\trlwit_discharge_empty(&rlw_i, out);\n> +\t} else {\n> +\t\trlwit_discharge_empty(&rlw_j, out);\n> +\t}\n> +\n> +\tout->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);\n> +}\n> +\n> +void ewah_and_not(\n> +\tstruct ewah_bitmap *ewah_i,\n> +\tstruct ewah_bitmap *ewah_j,\n> +\tstruct ewah_bitmap *out)\n> +{\n> +\tstruct rlw_iterator rlw_i;\n> +\tstruct rlw_iterator rlw_j;\n> +\n> +\trlwit_init(&rlw_i, ewah_i);\n> +\trlwit_init(&rlw_j, ewah_j);\n> +\n> +\twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n> +\t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n> +\t\t\tstruct rlw_iterator *prey, *predator;\n> +\n> +\t\t\tif (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {\n> +\t\t\t\tprey = &rlw_i;\n> +\t\t\t\tpredator = &rlw_j;\n> +\t\t\t} else {\n> +\t\t\t\tprey = &rlw_j;\n> +\t\t\t\tpredator = &rlw_i;\n> +\t\t\t}\n> +\n> +\t\t\tif ((predator->rlw.running_bit && prey == &rlw_i) ||\n> +\t\t\t\t(!predator->rlw.running_bit && prey != &rlw_i)) {\n> +\t\t\t\tewah_add_empty_words(out, 0, predator->rlw.running_len);\n> +\t\t\t\trlwit_discard_first_words(prey, predator->rlw.running_len);\n> +\t\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n> +\t\t\t} else {\n> +\t\t\t\tsize_t index;\n> +\t\t\t\tint negate_words;\n> +\n> +\t\t\t\tnegate_words = (&rlw_i != prey);\n> +\t\t\t\tindex = rlwit_discharge(prey, out, predator->rlw.running_len, negate_words);\n> +\t\t\t\tewah_add_empty_words(out, negate_words, predator->rlw.running_len - index);\n> +\t\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tsize_t literals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n> +\n> +\t\tif (literals) {\n> +\t\t\tsize_t k;\n> +\n> +\t\t\tfor (k = 0; k < literals; ++k) {\n> +\t\t\t\tewah_add(out,\n> +\t\t\t\t\trlw_i.buffer[rlw_i.literal_word_start + k] &\n> +\t\t\t\t\t~(rlw_j.buffer[rlw_j.literal_word_start + k])\n> +\t\t\t\t);\n> +\t\t\t}\n> +\n> +\t\t\trlwit_discard_first_words(&rlw_i, literals);\n> +\t\t\trlwit_discard_first_words(&rlw_j, literals);\n> +\t\t}\n> +\t}\n> +\n> +\tif (rlwit_word_size(&rlw_i) > 0) {\n> +\t\trlwit_discharge(&rlw_i, out, ~0, 0);\n> +\t} else {\n> +\t\trlwit_discharge_empty(&rlw_j, out);\n> +\t}\n> +\n> +\tout->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);\n> +}\n> +\n> +void ewah_or(\n> +\tstruct ewah_bitmap *ewah_i,\n> +\tstruct ewah_bitmap *ewah_j,\n> +\tstruct ewah_bitmap *out)\n> +{\n> +\tstruct rlw_iterator rlw_i;\n> +\tstruct rlw_iterator rlw_j;\n> +\n> +\trlwit_init(&rlw_i, ewah_i);\n> +\trlwit_init(&rlw_j, ewah_j);\n> +\n> +\twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n> +\t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n> +\t\t\tstruct rlw_iterator *prey, *predator;\n> +\n> +\t\t\tif (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {\n> +\t\t\t\tprey = &rlw_i;\n> +\t\t\t\tpredator = &rlw_j;\n> +\t\t\t} else {\n> +\t\t\t\tprey = &rlw_j;\n> +\t\t\t\tpredator = &rlw_i;\n> +\t\t\t}\n> +\n> +\n> +\t\t\tif (predator->rlw.running_bit) {\n> +\t\t\t\tewah_add_empty_words(out, 0, predator->rlw.running_len);\n> +\t\t\t\trlwit_discard_first_words(prey, predator->rlw.running_len);\n> +\t\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n> +\t\t\t} else {\n> +\t\t\t\tsize_t index;\n> +\t\t\t\tindex = rlwit_discharge(prey, out, predator->rlw.running_len, 0);\n> +\t\t\t\tewah_add_empty_words(out, 0, predator->rlw.running_len - index);\n> +\t\t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tsize_t literals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n> +\n> +\t\tif (literals) {\n> +\t\t\tsize_t k;\n> +\n> +\t\t\tfor (k = 0; k < literals; ++k) {\n> +\t\t\t\tewah_add(out,\n> +\t\t\t\t\trlw_i.buffer[rlw_i.literal_word_start + k] |\n> +\t\t\t\t\trlw_j.buffer[rlw_j.literal_word_start + k]\n> +\t\t\t\t);\n> +\t\t\t}\n> +\n> +\t\t\trlwit_discard_first_words(&rlw_i, literals);\n> +\t\t\trlwit_discard_first_words(&rlw_j, literals);\n> +\t\t}\n> +\t}\n> +\n> +\tif (rlwit_word_size(&rlw_i) > 0) {\n> +\t\trlwit_discharge(&rlw_i, out, ~0, 0);\n> +\t} else {\n> +\t\trlwit_discharge(&rlw_j, out, ~0, 0);\n> +\t}\n> +\n> +\tout->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);\n> +}\n> +\n> +\n> +#define BITMAP_POOL_MAX 16\n> +static struct ewah_bitmap *bitmap_pool[BITMAP_POOL_MAX];\n> +static size_t bitmap_pool_size;\n> +\n> +struct ewah_bitmap *ewah_pool_new(void)\n> +{\n> +\tif (bitmap_pool_size)\n> +\t\treturn bitmap_pool[--bitmap_pool_size];\n> +\n> +\treturn ewah_new();\n> +}\n> +\n> +void ewah_pool_free(struct ewah_bitmap *self)\n> +{\n> +\tif (self == NULL)\n> +\t\treturn;\n> +\n> +\tif (bitmap_pool_size == BITMAP_POOL_MAX ||\n> +\t\tself->alloc_size == 0) {\n> +\t\tewah_free(self);\n> +\t\treturn;\n> +\t}\n> +\n> +\tewah_clear(self);\n> +\tbitmap_pool[bitmap_pool_size++] = self;\n> +}\n> +\n> +uint32_t ewah_checksum(struct ewah_bitmap *self)\n> +{\n> +\tconst uint8_t *p = (uint8_t *)self->buffer;\n> +\tuint32_t crc = (uint32_t)self->bit_size;\n> +\tsize_t size = self->buffer_size * sizeof(eword_t);\n> +\n> +\twhile (size--)\n> +\t\tcrc = (crc << 5) - crc + (uint32_t)*p++;\n> +\n> +\treturn crc;\n> +}\n> diff --git a/ewah/ewah_io.c b/ewah/ewah_io.c\n> new file mode 100644\n> index 0000000..db6c062\n> --- /dev/null\n> +++ b/ewah/ewah_io.c\n> @@ -0,0 +1,192 @@\n> +/**\n> + * Copyright 2013, GitHub, Inc\n> + * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n> + *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n> + *\n> + * This program is free software; you can redistribute it and/or\n> + * modify it under the terms of the GNU General Public License\n> + * as published by the Free Software Foundation; either version 2\n> + * of the License, or (at your option) any later version.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + *\n> + * You should have received a copy of the GNU General Public License\n> + * along with this program; if not, write to the Free Software\n> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n> + */\n> +#include <stdlib.h>\n> +#include <unistd.h>\n> +#include <stdio.h>\n> +\n> +#include \"git-compat-util.h\"\n> +#include \"ewok.h\"\n> +\n> +int ewah_serialize_native(struct ewah_bitmap *self, int fd)\n> +{\n> +\tuint32_t write32;\n> +\tsize_t to_write = self->buffer_size * 8;\n> +\n> +\t/* 32 bit -- bit size fr the map */\n> +\twrite32 = (uint32_t)self->bit_size;\n> +\tif (write(fd, &write32, 4) != 4)\n> +\t\treturn -1;\n> +\n> +\t/** 32 bit -- number of compressed 64-bit words */\n> +\twrite32 = (uint32_t)self->buffer_size;\n> +\tif (write(fd, &write32, 4) != 4)\n> +\t\treturn -1;\n> +\n> +\tif (write(fd, self->buffer, to_write) != to_write)\n> +\t\treturn -1;\n> +\n> +\t/** 32 bit -- position for the RLW */\n> +\twrite32 = self->rlw - self->buffer;\n> +\tif (write(fd, &write32, 4) != 4)\n> +\t\treturn -1;\n> +\n> +\treturn (3 * 4) + to_write;\n> +}\n> +\n> +int ewah_serialize_to(struct ewah_bitmap *self,\n> +\t\t      int (*write_fun)(void *, const void *, size_t),\n> +\t\t      void *data)\n> +{\n> +\tsize_t i;\n> +\teword_t dump[2048];\n> +\tconst size_t words_per_dump = sizeof(dump) / sizeof(eword_t);\n> +\n> +\t/* 32 bit -- bit size fr the map */\n> +\tuint32_t bitsize =  htonl((uint32_t)self->bit_size);\n> +\tif (write_fun(data, &bitsize, 4) != 4)\n> +\t\treturn -1;\n> +\n> +\t/** 32 bit -- number of compressed 64-bit words */\n> +\tuint32_t word_count =  htonl((uint32_t)self->buffer_size);\n> +\tif (write_fun(data, &word_count, 4) != 4)\n> +\t\treturn -1;\n> +\n> +\t/** 64 bit x N -- compressed words */\n> +\tconst eword_t *buffer = self->buffer;\n> +\tsize_t words_left = self->buffer_size;\n> +\n> +\twhile (words_left >= words_per_dump) {\n> +\t\tfor (i = 0; i < words_per_dump; ++i, ++buffer)\n> +\t\t\tdump[i] = htonll(*buffer);\n> +\n> +\t\tif (write_fun(data, dump, sizeof(dump)) != sizeof(dump))\n> +\t\t\treturn -1;\n> +\n> +\t\twords_left -= words_per_dump;\n> +\t}\n> +\n> +\tif (words_left) {\n> +\t\tfor (i = 0; i < words_left; ++i, ++buffer)\n> +\t\t\tdump[i] = htonll(*buffer);\n> +\n> +\t\tif (write_fun(data, dump, words_left * 8) != words_left * 8)\n> +\t\t\treturn -1;\n> +\t}\n> +\n> +\t/** 32 bit -- position for the RLW */\n> +\tuint32_t rlw_pos = (uint8_t*)self->rlw - (uint8_t *)self->buffer;\n> +\trlw_pos = htonl(rlw_pos / sizeof(eword_t));\n> +\n> +\tif (write_fun(data, &rlw_pos, 4) != 4)\n> +\t\treturn -1;\n> +\n> +\treturn (3 * 4) + (self->buffer_size * 8);\n> +}\n> +\n> +static int write_helper(void *fd, const void *buf, size_t len)\n> +{\n> +\treturn write((intptr_t)fd, buf, len);\n> +}\n> +\n> +int ewah_serialize(struct ewah_bitmap *self, int fd)\n> +{\n> +\treturn ewah_serialize_to(self, write_helper, (void *)(intptr_t)fd);\n> +}\n> +\n> +int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len)\n> +{\n> +\tuint32_t *read32 = map;\n> +\teword_t *read64;\n> +\tsize_t i;\n> +\n> +\tself->bit_size = ntohl(*read32++);\n> +\tself->buffer_size = self->alloc_size = ntohl(*read32++);\n> +\tself->buffer = ewah_realloc(self->buffer, self->alloc_size * sizeof(eword_t));\n> +\n> +\tif (!self->buffer)\n> +\t\treturn -1;\n> +\n> +\tfor (i = 0, read64 = (void *)read32; i < self->buffer_size; ++i) {\n> +\t\tself->buffer[i] = ntohll(*read64++);\n> +\t}\n> +\n> +\tread32 = (void *)read64;\n> +\tself->rlw = self->buffer + ntohl(*read32++);\n> +\n> +\treturn (3 * 4) + (self->buffer_size * 8);\n> +}\n> +\n> +int ewah_deserialize(struct ewah_bitmap *self, int fd)\n> +{\n> +\tsize_t i;\n> +\teword_t dump[2048];\n> +\tconst size_t words_per_dump = sizeof(dump) / sizeof(eword_t);\n> +\n> +\tewah_clear(self);\n> +\n> +\t/* 32 bit -- bit size fr the map */\n> +\tuint32_t bitsize;\n> +\tif (read(fd, &bitsize, 4) != 4)\n> +\t\treturn -1;\n> +\n> +\tself->bit_size = (size_t)ntohl(bitsize);\n> +\n> +\t/** 32 bit -- number of compressed 64-bit words */\n> +\tuint32_t word_count;\n> +\tif (read(fd, &word_count, 4) != 4)\n> +\t\treturn -1;\n> +\n> +\tself->buffer_size = self->alloc_size = (size_t)ntohl(word_count);\n> +\tself->buffer = ewah_realloc(self->buffer, self->alloc_size * sizeof(eword_t));\n> +\n> +\tif (!self->buffer)\n> +\t\treturn -1;\n> +\n> +\t/** 64 bit x N -- compressed words */\n> +\teword_t *buffer = self->buffer;\n> +\tsize_t words_left = self->buffer_size;\n> +\n> +\twhile (words_left >= words_per_dump) {\n> +\t\tif (read(fd, dump, sizeof(dump)) != sizeof(dump))\n> +\t\t\treturn -1;\n> +\n> +\t\tfor (i = 0; i < words_per_dump; ++i, ++buffer)\n> +\t\t\t*buffer = ntohll(dump[i]);\n> +\n> +\t\twords_left -= words_per_dump;\n> +\t}\n> +\n> +\tif (words_left) {\n> +\t\tif (read(fd, dump, words_left * 8) != words_left * 8)\n> +\t\t\treturn -1;\n> +\n> +\t\tfor (i = 0; i < words_left; ++i, ++buffer)\n> +\t\t\t*buffer = ntohll(dump[i]);\n> +\t}\n> +\n> +\t/** 32 bit -- position for the RLW */\n> +\tuint32_t rlw_pos;\n> +\tif (read(fd, &rlw_pos, 4) != 4)\n> +\t\treturn -1;\n> +\n> +\tself->rlw = self->buffer + ntohl(rlw_pos);\n> +\n> +\treturn 0;\n> +}\n> diff --git a/ewah/ewah_rlw.c b/ewah/ewah_rlw.c\n> new file mode 100644\n> index 0000000..0281025\n> --- /dev/null\n> +++ b/ewah/ewah_rlw.c\n> @@ -0,0 +1,120 @@\n> +/**\n> + * Copyright 2013, GitHub, Inc\n> + * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n> + *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n> + *\n> + * This program is free software; you can redistribute it and/or\n> + * modify it under the terms of the GNU General Public License\n> + * as published by the Free Software Foundation; either version 2\n> + * of the License, or (at your option) any later version.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + *\n> + * You should have received a copy of the GNU General Public License\n> + * along with this program; if not, write to the Free Software\n> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n> + */\n> +#include <assert.h>\n> +#include <stdlib.h>\n> +#include <unistd.h>\n> +#include <string.h>\n> +\n> +#include \"ewok.h\"\n> +#include \"ewok_rlw.h\"\n> +\n> +static inline int next_word(struct rlw_iterator *it)\n> +{\n> +\tif (it->pointer >= it->size)\n> +\t\treturn 0;\n> +\n> +\tit->rlw.word = &it->buffer[it->pointer];\n> +\tit->pointer += rlw_get_literal_words(it->rlw.word) + 1;\n> +\n> +\tit->rlw.literal_words = rlw_get_literal_words(it->rlw.word);\n> +\tit->rlw.running_len = rlw_get_running_len(it->rlw.word);\n> +\tit->rlw.running_bit = rlw_get_run_bit(it->rlw.word);\n> +\tit->rlw.literal_word_offset = 0;\n> +\n> +\treturn 1;\n> +}\n> +\n> +void rlwit_init(struct rlw_iterator *it, struct ewah_bitmap *from_ewah)\n> +{\n> +\tit->buffer = from_ewah->buffer;\n> +\tit->size = from_ewah->buffer_size;\n> +\tit->pointer = 0;\n> +\n> +\tnext_word(it);\n> +\n> +\tit->literal_word_start = rlwit_literal_words(it) + it->rlw.literal_word_offset;\n> +}\n> +\n> +void rlwit_discard_first_words(struct rlw_iterator *it, size_t x)\n> +{\n> +\twhile (x > 0) {\n> +\t\tsize_t discard;\n> +\n> +\t\tif (it->rlw.running_len > x) {\n> +\t\t\tit->rlw.running_len -= x;\n> +\t\t\treturn;\n> +\t\t}\n> +\n> +\t\tx -= it->rlw.running_len;\n> +\t\tit->rlw.running_len = 0;\n> +\n> +\t\tdiscard = (x > it->rlw.literal_words) ? it->rlw.literal_words : x;\n> +\n> +\t\tit->literal_word_start += discard;\n> +\t\tit->rlw.literal_words -= discard;\n> +\t\tx -= discard;\n> +\n> +\t\tif (x > 0 || rlwit_word_size(it) == 0) {\n> +\t\t\tif (!next_word(it))\n> +\t\t\t\tbreak;\n> +\n> +\t\t\tit->literal_word_start =\n> +\t\t\t\trlwit_literal_words(it) + it->rlw.literal_word_offset;\n> +\t\t}\n> +\t}\n> +}\n> +\n> +size_t rlwit_discharge(\n> +\tstruct rlw_iterator *it, struct ewah_bitmap *out, size_t max, int negate)\n> +{\n> +\tsize_t index = 0;\n> +\n> +\twhile (index < max && rlwit_word_size(it) > 0) {\n> +\t\tsize_t pd, pl = it->rlw.running_len;\n> +\n> +\t\tif (index + pl > max) {\n> +\t\t\tpl = max - index;\n> +\t\t}\n> +\n> +\t\tewah_add_empty_words(out, it->rlw.running_bit ^ negate, pl);\n> +\t\tindex += pl;\n> +\n> +\t\tpd = it->rlw.literal_words;\n> +\t\tif (pd + index > max) {\n> +\t\t\tpd = max - index;\n> +\t\t}\n> +\n> +\t\tewah_add_dirty_words(out,\n> +\t\t\tit->buffer + it->literal_word_start, pd, negate);\n> +\n> +\t\trlwit_discard_first_words(it, pd + pl);\n> +\t\tindex += pd;\n> +\t}\n> +\n> +\treturn index;\n> +}\n> +\n> +void rlwit_discharge_empty(struct rlw_iterator *it, struct ewah_bitmap *out)\n> +{\n> +\twhile (rlwit_word_size(it) > 0) {\n> +\t\tewah_add_empty_words(out, 0, rlwit_word_size(it));\n> +\t\trlwit_discard_first_words(it, rlwit_word_size(it));\n> +\t}\n> +}\n> diff --git a/ewah/ewok.h b/ewah/ewok.h\n> new file mode 100644\n> index 0000000..3db7703\n> --- /dev/null\n> +++ b/ewah/ewok.h\n> @@ -0,0 +1,223 @@\n> +/**\n> + * Copyright 2013, GitHub, Inc\n> + * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n> + *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n> + *\n> + * This program is free software; you can redistribute it and/or\n> + * modify it under the terms of the GNU General Public License\n> + * as published by the Free Software Foundation; either version 2\n> + * of the License, or (at your option) any later version.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + *\n> + * You should have received a copy of the GNU General Public License\n> + * along with this program; if not, write to the Free Software\n> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n> + */\n> +#ifndef __EWOK_BITMAP_C__\n> +#define __EWOK_BITMAP_C__\n> +\n> +#include <stdint.h>\n> +\n> +#ifndef ewah_malloc\n> +#\tdefine ewah_malloc malloc\n> +#endif\n> +#ifndef ewah_realloc\n> +#\tdefine ewah_realloc realloc\n> +#endif\n> +#ifndef ewah_calloc\n> +#\tdefine ewah_calloc calloc\n> +#endif\n> +\n> +typedef uint64_t eword_t;\n> +#define BITS_IN_WORD (sizeof(eword_t) * 8)\n> +\n> +/**\n> + * Do not use __builtin_popcountll. The GCC implementation\n> + * is notoriously slow on all platforms.\n> + *\n> + * See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36041\n> + */\n> +static inline uint32_t ewah_bit_popcount64(uint64_t x)\n> +{\n> +\tx = (x & 0x5555555555555555ULL) + ((x >>  1) & 0x5555555555555555ULL);\n> +\tx = (x & 0x3333333333333333ULL) + ((x >>  2) & 0x3333333333333333ULL);\n> +\tx = (x & 0x0F0F0F0F0F0F0F0FULL) + ((x >>  4) & 0x0F0F0F0F0F0F0F0FULL);\n> +\treturn (x * 0x0101010101010101ULL) >> 56;\n> +}\n> +\n> +#define ewah_bit_ctz64(x) __builtin_ctzll(x)\n> +\n> +struct ewah_bitmap {\n> +\teword_t *buffer;\n> +\tsize_t buffer_size;\n> +\tsize_t alloc_size;\n> +\tsize_t bit_size;\n> +\teword_t *rlw;\n> +};\n> +\n> +typedef void (*ewah_callback)(size_t pos, void *);\n> +\n> +struct ewah_bitmap *ewah_pool_new(void);\n> +void ewah_pool_free(struct ewah_bitmap *self);\n> +\n> +/**\n> + * Allocate a new EWAH Compressed bitmap\n> + */\n> +struct ewah_bitmap *ewah_new(void);\n> +\n> +/**\n> + * Clear all the bits in the bitmap. Does not free or resize\n> + * memory.\n> + */\n> +void ewah_clear(struct ewah_bitmap *self);\n> +\n> +/**\n> + * Free all the memory of the bitmap\n> + */\n> +void ewah_free(struct ewah_bitmap *self);\n> +\n> +int ewah_serialize_to(struct ewah_bitmap *self,\n> +\t\t      int (*write_fun)(void *out, const void *buf, size_t len),\n> +\t\t      void *out);\n> +int ewah_serialize(struct ewah_bitmap *self, int fd);\n> +int ewah_serialize_native(struct ewah_bitmap *self, int fd);\n> +\n> +int ewah_deserialize(struct ewah_bitmap *self, int fd);\n> +int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len);\n> +int ewah_read_mmap_native(struct ewah_bitmap *self, void *map, size_t len);\n> +\n> +uint32_t ewah_checksum(struct ewah_bitmap *self);\n> +\n> +/**\n> + * Logical not (bitwise negation) in-place on the bitmap\n> + *\n> + * This operation is linear time based on the size of the bitmap.\n> + */\n> +void ewah_not(struct ewah_bitmap *self);\n> +\n> +/**\n> + * Call the given callback with the position of every single bit\n> + * that has been set on the bitmap.\n> + *\n> + * This is an efficient operation that does not fully decompress\n> + * the bitmap.\n> + */\n> +void ewah_each_bit(struct ewah_bitmap *self, ewah_callback callback, void *payload);\n> +\n> +/**\n> + * Set a given bit on the bitmap.\n> + *\n> + * The bit at position `pos` will be set to true. Because of the\n> + * way that the bitmap is compressed, a set bit cannot be unset\n> + * later on.\n> + *\n> + * Furthermore, since the bitmap uses streaming compression, bits\n> + * can only set incrementally.\n> + *\n> + * E.g.\n> + *\t\tewah_set(bitmap, 1); // ok\n> + *\t\tewah_set(bitmap, 76); // ok\n> + *\t\tewah_set(bitmap, 77); // ok\n> + *\t\tewah_set(bitmap, 8712800127); // ok\n> + *\t\tewah_set(bitmap, 25); // failed, assert raised\n> + */\n> +void ewah_set(struct ewah_bitmap *self, size_t i);\n> +\n> +struct ewah_iterator {\n> +\tconst eword_t *buffer;\n> +\tsize_t buffer_size;\n> +\n> +\tsize_t pointer;\n> +\teword_t compressed, literals;\n> +\teword_t rl, lw;\n> +\tint b;\n> +};\n> +\n> +/**\n> + * Initialize a new iterator to run through the bitmap in uncompressed form.\n> + *\n> + * The iterator can be stack allocated. The underlying bitmap must not be freed\n> + * before the iteration is over.\n> + *\n> + * E.g.\n> + *\n> + *\t\tstruct ewah_bitmap *bitmap = ewah_new();\n> + *\t\tstruct ewah_iterator it;\n> + *\n> + *\t\tewah_iterator_init(&it, bitmap);\n> + */\n> +void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent);\n> +\n> +/**\n> + * Yield every single word in the bitmap in uncompressed form. This is:\n> + * yield single words (32-64 bits) where each bit represents an actual\n> + * bit from the bitmap.\n> + *\n> + * Return: true if a word was yield, false if there are no words left\n> + */\n> +int ewah_iterator_next(eword_t *next, struct ewah_iterator *it);\n> +\n> +void ewah_or(\n> +\tstruct ewah_bitmap *ewah_i,\n> +\tstruct ewah_bitmap *ewah_j,\n> +\tstruct ewah_bitmap *out);\n> +\n> +void ewah_and_not(\n> +\tstruct ewah_bitmap *ewah_i,\n> +\tstruct ewah_bitmap *ewah_j,\n> +\tstruct ewah_bitmap *out);\n> +\n> +void ewah_xor(\n> +\tstruct ewah_bitmap *ewah_i,\n> +\tstruct ewah_bitmap *ewah_j,\n> +\tstruct ewah_bitmap *out);\n> +\n> +void ewah_and(\n> +\tstruct ewah_bitmap *ewah_i,\n> +\tstruct ewah_bitmap *ewah_j,\n> +\tstruct ewah_bitmap *out);\n> +\n> +void ewah_dump(struct ewah_bitmap *self);\n> +\n> +/**\n> + * Direct word access\n> + */\n> +size_t ewah_add_empty_words(struct ewah_bitmap *self, int v, size_t number);\n> +void ewah_add_dirty_words(\n> +\tstruct ewah_bitmap *self, const eword_t *buffer, size_t number, int negate);\n> +size_t ewah_add(struct ewah_bitmap *self, eword_t word);\n> +\n> +\n> +/**\n> + * Uncompressed, old-school bitmap that can be efficiently compressed\n> + * into an `ewah_bitmap`.\n> + */\n> +struct bitmap {\n> +\teword_t *words;\n> +\tsize_t word_alloc;\n> +};\n> +\n> +struct bitmap *bitmap_new(void);\n> +void bitmap_set(struct bitmap *self, size_t pos);\n> +void bitmap_clear(struct bitmap *self, size_t pos);\n> +int bitmap_get(struct bitmap *self, size_t pos);\n> +void bitmap_reset(struct bitmap *self);\n> +void bitmap_free(struct bitmap *self);\n> +int bitmap_equals(struct bitmap *self, struct bitmap *other);\n> +int bitmap_is_subset(struct bitmap *self, struct bitmap *super);\n> +\n> +struct ewah_bitmap * bitmap_to_ewah(struct bitmap *bitmap);\n> +struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah);\n> +\n> +void bitmap_and_not(struct bitmap *self, struct bitmap *other);\n> +void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other);\n> +void bitmap_or(struct bitmap *self, const struct bitmap *other);\n> +\n> +void bitmap_each_bit(struct bitmap *self, ewah_callback callback, void *data);\n> +size_t bitmap_popcount(struct bitmap *self);\n> +\n> +#endif\n> diff --git a/ewah/ewok_rlw.h b/ewah/ewok_rlw.h\n> new file mode 100644\n> index 0000000..63efdf9\n> --- /dev/null\n> +++ b/ewah/ewok_rlw.h\n> @@ -0,0 +1,114 @@\n> +/**\n> + * Copyright 2013, GitHub, Inc\n> + * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n> + *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n> + *\n> + * This program is free software; you can redistribute it and/or\n> + * modify it under the terms of the GNU General Public License\n> + * as published by the Free Software Foundation; either version 2\n> + * of the License, or (at your option) any later version.\n> + *\n> + * This program is distributed in the hope that it will be useful,\n> + * but WITHOUT ANY WARRANTY; without even the implied warranty of\n> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> + * GNU General Public License for more details.\n> + *\n> + * You should have received a copy of the GNU General Public License\n> + * along with this program; if not, write to the Free Software\n> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n> + */\n> +#ifndef __EWOK_RLW_H__\n> +#define __EWOK_RLW_H__\n> +\n> +#define RLW_RUNNING_BITS (sizeof(eword_t) * 4)\n> +#define RLW_LITERAL_BITS (sizeof(eword_t) * 8 - 1 - RLW_RUNNING_BITS)\n> +\n> +#define RLW_LARGEST_RUNNING_COUNT (((eword_t)1 << RLW_RUNNING_BITS) - 1)\n> +#define RLW_LARGEST_LITERAL_COUNT (((eword_t)1 << RLW_LITERAL_BITS) - 1)\n> +\n> +#define RLW_LARGEST_RUNNING_COUNT_SHIFT (RLW_LARGEST_RUNNING_COUNT << 1)\n> +\n> +#define RLW_RUNNING_LEN_PLUS_BIT (((eword_t)1 << (RLW_RUNNING_BITS + 1)) - 1)\n> +\n> +static int rlw_get_run_bit(const eword_t *word)\n> +{\n> +\treturn *word & (eword_t)1;\n> +}\n> +\n> +static inline void rlw_set_run_bit(eword_t *word, int b)\n> +{\n> +\tif (b) {\n> +\t\t*word |= (eword_t)1;\n> +\t} else {\n> +\t\t*word &= (eword_t)(~1);\n> +\t}\n> +}\n> +\n> +static inline void rlw_xor_run_bit(eword_t *word)\n> +{\n> +\tif (*word & 1) {\n> +\t\t*word &= (eword_t)(~1);\n> +\t} else {\n> +\t\t*word |= (eword_t)1;\n> +\t}\n> +}\n> +\n> +static inline void rlw_set_running_len(eword_t *word, eword_t l)\n> +{\n> +\t*word |= RLW_LARGEST_RUNNING_COUNT_SHIFT;\n> +\t*word &= (l << 1) | (~RLW_LARGEST_RUNNING_COUNT_SHIFT);\n> +}\n> +\n> +static inline eword_t rlw_get_running_len(const eword_t *word)\n> +{\n> +\treturn (*word >> 1) & RLW_LARGEST_RUNNING_COUNT;\n> +}\n> +\n> +static inline eword_t rlw_get_literal_words(const eword_t *word)\n> +{\n> +\treturn *word >> (1 + RLW_RUNNING_BITS);\n> +}\n> +\n> +static inline void rlw_set_literal_words(eword_t *word, eword_t l)\n> +{\n> +\t*word |= ~RLW_RUNNING_LEN_PLUS_BIT;\n> +\t*word &= (l << (RLW_RUNNING_BITS + 1)) | RLW_RUNNING_LEN_PLUS_BIT;\n> +}\n> +\n> +static inline eword_t rlw_size(const eword_t *self)\n> +{\n> +\treturn rlw_get_running_len(self) + rlw_get_literal_words(self);\n> +}\n> +\n> +struct rlw_iterator {\n> +\tconst eword_t *buffer;\n> +\tsize_t size;\n> +\tsize_t pointer;\n> +\tsize_t literal_word_start;\n> +\n> +\tstruct {\n> +\t\tconst eword_t *word;\n> +\t\tint literal_words;\n> +\t\tint running_len;\n> +\t\tint literal_word_offset;\n> +\t\tint running_bit;\n> +\t} rlw;\n> +};\n> +\n> +void rlwit_init(struct rlw_iterator *it, struct ewah_bitmap *bitmap);\n> +void rlwit_discard_first_words(struct rlw_iterator *it, size_t x);\n> +size_t rlwit_discharge(\n> +\tstruct rlw_iterator *it, struct ewah_bitmap *out, size_t max, int negate);\n> +void rlwit_discharge_empty(struct rlw_iterator *it, struct ewah_bitmap *out);\n> +\n> +static inline size_t rlwit_word_size(struct rlw_iterator *it)\n> +{\n> +\treturn it->rlw.running_len + it->rlw.literal_words;\n> +}\n> +\n> +static inline size_t rlwit_literal_words(struct rlw_iterator *it)\n> +{\n> +\treturn it->pointer - it->rlw.literal_words;\n> +}\n> +\n> +#endif\n"},{"id":"229454","messageId":"CACsJy8DFisnZZiPOWjktuMC2J289AYGtgefZob=4YFAJAxBc6w@mail.gmail.com","threadId":"35192","inReplyTo":"20131024180347.GI24180@sigill.intra.peff.net","subject":"Re: [PATCH 09/19] documentation: add documentation for the bitmap format","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-10-25T01:16:18Z","receivedAt":"2013-10-25T01:16:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Oct 25, 2013 at 1:03 AM, Jeff King <peff@peff.net> wrote:\n> diff --git a/Documentation/technical/bitmap-format.txt b/Documentation/technical/bitmap-format.txt\n> new file mode 100644\n> index 0000000..c686dd1\n> --- /dev/null\n> +++ b/Documentation/technical/bitmap-format.txt\n> @@ -0,0 +1,131 @@\n> +GIT bitmap v1 format\n> +====================\n> +\n> +       - A header appears at the beginning:\n> +\n> +               4-byte signature: {'B', 'I', 'T', 'M'}\n> +\n> +               2-byte version number (network byte order)\n> +                       The current implementation only supports version 1\n> +                       of the bitmap index (the same one as JGit).\n\nI suppose this means if I want to extend pack bitmaps to be used on\nshallow clones, I need to step version to 2 before adding the shallow\npoints in .bitmap file as there's no chance of modifying v1 anymore,\ncorrect?\n\n> +\n> +               2-byte flags (network byte order)\n> +\n> +                       The following flags are supported:\n> +\n> +                       - BITMAP_OPT_FULL_DAG (0x1) REQUIRED\n> +                       This flag must always be present. It implies that the bitmap\n> +                       index has been generated for a packfile with full closure\n> +                       (i.e. where every single object in the packfile can find\n> +                        its parent links inside the same packfile). This is a\n> +                       requirement for the bitmap index format, also present in JGit,\n> +                       that greatly reduces the complexity of the implementation.\n> +\n> +               4-byte entry count (network byte order)\n> +\n> +                       The total count of entries (bitmapped commits) in this bitmap index.\n> +\n> +               20-byte checksum\n> +\n> +                       The SHA1 checksum of the pack this bitmap index belongs to.\n> +\n-- \nDuy\n"},{"id":"229455","messageId":"CACsJy8By5bdJLMnf2FDJKU5+wD=NFh8QmS80crmk_u+K78Qa_A@mail.gmail.com","threadId":"35192","inReplyTo":"20131024180642.GM24180@sigill.intra.peff.net","subject":"Re: [PATCH 13/19] pack-objects: implement bitmap writing","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-10-25T01:21:12Z","receivedAt":"2013-10-25T01:21:12Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Oct 25, 2013 at 1:06 AM, Jeff King <peff@peff.net> wrote:\n> From: Vicent Marti <tanoku@gmail.com>\n>\n> This commit extends more the functionality of `pack-objects` by allowing\n> it to write out a `.bitmap` index next to any written packs, together\n> with the `.idx` index that currently gets written.\n>\n> If bitmap writing is enabled for a given repository (either by calling\n> `pack-objects` with the `--write-bitmap-index` flag or by having\n> `pack.writebitmaps` set to `true` in the config) and pack-objects is\n> writing a packfile that would normally be indexed (i.e. not piping to\n> stdout), we will attempt to write the corresponding bitmap index for the\n> packfile.\n\nI haven't read the actual patch yet, but the diffstat says user\ndocumentation is missing..\n-- \nDuy\n"},{"id":"229458","messageId":"xmqq38nqkpzx.fsf@gitster.dls.corp.google.com","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"Re: [PATCH 0/19] pack bitmaps","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-10-25T03:07:14Z","receivedAt":"2013-10-25T03:07:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This is only to tentatively work-around the compilation breakages;\nthe fixes need to be split into the respective patches that\nintroduce breakages when the series is rerolled (the one I sent for\npack-bitmap.c separately is also included in this message).\n\nThanks.\n\n ewah/ewah_bitmap.c  | 22 ++++++++++++++++------\n ewah/ewah_io.c      | 44 ++++++++++++++++++++++++++------------------\n pack-bitmap-write.c |  2 --\n pack-bitmap.c       | 13 ++++++-------\n 4 files changed, 48 insertions(+), 33 deletions(-)\n\ndiff --git a/ewah/ewah_bitmap.c b/ewah/ewah_bitmap.c\nindex b74a1eb..7986720 100644\n--- a/ewah/ewah_bitmap.c\n+++ b/ewah/ewah_bitmap.c\n@@ -65,6 +65,8 @@ static void buffer_push_rlw(struct ewah_bitmap *self, eword_t value)\n \n static size_t add_empty_words(struct ewah_bitmap *self, int v, size_t number)\n {\n+\teword_t runlen;\n+\teword_t can_add;\n \tsize_t added = 0;\n \n \tif (rlw_get_run_bit(self->rlw) != v && rlw_size(self->rlw) == 0) {\n@@ -76,8 +78,8 @@ static size_t add_empty_words(struct ewah_bitmap *self, int v, size_t number)\n \t\tadded++;\n \t}\n \n-\teword_t runlen = rlw_get_running_len(self->rlw);\n-\teword_t can_add = min_size(number, RLW_LARGEST_RUNNING_COUNT - runlen);\n+\trunlen = rlw_get_running_len(self->rlw);\n+\tcan_add = min_size(number, RLW_LARGEST_RUNNING_COUNT - runlen);\n \n \trlw_set_running_len(self->rlw, runlen + can_add);\n \tnumber -= can_add;\n@@ -426,6 +428,8 @@ void ewah_xor(\n \trlwit_init(&rlw_j, ewah_j);\n \n \twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n+\t\tsize_t literals;\n+\n \t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n \t\t\tstruct rlw_iterator *prey, *predator;\n \t\t\tsize_t index;\n@@ -446,7 +450,7 @@ void ewah_xor(\n \t\t\trlwit_discard_first_words(predator, predator->rlw.running_len);\n \t\t}\n \n-\t\tsize_t literals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n+\t\tliterals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n \n \t\tif (literals) {\n \t\t\tsize_t k;\n@@ -484,6 +488,8 @@ void ewah_and(\n \trlwit_init(&rlw_j, ewah_j);\n \n \twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n+\t\tsize_t literals;\n+\n \t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n \t\t\tstruct rlw_iterator *prey, *predator;\n \n@@ -507,7 +513,7 @@ void ewah_and(\n \t\t\t}\n \t\t}\n \n-\t\tsize_t literals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n+\t\tliterals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n \n \t\tif (literals) {\n \t\t\tsize_t k;\n@@ -545,6 +551,8 @@ void ewah_and_not(\n \trlwit_init(&rlw_j, ewah_j);\n \n \twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n+\t\tsize_t literals;\n+\n \t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n \t\t\tstruct rlw_iterator *prey, *predator;\n \n@@ -572,7 +580,7 @@ void ewah_and_not(\n \t\t\t}\n \t\t}\n \n-\t\tsize_t literals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n+\t\tliterals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n \n \t\tif (literals) {\n \t\t\tsize_t k;\n@@ -610,6 +618,8 @@ void ewah_or(\n \trlwit_init(&rlw_j, ewah_j);\n \n \twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n+\t\tsize_t literals;\n+\n \t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n \t\t\tstruct rlw_iterator *prey, *predator;\n \n@@ -634,7 +644,7 @@ void ewah_or(\n \t\t\t}\n \t\t}\n \n-\t\tsize_t literals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n+\t\tliterals = min_size(rlw_i.rlw.literal_words, rlw_j.rlw.literal_words);\n \n \t\tif (literals) {\n \t\t\tsize_t k;\ndiff --git a/ewah/ewah_io.c b/ewah/ewah_io.c\nindex db6c062..05c51d9 100644\n--- a/ewah/ewah_io.c\n+++ b/ewah/ewah_io.c\n@@ -58,19 +58,26 @@ int ewah_serialize_to(struct ewah_bitmap *self,\n \teword_t dump[2048];\n \tconst size_t words_per_dump = sizeof(dump) / sizeof(eword_t);\n \n-\t/* 32 bit -- bit size fr the map */\n-\tuint32_t bitsize =  htonl((uint32_t)self->bit_size);\n+\t/* 32 bit -- bit size for the map */\n+\tuint32_t bitsize;\n+\t/* 32 bit -- number of compressed 64-bit words */\n+\tuint32_t word_count;\n+\t/* 64 bit x N -- compressed words */\n+\tconst eword_t *buffer = self->buffer;\n+\tsize_t words_left;\n+\n+\t/* 32 bit -- position for the RLW */\n+\tuint32_t rlw_pos;\n+\n+\tbitsize =  htonl((uint32_t)self->bit_size);\n \tif (write_fun(data, &bitsize, 4) != 4)\n \t\treturn -1;\n \n-\t/** 32 bit -- number of compressed 64-bit words */\n-\tuint32_t word_count =  htonl((uint32_t)self->buffer_size);\n+\tword_count =  htonl((uint32_t)self->buffer_size);\n \tif (write_fun(data, &word_count, 4) != 4)\n \t\treturn -1;\n \n-\t/** 64 bit x N -- compressed words */\n-\tconst eword_t *buffer = self->buffer;\n-\tsize_t words_left = self->buffer_size;\n+\twords_left = self->buffer_size;\n \n \twhile (words_left >= words_per_dump) {\n \t\tfor (i = 0; i < words_per_dump; ++i, ++buffer)\n@@ -90,8 +97,7 @@ int ewah_serialize_to(struct ewah_bitmap *self,\n \t\t\treturn -1;\n \t}\n \n-\t/** 32 bit -- position for the RLW */\n-\tuint32_t rlw_pos = (uint8_t*)self->rlw - (uint8_t *)self->buffer;\n+\trlw_pos = (uint8_t*)self->rlw - (uint8_t *)self->buffer;\n \trlw_pos = htonl(rlw_pos / sizeof(eword_t));\n \n \tif (write_fun(data, &rlw_pos, 4) != 4)\n@@ -138,18 +144,23 @@ int ewah_deserialize(struct ewah_bitmap *self, int fd)\n \tsize_t i;\n \teword_t dump[2048];\n \tconst size_t words_per_dump = sizeof(dump) / sizeof(eword_t);\n+\t/* 32 bit -- bit size for the map */\n+\tuint32_t bitsize;\n+\t/* 32 bit -- number of compressed 64-bit words */\n+\tuint32_t word_count;\n+\t/* 64 bit x N -- compressed words */\n+\teword_t *buffer;\n+\tsize_t words_left;\n+\t/** 32 bit -- position for the RLW */\n+\tuint32_t rlw_pos;\n \n \tewah_clear(self);\n \n-\t/* 32 bit -- bit size fr the map */\n-\tuint32_t bitsize;\n \tif (read(fd, &bitsize, 4) != 4)\n \t\treturn -1;\n \n \tself->bit_size = (size_t)ntohl(bitsize);\n \n-\t/** 32 bit -- number of compressed 64-bit words */\n-\tuint32_t word_count;\n \tif (read(fd, &word_count, 4) != 4)\n \t\treturn -1;\n \n@@ -159,9 +170,8 @@ int ewah_deserialize(struct ewah_bitmap *self, int fd)\n \tif (!self->buffer)\n \t\treturn -1;\n \n-\t/** 64 bit x N -- compressed words */\n-\teword_t *buffer = self->buffer;\n-\tsize_t words_left = self->buffer_size;\n+\tbuffer = self->buffer;\n+\twords_left = self->buffer_size;\n \n \twhile (words_left >= words_per_dump) {\n \t\tif (read(fd, dump, sizeof(dump)) != sizeof(dump))\n@@ -181,8 +191,6 @@ int ewah_deserialize(struct ewah_bitmap *self, int fd)\n \t\t\t*buffer = ntohll(dump[i]);\n \t}\n \n-\t/** 32 bit -- position for the RLW */\n-\tuint32_t rlw_pos;\n \tif (read(fd, &rlw_pos, 4) != 4)\n \t\treturn -1;\n \ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex c44874a..48087f1 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -1,5 +1,3 @@\n-#include <stdlib.h>\n-\n #include \"cache.h\"\n #include \"commit.h\"\n #include \"tag.h\"\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex a7c553d..3f1d369 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -1,5 +1,3 @@\n-#include <stdlib.h>\n-\n #include \"cache.h\"\n #include \"commit.h\"\n #include \"tag.h\"\n@@ -123,8 +121,8 @@ static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)\n \tstruct ewah_bitmap *b = ewah_pool_new();\n \n \tint bitmap_size = ewah_read_mmap(b,\n-\t\tindex->map + index->map_pos,\n-\t\tindex->map_size - index->map_pos);\n+\t\t\t\t\t (unsigned char *)index->map + index->map_pos,\n+\t\t\t\t\t index->map_size - index->map_pos);\n \n \tif (bitmap_size < 0) {\n \t\terror(\"Failed to load bitmap index (corrupted?)\");\n@@ -159,8 +157,9 @@ static int load_bitmap_header(struct bitmap_index *index)\n \t\t\t\t\"(Git requires BITMAP_OPT_FULL_DAG)\");\n \n \t\tif (flags & BITMAP_OPT_HASH_CACHE) {\n-\t\t\tindex->hashes = index->map + index->map_size - 20 -\n-\t\t\t\t(sizeof(uint32_t) * index->pack->num_objects);\n+\t\t\tindex->hashes = (uint32_t *)((unsigned char *)index->map +\n+\t\t\t\t\t\t     index->map_size - 20 -\n+\t\t\t\t\t\t     (sizeof(uint32_t) * index->pack->num_objects));\n \t\t}\n \t}\n \n@@ -216,7 +215,7 @@ static int load_bitmap_entries_v1(struct bitmap_index *index)\n \t\tuint32_t commit_idx_pos;\n \t\tconst unsigned char *sha1;\n \n-\t\tentry = index->map + index->map_pos;\n+\t\tentry = (struct bitmap_disk_entry *)((unsigned char *)index->map + index->map_pos);\n \t\tindex->map_pos += sizeof(struct bitmap_disk_entry);\n \n \t\tcommit_idx_pos = ntohl(entry->object_pos);\n"},{"id":"229460","messageId":"20131025031528.GA26283@sigill.intra.peff.net","threadId":"35192","inReplyTo":"xmqqiowmkztz.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 08/19] ewah: compressed bitmap implementation","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T03:15:28Z","receivedAt":"2013-10-25T03:15:28Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Oct 24, 2013 at 04:34:48PM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > From: Vicent Marti <tanoku@gmail.com>\n> >\n> > EWAH is a word-aligned compressed variant of a bitset (i.e. a data\n> > structure that acts as a 0-indexed boolean array for many entries).\n> \n> I think I've already pointed out some when the original series was\n> posted, but this does not seem to compile with -Wdecl-after-stmt\n> (possibly other violations may exist; I haven't looked very closely\n> yet).\n\nSorry about that. I typically compile with \"-Wall -Werror\" which does\nnot catch this, and I didn't carefully go over the ewah code for style,\nas I considered it mostly a black-box import (though certainly it is\nworth fixing this case, as it's not just a style issue).\n\nIt looks like there is also some void pointer arithmetic...I _thought_\nthat was handled by -Wall, but I suppose not. Maybe I need to beef up my\nwarning settings.\n\nNote that there is also some use of cast-mmap-file-to-struct, only one\ninstance of which uses __attribute__(packed). However, I notice that the\nregular pack code is also guilty of this (e.g., see check_packed_git_idx),\nso I don't know how careful we want to be (and whether we should be\nusing the packed attribute when needed, or if it is sufficiently\nnon-portable that we want to do it by hand in such cases).\n\n-Peff\n"},{"id":"229461","messageId":"20131025032144.GB26283@sigill.intra.peff.net","threadId":"35192","inReplyTo":"CACsJy8DFisnZZiPOWjktuMC2J289AYGtgefZob=4YFAJAxBc6w@mail.gmail.com","subject":"Re: [PATCH 09/19] documentation: add documentation for the bitmap format","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T03:21:44Z","receivedAt":"2013-10-25T03:21:44Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"[+cc spearce; sorry, I really should have cc'd the whole series to you\n     in the first place]\n\nOn Fri, Oct 25, 2013 at 08:16:18AM +0700, Nguyen Thai Ngoc Duy wrote:\n\n> > +       - A header appears at the beginning:\n> > +\n> > +               4-byte signature: {'B', 'I', 'T', 'M'}\n> > +\n> > +               2-byte version number (network byte order)\n> > +                       The current implementation only supports version 1\n> > +                       of the bitmap index (the same one as JGit).\n> \n> I suppose this means if I want to extend pack bitmaps to be used on\n> shallow clones, I need to step version to 2 before adding the shallow\n> points in .bitmap file as there's no chance of modifying v1 anymore,\n> correct?\n\nIt depends on how you want to change it. And what Shawn says. :)\n\nThis is an attempt to document the JGit format. My feeling is that there\nshould be a version bump if an existing implementation would barf on it,\nand I assume Shawn would agree.  But if you want to simply add extra\ndata that would be ignored by an existing implementation, it would be OK\nto add the data and mark it with a flag:\n\n> > +               2-byte flags (network byte order)\n> > +\n> > +                       The following flags are supported:\n> [...]\n\nThat's how we added the name-hash cache in the final patch.\n\nThat being said, JGit is _not_ happy with that, and complains when any\nflags besides OPT_FULL are used.  Whether that is because I am\nmisinterpreting the intent of the flags field, or because JGit is being\noverly strict is up for debate.\n\n-Peff\n"},{"id":"229462","messageId":"20131025032210.GC26283@sigill.intra.peff.net","threadId":"35192","inReplyTo":"CACsJy8By5bdJLMnf2FDJKU5+wD=NFh8QmS80crmk_u+K78Qa_A@mail.gmail.com","subject":"Re: [PATCH 13/19] pack-objects: implement bitmap writing","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T03:22:10Z","receivedAt":"2013-10-25T03:22:10Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 25, 2013 at 08:21:12AM +0700, Nguyen Thai Ngoc Duy wrote:\n\n> > If bitmap writing is enabled for a given repository (either by calling\n> > `pack-objects` with the `--write-bitmap-index` flag or by having\n> > `pack.writebitmaps` set to `true` in the config) and pack-objects is\n> > writing a packfile that would normally be indexed (i.e. not piping to\n> > stdout), we will attempt to write the corresponding bitmap index for the\n> > packfile.\n> \n> I haven't read the actual patch yet, but the diffstat says user\n> documentation is missing..\n\nI'll work on that for the re-roll.\n\n-Peff\n"},{"id":"229463","messageId":"CACsJy8D=3L73xQSZ-=DD+u57toKqZ=smKW0TDskfAcB1T7fkag@mail.gmail.com","threadId":"35192","inReplyTo":"20131025032144.GB26283@sigill.intra.peff.net","subject":"Re: [PATCH 09/19] documentation: add documentation for the bitmap format","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-10-25T03:28:11Z","receivedAt":"2013-10-25T03:28:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Oct 25, 2013 at 10:21 AM, Jeff King <peff@peff.net> wrote:\n> But if you want to simply add extra\n> data that would be ignored by an existing implementation, it would be OK\n> to add the data and mark it with a flag:\n>\n>> > +               2-byte flags (network byte order)\n>> > +\n>> > +                       The following flags are supported:\n>> [...]\n>\n> That's how we added the name-hash cache in the final patch.\n>\n> That being said, JGit is _not_ happy with that, and complains when any\n> flags besides OPT_FULL are used.  Whether that is because I am\n> misinterpreting the intent of the flags field, or because JGit is being\n> overly strict is up for debate.\n\nMight be a good idea to support two classes of flags like how\nextensions are handled in the index: ignore unrecognized uppercase\nextension names, barf on unrecongized lowercase names. We could\npartition the flag bit space more or less the same way.\n-- \nDuy\n"},{"id":"229475","messageId":"20131025055521.GD11810@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131024175915.GA23398@sigill.intra.peff.net","subject":"[PATCHv2 0/19] pack bitmaps","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T05:55:21Z","receivedAt":"2013-10-25T05:55:21Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Oct 24, 2013 at 01:59:15PM -0400, Jeff King wrote:\n\n> This series implements JGit-style pack bitmaps to speed up fetching and\n> cloning.\n\nHere is a re-roll fixing the comments so far. I know that nobody is\nlikely to have had a chance to read through it carefully yet, but given\nthe compiler warnings from the initial version, it makes sense to me to\nget a clean baseline in front of people before they start reading the\nold one. Given the size, I'll let this one collect more in-depth\ncomments for a bit before sending out another re-roll.\n\nBesides the warning fixups, the ewah/* files have some style fixups to\nbetter match git, and I added documentation for some config and\ncommand-line options. I didn't add documentation for items I do not\nexpect people to actually use (e.g., \"rev-list --test-bitmaps\"), as they\nclutter the documentation.\n\n-Peff\n"},{"id":"229476","messageId":"20131025055733.GA21179@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 01/19] sha1write: make buffer const-correct","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T05:57:33Z","receivedAt":"2013-10-25T05:57:33Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We are passed a \"void *\" and write it out without ever\ntouching it; let's indicate that by using \"const\".\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n csum-file.c | 6 +++---\n csum-file.h | 2 +-\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/csum-file.c b/csum-file.c\nindex 53f5375..465971c 100644\n--- a/csum-file.c\n+++ b/csum-file.c\n@@ -11,7 +11,7 @@\n #include \"progress.h\"\n #include \"csum-file.h\"\n \n-static void flush(struct sha1file *f, void *buf, unsigned int count)\n+static void flush(struct sha1file *f, const void *buf, unsigned int count)\n {\n \tif (0 <= f->check_fd && count)  {\n \t\tunsigned char check_buffer[8192];\n@@ -86,13 +86,13 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)\n \treturn fd;\n }\n \n-int sha1write(struct sha1file *f, void *buf, unsigned int count)\n+int sha1write(struct sha1file *f, const void *buf, unsigned int count)\n {\n \twhile (count) {\n \t\tunsigned offset = f->offset;\n \t\tunsigned left = sizeof(f->buffer) - offset;\n \t\tunsigned nr = count > left ? left : count;\n-\t\tvoid *data;\n+\t\tconst void *data;\n \n \t\tif (f->do_crc)\n \t\t\tf->crc32 = crc32(f->crc32, buf, nr);\ndiff --git a/csum-file.h b/csum-file.h\nindex 3b540bd..9dedb03 100644\n--- a/csum-file.h\n+++ b/csum-file.h\n@@ -34,7 +34,7 @@ extern struct sha1file *sha1fd(int fd, const char *name);\n extern struct sha1file *sha1fd_check(const char *name);\n extern struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp);\n extern int sha1close(struct sha1file *, unsigned char *, unsigned int);\n-extern int sha1write(struct sha1file *, void *, unsigned int);\n+extern int sha1write(struct sha1file *, const void *, unsigned int);\n extern void sha1flush(struct sha1file *f);\n extern void crc32_begin(struct sha1file *);\n extern uint32_t crc32_end(struct sha1file *);\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229477","messageId":"20131025060258.GA23067@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH 02/19] revindex: Export new APIs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:02:58Z","receivedAt":"2013-10-25T06:02:58Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nAllow users to efficiently lookup consecutive entries that are expected\nto be found on the same revindex by exporting `find_revindex_position`:\nthis function takes a pointer to revindex itself, instead of looking up\nthe proper revindex for a given packfile on each call.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n pack-revindex.c | 38 +++++++++++++++++++++++++-------------\n pack-revindex.h |  8 ++++++++\n 2 files changed, 33 insertions(+), 13 deletions(-)\n\ndiff --git a/pack-revindex.c b/pack-revindex.c\nindex b4d2b35..0bb13b1 100644\n--- a/pack-revindex.c\n+++ b/pack-revindex.c\n@@ -16,11 +16,6 @@\n  * get the object sha1 from the main index.\n  */\n \n-struct pack_revindex {\n-\tstruct packed_git *p;\n-\tstruct revindex_entry *revindex;\n-};\n-\n static struct pack_revindex *pack_revindex;\n static int pack_revindex_hashsz;\n \n@@ -201,15 +196,14 @@ static void create_pack_revindex(struct pack_revindex *rix)\n \tsort_revindex(rix->revindex, num_ent, p->pack_size);\n }\n \n-struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)\n+struct pack_revindex *revindex_for_pack(struct packed_git *p)\n {\n \tint num;\n-\tunsigned lo, hi;\n \tstruct pack_revindex *rix;\n-\tstruct revindex_entry *revindex;\n \n \tif (!pack_revindex_hashsz)\n \t\tinit_pack_revindex();\n+\n \tnum = pack_revindex_ix(p);\n \tif (num < 0)\n \t\tdie(\"internal error: pack revindex fubar\");\n@@ -217,21 +211,39 @@ struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)\n \trix = &pack_revindex[num];\n \tif (!rix->revindex)\n \t\tcreate_pack_revindex(rix);\n-\trevindex = rix->revindex;\n \n-\tlo = 0;\n-\thi = p->num_objects + 1;\n+\treturn rix;\n+}\n+\n+int find_revindex_position(struct pack_revindex *pridx, off_t ofs)\n+{\n+\tint lo = 0;\n+\tint hi = pridx->p->num_objects + 1;\n+\tstruct revindex_entry *revindex = pridx->revindex;\n+\n \tdo {\n \t\tunsigned mi = lo + (hi - lo) / 2;\n \t\tif (revindex[mi].offset == ofs) {\n-\t\t\treturn revindex + mi;\n+\t\t\treturn mi;\n \t\t} else if (ofs < revindex[mi].offset)\n \t\t\thi = mi;\n \t\telse\n \t\t\tlo = mi + 1;\n \t} while (lo < hi);\n+\n \terror(\"bad offset for revindex\");\n-\treturn NULL;\n+\treturn -1;\n+}\n+\n+struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)\n+{\n+\tstruct pack_revindex *pridx = revindex_for_pack(p);\n+\tint pos = find_revindex_position(pridx, ofs);\n+\n+\tif (pos < 0)\n+\t\treturn NULL;\n+\n+\treturn pridx->revindex + pos;\n }\n \n void discard_revindex(void)\ndiff --git a/pack-revindex.h b/pack-revindex.h\nindex 8d5027a..866ca9c 100644\n--- a/pack-revindex.h\n+++ b/pack-revindex.h\n@@ -6,6 +6,14 @@ struct revindex_entry {\n \tunsigned int nr;\n };\n \n+struct pack_revindex {\n+\tstruct packed_git *p;\n+\tstruct revindex_entry *revindex;\n+};\n+\n+struct pack_revindex *revindex_for_pack(struct packed_git *p);\n+int find_revindex_position(struct pack_revindex *pridx, off_t ofs);\n+\n struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs);\n void discard_revindex(void);\n \n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229479","messageId":"20131025060310.GA23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 03/19] pack-objects: Refactor the packing list","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:03:11Z","receivedAt":"2013-10-25T06:03:11Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThe hash table that stores the packing list for a given `pack-objects`\nrun was tightly coupled to the pack-objects code.\n\nIn this commit, we refactor the hash table and the underlying storage\narray into a `packing_data` struct. The functionality for accessing and\nadding entries to the packing list is hence accessible from other parts\nof Git besides the `pack-objects` builtin.\n\nThis refactoring is a requirement for further patches in this series\nthat will require accessing the commit packing list from outside of\n`pack-objects`.\n\nThe hash table implementation has been minimally altered: we now\nuse table sizes which are always a power of two, to ensure a uniform\nindex distribution in the array.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Makefile               |   2 +\n builtin/pack-objects.c | 175 +++++++++++--------------------------------------\n pack-objects.c         | 111 +++++++++++++++++++++++++++++++\n pack-objects.h         |  47 +++++++++++++\n 4 files changed, 200 insertions(+), 135 deletions(-)\n create mode 100644 pack-objects.c\n create mode 100644 pack-objects.h\n\ndiff --git a/Makefile b/Makefile\nindex af847f8..48ff0bd 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -694,6 +694,7 @@ LIB_H += notes-merge.h\n LIB_H += notes-utils.h\n LIB_H += notes.h\n LIB_H += object.h\n+LIB_H += pack-objects.h\n LIB_H += pack-revindex.h\n LIB_H += pack.h\n LIB_H += parse-options.h\n@@ -831,6 +832,7 @@ LIB_OBJS += notes-merge.o\n LIB_OBJS += notes-utils.o\n LIB_OBJS += object.o\n LIB_OBJS += pack-check.o\n+LIB_OBJS += pack-objects.o\n LIB_OBJS += pack-revindex.o\n LIB_OBJS += pack-write.o\n LIB_OBJS += pager.o\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 36273dd..f3f0cf9 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -14,6 +14,7 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"list-objects.h\"\n+#include \"pack-objects.h\"\n #include \"progress.h\"\n #include \"refs.h\"\n #include \"streaming.h\"\n@@ -25,42 +26,15 @@ static const char *pack_usage[] = {\n \tNULL\n };\n \n-struct object_entry {\n-\tstruct pack_idx_entry idx;\n-\tunsigned long size;\t/* uncompressed size */\n-\tstruct packed_git *in_pack; \t/* already in pack */\n-\toff_t in_pack_offset;\n-\tstruct object_entry *delta;\t/* delta base object */\n-\tstruct object_entry *delta_child; /* deltified objects who bases me */\n-\tstruct object_entry *delta_sibling; /* other deltified objects who\n-\t\t\t\t\t     * uses the same base as me\n-\t\t\t\t\t     */\n-\tvoid *delta_data;\t/* cached delta (uncompressed) */\n-\tunsigned long delta_size;\t/* delta data size (uncompressed) */\n-\tunsigned long z_delta_size;\t/* delta data size (compressed) */\n-\tenum object_type type;\n-\tenum object_type in_pack_type;\t/* could be delta */\n-\tuint32_t hash;\t\t\t/* name hint hash */\n-\tunsigned char in_pack_header_size;\n-\tunsigned preferred_base:1; /*\n-\t\t\t\t    * we do not pack this, but is available\n-\t\t\t\t    * to be used as the base object to delta\n-\t\t\t\t    * objects against.\n-\t\t\t\t    */\n-\tunsigned no_try_delta:1;\n-\tunsigned tagged:1; /* near the very tip of refs */\n-\tunsigned filled:1; /* assigned write-order */\n-};\n-\n /*\n- * Objects we are going to pack are collected in objects array (dynamically\n- * expanded).  nr_objects & nr_alloc controls this array.  They are stored\n- * in the order we see -- typically rev-list --objects order that gives us\n- * nice \"minimum seek\" order.\n+ * Objects we are going to pack are collected in the `to_pack` structure.\n+ * It contains an array (dynamically expanded) of the object data, and a map\n+ * that can resolve SHA1s to their position in the array.\n  */\n-static struct object_entry *objects;\n+static struct packing_data to_pack;\n+\n static struct pack_idx_entry **written_list;\n-static uint32_t nr_objects, nr_alloc, nr_result, nr_written;\n+static uint32_t nr_result, nr_written;\n \n static int non_empty;\n static int reuse_delta = 1, reuse_object = 1;\n@@ -90,21 +64,11 @@ static unsigned long cache_max_small_delta_size = 1000;\n static unsigned long window_memory_limit = 0;\n \n /*\n- * The object names in objects array are hashed with this hashtable,\n- * to help looking up the entry by object name.\n- * This hashtable is built after all the objects are seen.\n- */\n-static int *object_ix;\n-static int object_ix_hashsz;\n-static struct object_entry *locate_object_entry(const unsigned char *sha1);\n-\n-/*\n  * stats\n  */\n static uint32_t written, written_delta;\n static uint32_t reused, reused_delta;\n \n-\n static void *get_delta(struct object_entry *entry)\n {\n \tunsigned long size, base_size, delta_size;\n@@ -553,12 +517,12 @@ static int mark_tagged(const char *path, const unsigned char *sha1, int flag,\n \t\t       void *cb_data)\n {\n \tunsigned char peeled[20];\n-\tstruct object_entry *entry = locate_object_entry(sha1);\n+\tstruct object_entry *entry = packlist_find(&to_pack, sha1, NULL);\n \n \tif (entry)\n \t\tentry->tagged = 1;\n \tif (!peel_ref(path, peeled)) {\n-\t\tentry = locate_object_entry(peeled);\n+\t\tentry = packlist_find(&to_pack, peeled, NULL);\n \t\tif (entry)\n \t\t\tentry->tagged = 1;\n \t}\n@@ -633,9 +597,10 @@ static struct object_entry **compute_write_order(void)\n {\n \tunsigned int i, wo_end, last_untagged;\n \n-\tstruct object_entry **wo = xmalloc(nr_objects * sizeof(*wo));\n+\tstruct object_entry **wo = xmalloc(to_pack.nr_objects * sizeof(*wo));\n+\tstruct object_entry *objects = to_pack.objects;\n \n-\tfor (i = 0; i < nr_objects; i++) {\n+\tfor (i = 0; i < to_pack.nr_objects; i++) {\n \t\tobjects[i].tagged = 0;\n \t\tobjects[i].filled = 0;\n \t\tobjects[i].delta_child = NULL;\n@@ -647,7 +612,7 @@ static struct object_entry **compute_write_order(void)\n \t * Make sure delta_sibling is sorted in the original\n \t * recency order.\n \t */\n-\tfor (i = nr_objects; i > 0;) {\n+\tfor (i = to_pack.nr_objects; i > 0;) {\n \t\tstruct object_entry *e = &objects[--i];\n \t\tif (!e->delta)\n \t\t\tcontinue;\n@@ -665,7 +630,7 @@ static struct object_entry **compute_write_order(void)\n \t * Give the objects in the original recency order until\n \t * we see a tagged tip.\n \t */\n-\tfor (i = wo_end = 0; i < nr_objects; i++) {\n+\tfor (i = wo_end = 0; i < to_pack.nr_objects; i++) {\n \t\tif (objects[i].tagged)\n \t\t\tbreak;\n \t\tadd_to_write_order(wo, &wo_end, &objects[i]);\n@@ -675,7 +640,7 @@ static struct object_entry **compute_write_order(void)\n \t/*\n \t * Then fill all the tagged tips.\n \t */\n-\tfor (; i < nr_objects; i++) {\n+\tfor (; i < to_pack.nr_objects; i++) {\n \t\tif (objects[i].tagged)\n \t\t\tadd_to_write_order(wo, &wo_end, &objects[i]);\n \t}\n@@ -683,7 +648,7 @@ static struct object_entry **compute_write_order(void)\n \t/*\n \t * And then all remaining commits and tags.\n \t */\n-\tfor (i = last_untagged; i < nr_objects; i++) {\n+\tfor (i = last_untagged; i < to_pack.nr_objects; i++) {\n \t\tif (objects[i].type != OBJ_COMMIT &&\n \t\t    objects[i].type != OBJ_TAG)\n \t\t\tcontinue;\n@@ -693,7 +658,7 @@ static struct object_entry **compute_write_order(void)\n \t/*\n \t * And then all the trees.\n \t */\n-\tfor (i = last_untagged; i < nr_objects; i++) {\n+\tfor (i = last_untagged; i < to_pack.nr_objects; i++) {\n \t\tif (objects[i].type != OBJ_TREE)\n \t\t\tcontinue;\n \t\tadd_to_write_order(wo, &wo_end, &objects[i]);\n@@ -702,13 +667,13 @@ static struct object_entry **compute_write_order(void)\n \t/*\n \t * Finally all the rest in really tight order\n \t */\n-\tfor (i = last_untagged; i < nr_objects; i++) {\n+\tfor (i = last_untagged; i < to_pack.nr_objects; i++) {\n \t\tif (!objects[i].filled)\n \t\t\tadd_family_to_write_order(wo, &wo_end, &objects[i]);\n \t}\n \n-\tif (wo_end != nr_objects)\n-\t\tdie(\"ordered %u objects, expected %\"PRIu32, wo_end, nr_objects);\n+\tif (wo_end != to_pack.nr_objects)\n+\t\tdie(\"ordered %u objects, expected %\"PRIu32, wo_end, to_pack.nr_objects);\n \n \treturn wo;\n }\n@@ -724,7 +689,7 @@ static void write_pack_file(void)\n \n \tif (progress > pack_to_stdout)\n \t\tprogress_state = start_progress(\"Writing objects\", nr_result);\n-\twritten_list = xmalloc(nr_objects * sizeof(*written_list));\n+\twritten_list = xmalloc(to_pack.nr_objects * sizeof(*written_list));\n \twrite_order = compute_write_order();\n \n \tdo {\n@@ -740,7 +705,7 @@ static void write_pack_file(void)\n \t\tif (!offset)\n \t\t\tdie_errno(\"unable to write pack header\");\n \t\tnr_written = 0;\n-\t\tfor (; i < nr_objects; i++) {\n+\t\tfor (; i < to_pack.nr_objects; i++) {\n \t\t\tstruct object_entry *e = write_order[i];\n \t\t\tif (write_one(f, e, &offset) == WRITE_ONE_BREAK)\n \t\t\t\tbreak;\n@@ -803,7 +768,7 @@ static void write_pack_file(void)\n \t\t\twritten_list[j]->offset = (off_t)-1;\n \t\t}\n \t\tnr_remaining -= nr_written;\n-\t} while (nr_remaining && i < nr_objects);\n+\t} while (nr_remaining && i < to_pack.nr_objects);\n \n \tfree(written_list);\n \tfree(write_order);\n@@ -813,53 +778,6 @@ static void write_pack_file(void)\n \t\t\twritten, nr_result);\n }\n \n-static int locate_object_entry_hash(const unsigned char *sha1)\n-{\n-\tint i;\n-\tunsigned int ui;\n-\tmemcpy(&ui, sha1, sizeof(unsigned int));\n-\ti = ui % object_ix_hashsz;\n-\twhile (0 < object_ix[i]) {\n-\t\tif (!hashcmp(sha1, objects[object_ix[i] - 1].idx.sha1))\n-\t\t\treturn i;\n-\t\tif (++i == object_ix_hashsz)\n-\t\t\ti = 0;\n-\t}\n-\treturn -1 - i;\n-}\n-\n-static struct object_entry *locate_object_entry(const unsigned char *sha1)\n-{\n-\tint i;\n-\n-\tif (!object_ix_hashsz)\n-\t\treturn NULL;\n-\n-\ti = locate_object_entry_hash(sha1);\n-\tif (0 <= i)\n-\t\treturn &objects[object_ix[i]-1];\n-\treturn NULL;\n-}\n-\n-static void rehash_objects(void)\n-{\n-\tuint32_t i;\n-\tstruct object_entry *oe;\n-\n-\tobject_ix_hashsz = nr_objects * 3;\n-\tif (object_ix_hashsz < 1024)\n-\t\tobject_ix_hashsz = 1024;\n-\tobject_ix = xrealloc(object_ix, sizeof(int) * object_ix_hashsz);\n-\tmemset(object_ix, 0, sizeof(int) * object_ix_hashsz);\n-\tfor (i = 0, oe = objects; i < nr_objects; i++, oe++) {\n-\t\tint ix = locate_object_entry_hash(oe->idx.sha1);\n-\t\tif (0 <= ix)\n-\t\t\tcontinue;\n-\t\tix = -1 - ix;\n-\t\tobject_ix[ix] = i + 1;\n-\t}\n-}\n-\n static uint32_t name_hash(const char *name)\n {\n \tuint32_t c, hash = 0;\n@@ -908,13 +826,12 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,\n \tstruct object_entry *entry;\n \tstruct packed_git *p, *found_pack = NULL;\n \toff_t found_offset = 0;\n-\tint ix;\n \tuint32_t hash = name_hash(name);\n+\tuint32_t index_pos;\n \n-\tix = nr_objects ? locate_object_entry_hash(sha1) : -1;\n-\tif (ix >= 0) {\n+\tentry = packlist_find(&to_pack, sha1, &index_pos);\n+\tif (entry) {\n \t\tif (exclude) {\n-\t\t\tentry = objects + object_ix[ix] - 1;\n \t\t\tif (!entry->preferred_base)\n \t\t\t\tnr_result--;\n \t\t\tentry->preferred_base = 1;\n@@ -947,14 +864,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,\n \t\t}\n \t}\n \n-\tif (nr_objects >= nr_alloc) {\n-\t\tnr_alloc = (nr_alloc  + 1024) * 3 / 2;\n-\t\tobjects = xrealloc(objects, nr_alloc * sizeof(*entry));\n-\t}\n-\n-\tentry = objects + nr_objects++;\n-\tmemset(entry, 0, sizeof(*entry));\n-\thashcpy(entry->idx.sha1, sha1);\n+\tentry = packlist_alloc(&to_pack, sha1, index_pos);\n \tentry->hash = hash;\n \tif (type)\n \t\tentry->type = type;\n@@ -967,12 +877,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,\n \t\tentry->in_pack_offset = found_offset;\n \t}\n \n-\tif (object_ix_hashsz * 3 <= nr_objects * 4)\n-\t\trehash_objects();\n-\telse\n-\t\tobject_ix[-1 - ix] = nr_objects;\n-\n-\tdisplay_progress(progress_state, nr_objects);\n+\tdisplay_progress(progress_state, to_pack.nr_objects);\n \n \tif (name && no_try_delta(name))\n \t\tentry->no_try_delta = 1;\n@@ -1329,7 +1234,7 @@ static void check_object(struct object_entry *entry)\n \t\t\tbreak;\n \t\t}\n \n-\t\tif (base_ref && (base_entry = locate_object_entry(base_ref))) {\n+\t\tif (base_ref && (base_entry = packlist_find(&to_pack, base_ref, NULL))) {\n \t\t\t/*\n \t\t\t * If base_ref was set above that means we wish to\n \t\t\t * reuse delta data, and we even found that base\n@@ -1403,12 +1308,12 @@ static void get_object_details(void)\n \tuint32_t i;\n \tstruct object_entry **sorted_by_offset;\n \n-\tsorted_by_offset = xcalloc(nr_objects, sizeof(struct object_entry *));\n-\tfor (i = 0; i < nr_objects; i++)\n-\t\tsorted_by_offset[i] = objects + i;\n-\tqsort(sorted_by_offset, nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);\n+\tsorted_by_offset = xcalloc(to_pack.nr_objects, sizeof(struct object_entry *));\n+\tfor (i = 0; i < to_pack.nr_objects; i++)\n+\t\tsorted_by_offset[i] = to_pack.objects + i;\n+\tqsort(sorted_by_offset, to_pack.nr_objects, sizeof(*sorted_by_offset), pack_offset_sort);\n \n-\tfor (i = 0; i < nr_objects; i++) {\n+\tfor (i = 0; i < to_pack.nr_objects; i++) {\n \t\tstruct object_entry *entry = sorted_by_offset[i];\n \t\tcheck_object(entry);\n \t\tif (big_file_threshold < entry->size)\n@@ -2034,7 +1939,7 @@ static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, vo\n \n \tif (!prefixcmp(path, \"refs/tags/\") && /* is a tag? */\n \t    !peel_ref(path, peeled)        && /* peelable? */\n-\t    locate_object_entry(peeled))      /* object packed? */\n+\t    packlist_find(&to_pack, peeled, NULL))      /* object packed? */\n \t\tadd_object_entry(sha1, OBJ_TAG, NULL, 0);\n \treturn 0;\n }\n@@ -2057,14 +1962,14 @@ static void prepare_pack(int window, int depth)\n \tif (!pack_to_stdout)\n \t\tdo_check_packed_object_crc = 1;\n \n-\tif (!nr_objects || !window || !depth)\n+\tif (!to_pack.nr_objects || !window || !depth)\n \t\treturn;\n \n-\tdelta_list = xmalloc(nr_objects * sizeof(*delta_list));\n+\tdelta_list = xmalloc(to_pack.nr_objects * sizeof(*delta_list));\n \tnr_deltas = n = 0;\n \n-\tfor (i = 0; i < nr_objects; i++) {\n-\t\tstruct object_entry *entry = objects + i;\n+\tfor (i = 0; i < to_pack.nr_objects; i++) {\n+\t\tstruct object_entry *entry = to_pack.objects + i;\n \n \t\tif (entry->delta)\n \t\t\t/* This happens if we decided to reuse existing\n@@ -2342,7 +2247,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs)\n \n \t\tfor (i = 0; i < p->num_objects; i++) {\n \t\t\tsha1 = nth_packed_object_sha1(p, i);\n-\t\t\tif (!locate_object_entry(sha1) &&\n+\t\t\tif (!packlist_find(&to_pack, sha1, NULL) &&\n \t\t\t\t!has_sha1_pack_kept_or_nonlocal(sha1))\n \t\t\t\tif (force_object_loose(sha1, p->mtime))\n \t\t\t\t\tdie(\"unable to force loose object\");\ndiff --git a/pack-objects.c b/pack-objects.c\nnew file mode 100644\nindex 0000000..d01d851\n--- /dev/null\n+++ b/pack-objects.c\n@@ -0,0 +1,111 @@\n+#include \"cache.h\"\n+#include \"object.h\"\n+#include \"pack.h\"\n+#include \"pack-objects.h\"\n+\n+static uint32_t locate_object_entry_hash(struct packing_data *pdata,\n+\t\t\t\t\t const unsigned char *sha1,\n+\t\t\t\t\t int *found)\n+{\n+\tuint32_t i, hash, mask = (pdata->index_size - 1);\n+\n+\tmemcpy(&hash, sha1, sizeof(uint32_t));\n+\ti = hash & mask;\n+\n+\twhile (pdata->index[i] > 0) {\n+\t\tuint32_t pos = pdata->index[i] - 1;\n+\n+\t\tif (!hashcmp(sha1, pdata->objects[pos].idx.sha1)) {\n+\t\t\t*found = 1;\n+\t\t\treturn i;\n+\t\t}\n+\n+\t\ti = (i + 1) & mask;\n+\t}\n+\n+\t*found = 0;\n+\treturn i;\n+}\n+\n+static inline uint32_t closest_pow2(uint32_t v)\n+{\n+\tv = v - 1;\n+\tv |= v >> 1;\n+\tv |= v >> 2;\n+\tv |= v >> 4;\n+\tv |= v >> 8;\n+\tv |= v >> 16;\n+\treturn v + 1;\n+}\n+\n+static void rehash_objects(struct packing_data *pdata)\n+{\n+\tuint32_t i;\n+\tstruct object_entry *entry;\n+\n+\tpdata->index_size = closest_pow2(pdata->nr_objects * 3);\n+\tif (pdata->index_size < 1024)\n+\t\tpdata->index_size = 1024;\n+\n+\tpdata->index = xrealloc(pdata->index, sizeof(uint32_t) * pdata->index_size);\n+\tmemset(pdata->index, 0, sizeof(int) * pdata->index_size);\n+\n+\tentry = pdata->objects;\n+\n+\tfor (i = 0; i < pdata->nr_objects; i++) {\n+\t\tint found;\n+\t\tuint32_t ix = locate_object_entry_hash(pdata, entry->idx.sha1, &found);\n+\n+\t\tif (found)\n+\t\t\tdie(\"BUG: Duplicate object in hash\");\n+\n+\t\tpdata->index[ix] = i + 1;\n+\t\tentry++;\n+\t}\n+}\n+\n+struct object_entry *packlist_find(struct packing_data *pdata,\n+\t\t\t\t   const unsigned char *sha1,\n+\t\t\t\t   uint32_t *index_pos)\n+{\n+\tuint32_t i;\n+\tint found;\n+\n+\tif (!pdata->index_size)\n+\t\treturn NULL;\n+\n+\ti = locate_object_entry_hash(pdata, sha1, &found);\n+\n+\tif (index_pos)\n+\t\t*index_pos = i;\n+\n+\tif (!found)\n+\t\treturn NULL;\n+\n+\treturn &pdata->objects[pdata->index[i] - 1];\n+}\n+\n+struct object_entry *packlist_alloc(struct packing_data *pdata,\n+\t\t\t\t    const unsigned char *sha1,\n+\t\t\t\t    uint32_t index_pos)\n+{\n+\tstruct object_entry *new_entry;\n+\n+\tif (pdata->nr_objects >= pdata->nr_alloc) {\n+\t\tpdata->nr_alloc = (pdata->nr_alloc  + 1024) * 3 / 2;\n+\t\tpdata->objects = xrealloc(pdata->objects,\n+\t\t\t\t\t  pdata->nr_alloc * sizeof(*new_entry));\n+\t}\n+\n+\tnew_entry = pdata->objects + pdata->nr_objects++;\n+\n+\tmemset(new_entry, 0, sizeof(*new_entry));\n+\thashcpy(new_entry->idx.sha1, sha1);\n+\n+\tif (pdata->index_size * 3 <= pdata->nr_objects * 4)\n+\t\trehash_objects(pdata);\n+\telse\n+\t\tpdata->index[index_pos] = pdata->nr_objects;\n+\n+\treturn new_entry;\n+}\ndiff --git a/pack-objects.h b/pack-objects.h\nnew file mode 100644\nindex 0000000..f528215\n--- /dev/null\n+++ b/pack-objects.h\n@@ -0,0 +1,47 @@\n+#ifndef PACK_OBJECTS_H\n+#define PACK_OBJECTS_H\n+\n+struct object_entry {\n+\tstruct pack_idx_entry idx;\n+\tunsigned long size;\t/* uncompressed size */\n+\tstruct packed_git *in_pack;\t/* already in pack */\n+\toff_t in_pack_offset;\n+\tstruct object_entry *delta;\t/* delta base object */\n+\tstruct object_entry *delta_child; /* deltified objects who bases me */\n+\tstruct object_entry *delta_sibling; /* other deltified objects who\n+\t\t\t\t\t     * uses the same base as me\n+\t\t\t\t\t     */\n+\tvoid *delta_data;\t/* cached delta (uncompressed) */\n+\tunsigned long delta_size;\t/* delta data size (uncompressed) */\n+\tunsigned long z_delta_size;\t/* delta data size (compressed) */\n+\tenum object_type type;\n+\tenum object_type in_pack_type;\t/* could be delta */\n+\tuint32_t hash;\t\t\t/* name hint hash */\n+\tunsigned char in_pack_header_size;\n+\tunsigned preferred_base:1; /*\n+\t\t\t\t    * we do not pack this, but is available\n+\t\t\t\t    * to be used as the base object to delta\n+\t\t\t\t    * objects against.\n+\t\t\t\t    */\n+\tunsigned no_try_delta:1;\n+\tunsigned tagged:1; /* near the very tip of refs */\n+\tunsigned filled:1; /* assigned write-order */\n+};\n+\n+struct packing_data {\n+\tstruct object_entry *objects;\n+\tuint32_t nr_objects, nr_alloc;\n+\n+\tint32_t *index;\n+\tuint32_t index_size;\n+};\n+\n+struct object_entry *packlist_alloc(struct packing_data *pdata,\n+\t\t\t\t    const unsigned char *sha1,\n+\t\t\t\t    uint32_t index_pos);\n+\n+struct object_entry *packlist_find(struct packing_data *pdata,\n+\t\t\t\t   const unsigned char *sha1,\n+\t\t\t\t   uint32_t *index_pos);\n+\n+#endif\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229478","messageId":"20131025060315.GB23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 04/19] pack-objects: factor out name_hash","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:03:15Z","receivedAt":"2013-10-25T06:03:15Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nAs the pack-objects system grows beyond the single\npack-objects.c file, more parts (like the soon-to-exist\nbitmap code) will need to compute hashes for matching\ndeltas. Factor out name_hash to make it available to other\nfiles.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/pack-objects.c | 24 ++----------------------\n pack-objects.h         | 20 ++++++++++++++++++++\n 2 files changed, 22 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex f3f0cf9..faf746b 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -778,26 +778,6 @@ static void write_pack_file(void)\n \t\t\twritten, nr_result);\n }\n \n-static uint32_t name_hash(const char *name)\n-{\n-\tuint32_t c, hash = 0;\n-\n-\tif (!name)\n-\t\treturn 0;\n-\n-\t/*\n-\t * This effectively just creates a sortable number from the\n-\t * last sixteen non-whitespace characters. Last characters\n-\t * count \"most\", so things that end in \".c\" sort together.\n-\t */\n-\twhile ((c = *name++) != 0) {\n-\t\tif (isspace(c))\n-\t\t\tcontinue;\n-\t\thash = (hash >> 2) + (c << 24);\n-\t}\n-\treturn hash;\n-}\n-\n static void setup_delta_attr_check(struct git_attr_check *check)\n {\n \tstatic struct git_attr *attr_delta;\n@@ -826,7 +806,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,\n \tstruct object_entry *entry;\n \tstruct packed_git *p, *found_pack = NULL;\n \toff_t found_offset = 0;\n-\tuint32_t hash = name_hash(name);\n+\tuint32_t hash = pack_name_hash(name);\n \tuint32_t index_pos;\n \n \tentry = packlist_find(&to_pack, sha1, &index_pos);\n@@ -1082,7 +1062,7 @@ static void add_preferred_base_object(const char *name)\n {\n \tstruct pbase_tree *it;\n \tint cmplen;\n-\tunsigned hash = name_hash(name);\n+\tunsigned hash = pack_name_hash(name);\n \n \tif (!num_preferred_base || check_pbase_path(hash))\n \t\treturn;\ndiff --git a/pack-objects.h b/pack-objects.h\nindex f528215..90ad0a8 100644\n--- a/pack-objects.h\n+++ b/pack-objects.h\n@@ -44,4 +44,24 @@ struct object_entry *packlist_find(struct packing_data *pdata,\n \t\t\t\t   const unsigned char *sha1,\n \t\t\t\t   uint32_t *index_pos);\n \n+static inline uint32_t pack_name_hash(const char *name)\n+{\n+\tuint32_t c, hash = 0;\n+\n+\tif (!name)\n+\t\treturn 0;\n+\n+\t/*\n+\t * This effectively just creates a sortable number from the\n+\t * last sixteen non-whitespace characters. Last characters\n+\t * count \"most\", so things that end in \".c\" sort together.\n+\t */\n+\twhile ((c = *name++) != 0) {\n+\t\tif (isspace(c))\n+\t\t\tcontinue;\n+\t\thash = (hash >> 2) + (c << 24);\n+\t}\n+\treturn hash;\n+}\n+\n #endif\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229480","messageId":"20131025060319.GC23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 05/19] revision: allow setting custom limiter function","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:03:19Z","receivedAt":"2013-10-25T06:03:19Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThis commit enables users of `struct rev_info` to peform custom limiting\nduring a revision walk (i.e. `get_revision`).\n\nIf the field `include_check` has been set to a callback, this callback\nwill be issued once for each commit before it is added to the \"pending\"\nlist of the revwalk. If the include check returns 0, the commit will be\nmarked as added but won't be pushed to the pending list, effectively\nlimiting the walk.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n revision.c | 4 ++++\n revision.h | 2 ++\n 2 files changed, 6 insertions(+)\n\ndiff --git a/revision.c b/revision.c\nindex 0173e01..cddd605 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -779,6 +779,10 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,\n \t\treturn 0;\n \tcommit->object.flags |= ADDED;\n \n+\tif (revs->include_check &&\n+\t    !revs->include_check(commit, revs->include_check_data))\n+\t\treturn 0;\n+\n \t/*\n \t * If the commit is uninteresting, don't try to\n \t * prune parents - we want the maximal uninteresting\ndiff --git a/revision.h b/revision.h\nindex e7f1d21..9957f3c 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -168,6 +168,8 @@ struct rev_info {\n \tunsigned long min_age;\n \tint min_parents;\n \tint max_parents;\n+\tint (*include_check)(struct commit *, void *);\n+\tvoid *include_check_data;\n \n \t/* diff info for patches and for paths limiting */\n \tstruct diff_options diffopt;\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229482","messageId":"20131025060322.GD23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 06/19] sha1_file: export `git_open_noatime`","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:03:23Z","receivedAt":"2013-10-25T06:03:23Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThe `git_open_noatime` helper can be of general interest for other\nconsumers of git's different on-disk formats.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n cache.h     | 1 +\n sha1_file.c | 4 +---\n 2 files changed, 2 insertions(+), 3 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 5e3fc72..f2e5aa7 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -780,6 +780,7 @@ extern int hash_sha1_file(const void *buf, unsigned long len, const char *type,\n extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);\n extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);\n extern int force_object_loose(const unsigned char *sha1, time_t mtime);\n+extern int git_open_noatime(const char *name);\n extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);\n extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);\n extern int parse_sha1_header(const char *hdr, unsigned long *sizep);\ndiff --git a/sha1_file.c b/sha1_file.c\nindex f80bbe4..4714bd8 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -239,8 +239,6 @@ char *sha1_pack_index_name(const unsigned char *sha1)\n struct alternate_object_database *alt_odb_list;\n static struct alternate_object_database **alt_odb_tail;\n \n-static int git_open_noatime(const char *name);\n-\n /*\n  * Prepare alternate object database registry.\n  *\n@@ -1357,7 +1355,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map,\n \treturn hashcmp(sha1, real_sha1) ? -1 : 0;\n }\n \n-static int git_open_noatime(const char *name)\n+int git_open_noatime(const char *name)\n {\n \tstatic int sha1_file_open_flag = O_NOATIME;\n \n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229483","messageId":"20131025060326.GE23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 07/19] compat: add endianness helpers","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:03:26Z","receivedAt":"2013-10-25T06:03:26Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThe POSIX standard doesn't currently define a `nothll`/`htonll`\nfunction pair to perform network-to-host and host-to-network\nswaps of 64-bit data. These 64-bit swaps are necessary for the on-disk\nstorage of EWAH bitmaps if they are not in native byte order.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n compat/bswap.h | 35 +++++++++++++++++++++++++++++++++++\n 1 file changed, 35 insertions(+)\n\ndiff --git a/compat/bswap.h b/compat/bswap.h\nindex 5061214..ea1a9ed 100644\n--- a/compat/bswap.h\n+++ b/compat/bswap.h\n@@ -47,4 +47,39 @@ static inline uint32_t git_bswap32(uint32_t x)\n #define ntohl(x) bswap32(x)\n #define htonl(x) bswap32(x)\n \n+#ifndef __BYTE_ORDER\n+#\tif defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)\n+#\t\tdefine __BYTE_ORDER BYTE_ORDER\n+#\t\tdefine __LITTLE_ENDIAN LITTLE_ENDIAN\n+#\t\tdefine __BIG_ENDIAN BIG_ENDIAN\n+#\telse\n+#\t\terror \"Cannot determine endianness\"\n+#\tendif\n+#endif\n+\n+#if __BYTE_ORDER == __BIG_ENDIAN\n+# define ntohll(n) (n)\n+# define htonll(n) (n)\n+#elif __BYTE_ORDER == __LITTLE_ENDIAN\n+#\tif defined(__GNUC__) && defined(__GLIBC__)\n+#\t\tinclude <byteswap.h>\n+#\telse /* GNUC & GLIBC */\n+static inline uint64_t bswap_64(uint64_t val)\n+{\n+\treturn ((val & (uint64_t)0x00000000000000ffULL) << 56)\n+\t\t| ((val & (uint64_t)0x000000000000ff00ULL) << 40)\n+\t\t| ((val & (uint64_t)0x0000000000ff0000ULL) << 24)\n+\t\t| ((val & (uint64_t)0x00000000ff000000ULL) <<  8)\n+\t\t| ((val & (uint64_t)0x000000ff00000000ULL) >>  8)\n+\t\t| ((val & (uint64_t)0x0000ff0000000000ULL) >> 24)\n+\t\t| ((val & (uint64_t)0x00ff000000000000ULL) >> 40)\n+\t\t| ((val & (uint64_t)0xff00000000000000ULL) >> 56);\n+}\n+#\tendif /* GNUC & GLIBC */\n+#\tdefine ntohll(n) bswap_64(n)\n+#\tdefine htonll(n) bswap_64(n)\n+#else /* __BYTE_ORDER */\n+#\terror \"Can't define htonll or ntohll!\"\n+#endif\n+\n #endif\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229481","messageId":"20131025060330.GF23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 08/19] ewah: compressed bitmap implementation","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:03:31Z","receivedAt":"2013-10-25T06:03:31Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nEWAH is a word-aligned compressed variant of a bitset (i.e. a data\nstructure that acts as a 0-indexed boolean array for many entries).\n\nIt uses a 64-bit run-length encoding (RLE) compression scheme,\ntrading some compression for better processing speed.\n\nThe goal of this word-aligned implementation is not to achieve\nthe best compression, but rather to improve query processing time.\nAs it stands right now, this EWAH implementation will always be more\nefficient storage-wise than its uncompressed alternative.\n\nEWAH arrays will be used as the on-disk format to store reachability\nbitmaps for all objects in a repository while keeping reasonable sizes,\nin the same way that JGit does.\n\nThis EWAH implementation is a mostly straightforward port of the\noriginal `javaewah` library that JGit currently uses. The library is\nself-contained and has been embedded whole (4 files) inside the `ewah`\nfolder to ease redistribution.\n\nThe library is re-licensed under the GPLv2 with the permission of Daniel\nLemire, the original author. The source code for the C version can\nbe found on GitHub:\n\n\thttps://github.com/vmg/libewok\n\nThe original Java implementation can also be found on GitHub:\n\n\thttps://github.com/lemire/javaewah\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Makefile           |   6 +\n ewah/bitmap.c      | 224 +++++++++++++++++\n ewah/ewah_bitmap.c | 727 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n ewah/ewah_io.c     | 197 +++++++++++++++\n ewah/ewah_rlw.c    | 119 +++++++++\n ewah/ewok.h        | 223 ++++++++++++++++\n ewah/ewok_rlw.h    | 114 +++++++++\n 7 files changed, 1610 insertions(+)\n create mode 100644 ewah/bitmap.c\n create mode 100644 ewah/ewah_bitmap.c\n create mode 100644 ewah/ewah_io.c\n create mode 100644 ewah/ewah_rlw.c\n create mode 100644 ewah/ewok.h\n create mode 100644 ewah/ewok_rlw.h\n\ndiff --git a/Makefile b/Makefile\nindex 48ff0bd..eacb940 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -667,6 +667,8 @@ LIB_H += diff.h\n LIB_H += diffcore.h\n LIB_H += dir.h\n LIB_H += exec_cmd.h\n+LIB_H += ewah/ewok.h\n+LIB_H += ewah/ewok_rlw.h\n LIB_H += fetch-pack.h\n LIB_H += fmt-merge-msg.h\n LIB_H += fsck.h\n@@ -800,6 +802,10 @@ LIB_OBJS += dir.o\n LIB_OBJS += editor.o\n LIB_OBJS += entry.o\n LIB_OBJS += environment.o\n+LIB_OBJS += ewah/bitmap.o\n+LIB_OBJS += ewah/ewah_bitmap.o\n+LIB_OBJS += ewah/ewah_io.o\n+LIB_OBJS += ewah/ewah_rlw.o\n LIB_OBJS += exec_cmd.o\n LIB_OBJS += fetch-pack.o\n LIB_OBJS += fsck.o\ndiff --git a/ewah/bitmap.c b/ewah/bitmap.c\nnew file mode 100644\nindex 0000000..3e44970\n--- /dev/null\n+++ b/ewah/bitmap.c\n@@ -0,0 +1,224 @@\n+/**\n+ * Copyright 2013, GitHub, Inc\n+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n+ *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; either version 2\n+ * of the License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+ */\n+#include <assert.h>\n+#include <stdlib.h>\n+#include <string.h>\n+\n+#include \"ewok.h\"\n+\n+#define MASK(x) ((eword_t)1 << (x % BITS_IN_WORD))\n+#define BLOCK(x) (x / BITS_IN_WORD)\n+\n+struct bitmap *bitmap_new(void)\n+{\n+\tstruct bitmap *bitmap = ewah_malloc(sizeof(struct bitmap));\n+\tbitmap->words = ewah_calloc(32, sizeof(eword_t));\n+\tbitmap->word_alloc = 32;\n+\treturn bitmap;\n+}\n+\n+void bitmap_set(struct bitmap *self, size_t pos)\n+{\n+\tsize_t block = BLOCK(pos);\n+\n+\tif (block >= self->word_alloc) {\n+\t\tsize_t old_size = self->word_alloc;\n+\t\tself->word_alloc = block * 2;\n+\t\tself->words = ewah_realloc(self->words,\n+\t\t\tself->word_alloc * sizeof(eword_t));\n+\n+\t\tmemset(self->words + old_size, 0x0,\n+\t\t\t(self->word_alloc - old_size) * sizeof(eword_t));\n+\t}\n+\n+\tself->words[block] |= MASK(pos);\n+}\n+\n+void bitmap_clear(struct bitmap *self, size_t pos)\n+{\n+\tsize_t block = BLOCK(pos);\n+\n+\tif (block < self->word_alloc)\n+\t\tself->words[block] &= ~MASK(pos);\n+}\n+\n+int bitmap_get(struct bitmap *self, size_t pos)\n+{\n+\tsize_t block = BLOCK(pos);\n+\treturn block < self->word_alloc &&\n+\t\t(self->words[block] & MASK(pos)) != 0;\n+}\n+\n+struct ewah_bitmap *bitmap_to_ewah(struct bitmap *bitmap)\n+{\n+\tstruct ewah_bitmap *ewah = ewah_new();\n+\tsize_t i, running_empty_words = 0;\n+\teword_t last_word = 0;\n+\n+\tfor (i = 0; i < bitmap->word_alloc; ++i) {\n+\t\tif (bitmap->words[i] == 0) {\n+\t\t\trunning_empty_words++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (last_word != 0)\n+\t\t\tewah_add(ewah, last_word);\n+\n+\t\tif (running_empty_words > 0) {\n+\t\t\tewah_add_empty_words(ewah, 0, running_empty_words);\n+\t\t\trunning_empty_words = 0;\n+\t\t}\n+\n+\t\tlast_word = bitmap->words[i];\n+\t}\n+\n+\tewah_add(ewah, last_word);\n+\treturn ewah;\n+}\n+\n+struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah)\n+{\n+\tstruct bitmap *bitmap = bitmap_new();\n+\tstruct ewah_iterator it;\n+\teword_t blowup;\n+\tsize_t i = 0;\n+\n+\tewah_iterator_init(&it, ewah);\n+\n+\twhile (ewah_iterator_next(&blowup, &it)) {\n+\t\tif (i >= bitmap->word_alloc) {\n+\t\t\tbitmap->word_alloc *= 1.5;\n+\t\t\tbitmap->words = ewah_realloc(\n+\t\t\t\tbitmap->words, bitmap->word_alloc * sizeof(eword_t));\n+\t\t}\n+\n+\t\tbitmap->words[i++] = blowup;\n+\t}\n+\n+\tbitmap->word_alloc = i;\n+\treturn bitmap;\n+}\n+\n+void bitmap_and_not(struct bitmap *self, struct bitmap *other)\n+{\n+\tconst size_t count = (self->word_alloc < other->word_alloc) ?\n+\t\tself->word_alloc : other->word_alloc;\n+\n+\tsize_t i;\n+\n+\tfor (i = 0; i < count; ++i)\n+\t\tself->words[i] &= ~other->words[i];\n+}\n+\n+void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)\n+{\n+\tsize_t original_size = self->word_alloc;\n+\tsize_t other_final = (other->bit_size / BITS_IN_WORD) + 1;\n+\tsize_t i = 0;\n+\tstruct ewah_iterator it;\n+\teword_t word;\n+\n+\tif (self->word_alloc < other_final) {\n+\t\tself->word_alloc = other_final;\n+\t\tself->words = ewah_realloc(self->words,\n+\t\t\tself->word_alloc * sizeof(eword_t));\n+\t\tmemset(self->words + original_size, 0x0,\n+\t\t\t(self->word_alloc - original_size) * sizeof(eword_t));\n+\t}\n+\n+\tewah_iterator_init(&it, other);\n+\n+\twhile (ewah_iterator_next(&word, &it))\n+\t\tself->words[i++] |= word;\n+}\n+\n+void bitmap_each_bit(struct bitmap *self, ewah_callback callback, void *data)\n+{\n+\tsize_t pos = 0, i;\n+\n+\tfor (i = 0; i < self->word_alloc; ++i) {\n+\t\teword_t word = self->words[i];\n+\t\tuint32_t offset;\n+\n+\t\tif (word == (eword_t)~0) {\n+\t\t\tfor (offset = 0; offset < BITS_IN_WORD; ++offset)\n+\t\t\t\tcallback(pos++, data);\n+\t\t} else {\n+\t\t\tfor (offset = 0; offset < BITS_IN_WORD; ++offset) {\n+\t\t\t\tif ((word >> offset) == 0)\n+\t\t\t\t\tbreak;\n+\n+\t\t\t\toffset += ewah_bit_ctz64(word >> offset);\n+\t\t\t\tcallback(pos + offset, data);\n+\t\t\t}\n+\t\t\tpos += BITS_IN_WORD;\n+\t\t}\n+\t}\n+}\n+\n+size_t bitmap_popcount(struct bitmap *self)\n+{\n+\tsize_t i, count = 0;\n+\n+\tfor (i = 0; i < self->word_alloc; ++i)\n+\t\tcount += ewah_bit_popcount64(self->words[i]);\n+\n+\treturn count;\n+}\n+\n+int bitmap_equals(struct bitmap *self, struct bitmap *other)\n+{\n+\tstruct bitmap *big, *small;\n+\tsize_t i;\n+\n+\tif (self->word_alloc < other->word_alloc) {\n+\t\tsmall = self;\n+\t\tbig = other;\n+\t} else {\n+\t\tsmall = other;\n+\t\tbig = self;\n+\t}\n+\n+\tfor (i = 0; i < small->word_alloc; ++i) {\n+\t\tif (small->words[i] != big->words[i])\n+\t\t\treturn 0;\n+\t}\n+\n+\tfor (; i < big->word_alloc; ++i) {\n+\t\tif (big->words[i] != 0)\n+\t\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+void bitmap_reset(struct bitmap *bitmap)\n+{\n+\tmemset(bitmap->words, 0x0, bitmap->word_alloc * sizeof(eword_t));\n+}\n+\n+void bitmap_free(struct bitmap *bitmap)\n+{\n+\tif (bitmap == NULL)\n+\t\treturn;\n+\n+\tfree(bitmap->words);\n+\tfree(bitmap);\n+}\ndiff --git a/ewah/ewah_bitmap.c b/ewah/ewah_bitmap.c\nnew file mode 100644\nindex 0000000..625f5a6\n--- /dev/null\n+++ b/ewah/ewah_bitmap.c\n@@ -0,0 +1,727 @@\n+/**\n+ * Copyright 2013, GitHub, Inc\n+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n+ *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; either version 2\n+ * of the License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+ */\n+#include <assert.h>\n+#include <stdlib.h>\n+#include <unistd.h>\n+#include <string.h>\n+#include <stdio.h>\n+\n+#include \"ewok.h\"\n+#include \"ewok_rlw.h\"\n+\n+static inline size_t min_size(size_t a, size_t b)\n+{\n+\treturn a < b ? a : b;\n+}\n+\n+static inline size_t max_size(size_t a, size_t b)\n+{\n+\treturn a > b ? a : b;\n+}\n+\n+static inline void buffer_grow(struct ewah_bitmap *self, size_t new_size)\n+{\n+\tsize_t rlw_offset = (uint8_t *)self->rlw - (uint8_t *)self->buffer;\n+\n+\tif (self->alloc_size >= new_size)\n+\t\treturn;\n+\n+\tself->alloc_size = new_size;\n+\tself->buffer = ewah_realloc(self->buffer,\n+\t\tself->alloc_size * sizeof(eword_t));\n+\tself->rlw = self->buffer + (rlw_offset / sizeof(size_t));\n+}\n+\n+static inline void buffer_push(struct ewah_bitmap *self, eword_t value)\n+{\n+\tif (self->buffer_size + 1 >= self->alloc_size)\n+\t\tbuffer_grow(self, self->buffer_size * 3 / 2);\n+\n+\tself->buffer[self->buffer_size++] = value;\n+}\n+\n+static void buffer_push_rlw(struct ewah_bitmap *self, eword_t value)\n+{\n+\tbuffer_push(self, value);\n+\tself->rlw = self->buffer + self->buffer_size - 1;\n+}\n+\n+static size_t add_empty_words(struct ewah_bitmap *self, int v, size_t number)\n+{\n+\tsize_t added = 0;\n+\teword_t runlen, can_add;\n+\n+\tif (rlw_get_run_bit(self->rlw) != v && rlw_size(self->rlw) == 0) {\n+\t\trlw_set_run_bit(self->rlw, v);\n+\t} else if (rlw_get_literal_words(self->rlw) != 0 ||\n+\t\t\trlw_get_run_bit(self->rlw) != v) {\n+\t\tbuffer_push_rlw(self, 0);\n+\t\tif (v) rlw_set_run_bit(self->rlw, v);\n+\t\tadded++;\n+\t}\n+\n+\trunlen = rlw_get_running_len(self->rlw);\n+\tcan_add = min_size(number, RLW_LARGEST_RUNNING_COUNT - runlen);\n+\n+\trlw_set_running_len(self->rlw, runlen + can_add);\n+\tnumber -= can_add;\n+\n+\twhile (number >= RLW_LARGEST_RUNNING_COUNT) {\n+\t\tbuffer_push_rlw(self, 0);\n+\t\tadded++;\n+\t\tif (v) rlw_set_run_bit(self->rlw, v);\n+\t\trlw_set_running_len(self->rlw, RLW_LARGEST_RUNNING_COUNT);\n+\t\tnumber -= RLW_LARGEST_RUNNING_COUNT;\n+\t}\n+\n+\tif (number > 0) {\n+\t\tbuffer_push_rlw(self, 0);\n+\t\tadded++;\n+\n+\t\tif (v) rlw_set_run_bit(self->rlw, v);\n+\t\trlw_set_running_len(self->rlw, number);\n+\t}\n+\n+\treturn added;\n+}\n+\n+size_t ewah_add_empty_words(struct ewah_bitmap *self, int v, size_t number)\n+{\n+\tif (number == 0)\n+\t\treturn 0;\n+\n+\tself->bit_size += number * BITS_IN_WORD;\n+\treturn add_empty_words(self, v, number);\n+}\n+\n+static size_t add_literal(struct ewah_bitmap *self, eword_t new_data)\n+{\n+\teword_t current_num = rlw_get_literal_words(self->rlw);\n+\n+\tif (current_num >= RLW_LARGEST_LITERAL_COUNT) {\n+\t\tbuffer_push_rlw(self, 0);\n+\n+\t\trlw_set_literal_words(self->rlw, 1);\n+\t\tbuffer_push(self, new_data);\n+\t\treturn 2;\n+\t}\n+\n+\trlw_set_literal_words(self->rlw, current_num + 1);\n+\n+\t/* sanity check */\n+\tassert(rlw_get_literal_words(self->rlw) == current_num + 1);\n+\n+\tbuffer_push(self, new_data);\n+\treturn 1;\n+}\n+\n+void ewah_add_dirty_words(\n+\tstruct ewah_bitmap *self, const eword_t *buffer,\n+\tsize_t number, int negate)\n+{\n+\tsize_t literals, can_add;\n+\n+\twhile (1) {\n+\t\tliterals = rlw_get_literal_words(self->rlw);\n+\t\tcan_add = min_size(number, RLW_LARGEST_LITERAL_COUNT - literals);\n+\n+\t\trlw_set_literal_words(self->rlw, literals + can_add);\n+\n+\t\tif (self->buffer_size + can_add >= self->alloc_size)\n+\t\t\tbuffer_grow(self, (self->buffer_size + can_add) * 3 / 2);\n+\n+\t\tif (negate) {\n+\t\t\tsize_t i;\n+\t\t\tfor (i = 0; i < can_add; ++i)\n+\t\t\t\tself->buffer[self->buffer_size++] = ~buffer[i];\n+\t\t} else {\n+\t\t\tmemcpy(self->buffer + self->buffer_size,\n+\t\t\t\tbuffer, can_add * sizeof(eword_t));\n+\t\t\tself->buffer_size += can_add;\n+\t\t}\n+\n+\t\tself->bit_size += can_add * BITS_IN_WORD;\n+\n+\t\tif (number - can_add == 0)\n+\t\t\tbreak;\n+\n+\t\tbuffer_push_rlw(self, 0);\n+\t\tbuffer += can_add;\n+\t\tnumber -= can_add;\n+\t}\n+}\n+\n+static size_t add_empty_word(struct ewah_bitmap *self, int v)\n+{\n+\tint no_literal = (rlw_get_literal_words(self->rlw) == 0);\n+\teword_t run_len = rlw_get_running_len(self->rlw);\n+\n+\tif (no_literal && run_len == 0) {\n+\t\trlw_set_run_bit(self->rlw, v);\n+\t\tassert(rlw_get_run_bit(self->rlw) == v);\n+\t}\n+\n+\tif (no_literal && rlw_get_run_bit(self->rlw) == v &&\n+\t\trun_len < RLW_LARGEST_RUNNING_COUNT) {\n+\t\trlw_set_running_len(self->rlw, run_len + 1);\n+\t\tassert(rlw_get_running_len(self->rlw) == run_len + 1);\n+\t\treturn 0;\n+\t} else {\n+\t\tbuffer_push_rlw(self, 0);\n+\n+\t\tassert(rlw_get_running_len(self->rlw) == 0);\n+\t\tassert(rlw_get_run_bit(self->rlw) == 0);\n+\t\tassert(rlw_get_literal_words(self->rlw) == 0);\n+\n+\t\trlw_set_run_bit(self->rlw, v);\n+\t\tassert(rlw_get_run_bit(self->rlw) == v);\n+\n+\t\trlw_set_running_len(self->rlw, 1);\n+\t\tassert(rlw_get_running_len(self->rlw) == 1);\n+\t\tassert(rlw_get_literal_words(self->rlw) == 0);\n+\t\treturn 1;\n+\t}\n+}\n+\n+size_t ewah_add(struct ewah_bitmap *self, eword_t word)\n+{\n+\tself->bit_size += BITS_IN_WORD;\n+\n+\tif (word == 0)\n+\t\treturn add_empty_word(self, 0);\n+\n+\tif (word == (eword_t)(~0))\n+\t\treturn add_empty_word(self, 1);\n+\n+\treturn add_literal(self, word);\n+}\n+\n+void ewah_set(struct ewah_bitmap *self, size_t i)\n+{\n+\tconst size_t dist =\n+\t\t(i + BITS_IN_WORD) / BITS_IN_WORD -\n+\t\t(self->bit_size + BITS_IN_WORD - 1) / BITS_IN_WORD;\n+\n+\tassert(i >= self->bit_size);\n+\n+\tself->bit_size = i + 1;\n+\n+\tif (dist > 0) {\n+\t\tif (dist > 1)\n+\t\t\tadd_empty_words(self, 0, dist - 1);\n+\n+\t\tadd_literal(self, (eword_t)1 << (i % BITS_IN_WORD));\n+\t\treturn;\n+\t}\n+\n+\tif (rlw_get_literal_words(self->rlw) == 0) {\n+\t\trlw_set_running_len(self->rlw,\n+\t\t\trlw_get_running_len(self->rlw) - 1);\n+\t\tadd_literal(self, (eword_t)1 << (i % BITS_IN_WORD));\n+\t\treturn;\n+\t}\n+\n+\tself->buffer[self->buffer_size - 1] |=\n+\t\t((eword_t)1 << (i % BITS_IN_WORD));\n+\n+\t/* check if we just completed a stream of 1s */\n+\tif (self->buffer[self->buffer_size - 1] == (eword_t)(~0)) {\n+\t\tself->buffer[--self->buffer_size] = 0;\n+\t\trlw_set_literal_words(self->rlw,\n+\t\t\trlw_get_literal_words(self->rlw) - 1);\n+\t\tadd_empty_word(self, 1);\n+\t}\n+}\n+\n+void ewah_each_bit(struct ewah_bitmap *self, void (*callback)(size_t, void*), void *payload)\n+{\n+\tsize_t pos = 0;\n+\tsize_t pointer = 0;\n+\tsize_t k;\n+\n+\twhile (pointer < self->buffer_size) {\n+\t\teword_t *word = &self->buffer[pointer];\n+\n+\t\tif (rlw_get_run_bit(word)) {\n+\t\t\tsize_t len = rlw_get_running_len(word) * BITS_IN_WORD;\n+\t\t\tfor (k = 0; k < len; ++k, ++pos)\n+\t\t\t\tcallback(pos, payload);\n+\t\t} else {\n+\t\t\tpos += rlw_get_running_len(word) * BITS_IN_WORD;\n+\t\t}\n+\n+\t\t++pointer;\n+\n+\t\tfor (k = 0; k < rlw_get_literal_words(word); ++k) {\n+\t\t\tint c;\n+\n+\t\t\t/* todo: zero count optimization */\n+\t\t\tfor (c = 0; c < BITS_IN_WORD; ++c, ++pos) {\n+\t\t\t\tif ((self->buffer[pointer] & ((eword_t)1 << c)) != 0)\n+\t\t\t\t\tcallback(pos, payload);\n+\t\t\t}\n+\n+\t\t\t++pointer;\n+\t\t}\n+\t}\n+}\n+\n+struct ewah_bitmap *ewah_new(void)\n+{\n+\tstruct ewah_bitmap *self;\n+\n+\tself = ewah_malloc(sizeof(struct ewah_bitmap));\n+\tif (self == NULL)\n+\t\treturn NULL;\n+\n+\tself->buffer = ewah_malloc(32 * sizeof(eword_t));\n+\tself->alloc_size = 32;\n+\n+\tewah_clear(self);\n+\treturn self;\n+}\n+\n+void ewah_clear(struct ewah_bitmap *self)\n+{\n+\tself->buffer_size = 1;\n+\tself->buffer[0] = 0;\n+\tself->bit_size = 0;\n+\tself->rlw = self->buffer;\n+}\n+\n+void ewah_free(struct ewah_bitmap *self)\n+{\n+\tif (self->alloc_size)\n+\t\tfree(self->buffer);\n+\n+\tfree(self);\n+}\n+\n+static void read_new_rlw(struct ewah_iterator *it)\n+{\n+\tconst eword_t *word = NULL;\n+\n+\tit->literals = 0;\n+\tit->compressed = 0;\n+\n+\twhile (1) {\n+\t\tword = &it->buffer[it->pointer];\n+\n+\t\tit->rl = rlw_get_running_len(word);\n+\t\tit->lw = rlw_get_literal_words(word);\n+\t\tit->b = rlw_get_run_bit(word);\n+\n+\t\tif (it->rl || it->lw)\n+\t\t\treturn;\n+\n+\t\tif (it->pointer < it->buffer_size - 1) {\n+\t\t\tit->pointer++;\n+\t\t} else {\n+\t\t\tit->pointer = it->buffer_size;\n+\t\t\treturn;\n+\t\t}\n+\t}\n+}\n+\n+int ewah_iterator_next(eword_t *next, struct ewah_iterator *it)\n+{\n+\tif (it->pointer >= it->buffer_size)\n+\t\treturn 0;\n+\n+\tif (it->compressed < it->rl) {\n+\t\tit->compressed++;\n+\t\t*next = it->b ? (eword_t)(~0) : 0;\n+\t} else {\n+\t\tassert(it->literals < it->lw);\n+\n+\t\tit->literals++;\n+\t\tit->pointer++;\n+\n+\t\tassert(it->pointer < it->buffer_size);\n+\n+\t\t*next = it->buffer[it->pointer];\n+\t}\n+\n+\tif (it->compressed == it->rl && it->literals == it->lw) {\n+\t\tif (++it->pointer < it->buffer_size)\n+\t\t\tread_new_rlw(it);\n+\t}\n+\n+\treturn 1;\n+}\n+\n+void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent)\n+{\n+\tit->buffer = parent->buffer;\n+\tit->buffer_size = parent->buffer_size;\n+\tit->pointer = 0;\n+\n+\tit->lw = 0;\n+\tit->rl = 0;\n+\tit->compressed = 0;\n+\tit->literals = 0;\n+\tit->b = 0;\n+\n+\tif (it->pointer < it->buffer_size)\n+\t\tread_new_rlw(it);\n+}\n+\n+void ewah_dump(struct ewah_bitmap *self)\n+{\n+\tsize_t i;\n+\tfprintf(stderr, \"%zu bits | %zu words | \", self->bit_size, self->buffer_size);\n+\n+\tfor (i = 0; i < self->buffer_size; ++i)\n+\t\tfprintf(stderr, \"%016llx \", (unsigned long long)self->buffer[i]);\n+\n+\tfprintf(stderr, \"\\n\");\n+}\n+\n+void ewah_not(struct ewah_bitmap *self)\n+{\n+\tsize_t pointer = 0;\n+\n+\twhile (pointer < self->buffer_size) {\n+\t\teword_t *word = &self->buffer[pointer];\n+\t\tsize_t literals, k;\n+\n+\t\trlw_xor_run_bit(word);\n+\t\t++pointer;\n+\n+\t\tliterals = rlw_get_literal_words(word);\n+\t\tfor (k = 0; k < literals; ++k) {\n+\t\t\tself->buffer[pointer] = ~self->buffer[pointer];\n+\t\t\t++pointer;\n+\t\t}\n+\t}\n+}\n+\n+void ewah_xor(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out)\n+{\n+\tstruct rlw_iterator rlw_i;\n+\tstruct rlw_iterator rlw_j;\n+\tsize_t literals;\n+\n+\trlwit_init(&rlw_i, ewah_i);\n+\trlwit_init(&rlw_j, ewah_j);\n+\n+\twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n+\t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n+\t\t\tstruct rlw_iterator *prey, *predator;\n+\t\t\tsize_t index;\n+\t\t\tint negate_words;\n+\n+\t\t\tif (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {\n+\t\t\t\tprey = &rlw_i;\n+\t\t\t\tpredator = &rlw_j;\n+\t\t\t} else {\n+\t\t\t\tprey = &rlw_j;\n+\t\t\t\tpredator = &rlw_i;\n+\t\t\t}\n+\n+\t\t\tnegate_words = !!predator->rlw.running_bit;\n+\t\t\tindex = rlwit_discharge(prey, out,\n+\t\t\t\tpredator->rlw.running_len, negate_words);\n+\n+\t\t\tewah_add_empty_words(out, negate_words,\n+\t\t\t\tpredator->rlw.running_len - index);\n+\n+\t\t\trlwit_discard_first_words(predator,\n+\t\t\t\tpredator->rlw.running_len);\n+\t\t}\n+\n+\t\tliterals = min_size(\n+\t\t\trlw_i.rlw.literal_words,\n+\t\t\trlw_j.rlw.literal_words);\n+\n+\t\tif (literals) {\n+\t\t\tsize_t k;\n+\n+\t\t\tfor (k = 0; k < literals; ++k) {\n+\t\t\t\tewah_add(out,\n+\t\t\t\t\trlw_i.buffer[rlw_i.literal_word_start + k] ^\n+\t\t\t\t\trlw_j.buffer[rlw_j.literal_word_start + k]\n+\t\t\t\t);\n+\t\t\t}\n+\n+\t\t\trlwit_discard_first_words(&rlw_i, literals);\n+\t\t\trlwit_discard_first_words(&rlw_j, literals);\n+\t\t}\n+\t}\n+\n+\tif (rlwit_word_size(&rlw_i) > 0)\n+\t\trlwit_discharge(&rlw_i, out, ~0, 0);\n+\telse\n+\t\trlwit_discharge(&rlw_j, out, ~0, 0);\n+\n+\tout->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);\n+}\n+\n+void ewah_and(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out)\n+{\n+\tstruct rlw_iterator rlw_i;\n+\tstruct rlw_iterator rlw_j;\n+\tsize_t literals;\n+\n+\trlwit_init(&rlw_i, ewah_i);\n+\trlwit_init(&rlw_j, ewah_j);\n+\n+\twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n+\t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n+\t\t\tstruct rlw_iterator *prey, *predator;\n+\n+\t\t\tif (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {\n+\t\t\t\tprey = &rlw_i;\n+\t\t\t\tpredator = &rlw_j;\n+\t\t\t} else {\n+\t\t\t\tprey = &rlw_j;\n+\t\t\t\tpredator = &rlw_i;\n+\t\t\t}\n+\n+\t\t\tif (predator->rlw.running_bit == 0) {\n+\t\t\t\tewah_add_empty_words(out, 0,\n+\t\t\t\t\tpredator->rlw.running_len);\n+\t\t\t\trlwit_discard_first_words(prey,\n+\t\t\t\t\tpredator->rlw.running_len);\n+\t\t\t\trlwit_discard_first_words(predator,\n+\t\t\t\t\tpredator->rlw.running_len);\n+\t\t\t} else {\n+\t\t\t\tsize_t index = rlwit_discharge(prey, out,\n+\t\t\t\t\tpredator->rlw.running_len, 0);\n+\t\t\t\tewah_add_empty_words(out, 0,\n+\t\t\t\t\tpredator->rlw.running_len - index);\n+\t\t\t\trlwit_discard_first_words(predator,\n+\t\t\t\t\tpredator->rlw.running_len);\n+\t\t\t}\n+\t\t}\n+\n+\t\tliterals = min_size(\n+\t\t\trlw_i.rlw.literal_words,\n+\t\t\trlw_j.rlw.literal_words);\n+\n+\t\tif (literals) {\n+\t\t\tsize_t k;\n+\n+\t\t\tfor (k = 0; k < literals; ++k) {\n+\t\t\t\tewah_add(out,\n+\t\t\t\t\trlw_i.buffer[rlw_i.literal_word_start + k] &\n+\t\t\t\t\trlw_j.buffer[rlw_j.literal_word_start + k]\n+\t\t\t\t);\n+\t\t\t}\n+\n+\t\t\trlwit_discard_first_words(&rlw_i, literals);\n+\t\t\trlwit_discard_first_words(&rlw_j, literals);\n+\t\t}\n+\t}\n+\n+\tif (rlwit_word_size(&rlw_i) > 0)\n+\t\trlwit_discharge_empty(&rlw_i, out);\n+\telse\n+\t\trlwit_discharge_empty(&rlw_j, out);\n+\n+\tout->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);\n+}\n+\n+void ewah_and_not(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out)\n+{\n+\tstruct rlw_iterator rlw_i;\n+\tstruct rlw_iterator rlw_j;\n+\tsize_t literals;\n+\n+\trlwit_init(&rlw_i, ewah_i);\n+\trlwit_init(&rlw_j, ewah_j);\n+\n+\twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n+\t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n+\t\t\tstruct rlw_iterator *prey, *predator;\n+\n+\t\t\tif (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {\n+\t\t\t\tprey = &rlw_i;\n+\t\t\t\tpredator = &rlw_j;\n+\t\t\t} else {\n+\t\t\t\tprey = &rlw_j;\n+\t\t\t\tpredator = &rlw_i;\n+\t\t\t}\n+\n+\t\t\tif ((predator->rlw.running_bit && prey == &rlw_i) ||\n+\t\t\t\t(!predator->rlw.running_bit && prey != &rlw_i)) {\n+\t\t\t\tewah_add_empty_words(out, 0,\n+\t\t\t\t\tpredator->rlw.running_len);\n+\t\t\t\trlwit_discard_first_words(prey,\n+\t\t\t\t\tpredator->rlw.running_len);\n+\t\t\t\trlwit_discard_first_words(predator,\n+\t\t\t\t\tpredator->rlw.running_len);\n+\t\t\t} else {\n+\t\t\t\tsize_t index;\n+\t\t\t\tint negate_words;\n+\n+\t\t\t\tnegate_words = (&rlw_i != prey);\n+\t\t\t\tindex = rlwit_discharge(prey, out,\n+\t\t\t\t\tpredator->rlw.running_len, negate_words);\n+\t\t\t\tewah_add_empty_words(out, negate_words,\n+\t\t\t\t\tpredator->rlw.running_len - index);\n+\t\t\t\trlwit_discard_first_words(predator,\n+\t\t\t\t\tpredator->rlw.running_len);\n+\t\t\t}\n+\t\t}\n+\n+\t\tliterals = min_size(\n+\t\t\trlw_i.rlw.literal_words,\n+\t\t\trlw_j.rlw.literal_words);\n+\n+\t\tif (literals) {\n+\t\t\tsize_t k;\n+\n+\t\t\tfor (k = 0; k < literals; ++k) {\n+\t\t\t\tewah_add(out,\n+\t\t\t\t\trlw_i.buffer[rlw_i.literal_word_start + k] &\n+\t\t\t\t\t~(rlw_j.buffer[rlw_j.literal_word_start + k])\n+\t\t\t\t);\n+\t\t\t}\n+\n+\t\t\trlwit_discard_first_words(&rlw_i, literals);\n+\t\t\trlwit_discard_first_words(&rlw_j, literals);\n+\t\t}\n+\t}\n+\n+\tif (rlwit_word_size(&rlw_i) > 0)\n+\t\trlwit_discharge(&rlw_i, out, ~0, 0);\n+\telse\n+\t\trlwit_discharge_empty(&rlw_j, out);\n+\n+\tout->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);\n+}\n+\n+void ewah_or(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out)\n+{\n+\tstruct rlw_iterator rlw_i;\n+\tstruct rlw_iterator rlw_j;\n+\tsize_t literals;\n+\n+\trlwit_init(&rlw_i, ewah_i);\n+\trlwit_init(&rlw_j, ewah_j);\n+\n+\twhile (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {\n+\t\twhile (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {\n+\t\t\tstruct rlw_iterator *prey, *predator;\n+\n+\t\t\tif (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {\n+\t\t\t\tprey = &rlw_i;\n+\t\t\t\tpredator = &rlw_j;\n+\t\t\t} else {\n+\t\t\t\tprey = &rlw_j;\n+\t\t\t\tpredator = &rlw_i;\n+\t\t\t}\n+\n+\t\t\tif (predator->rlw.running_bit) {\n+\t\t\t\tewah_add_empty_words(out, 0,\n+\t\t\t\t\tpredator->rlw.running_len);\n+\t\t\t\trlwit_discard_first_words(prey,\n+\t\t\t\t\tpredator->rlw.running_len);\n+\t\t\t\trlwit_discard_first_words(predator,\n+\t\t\t\t\tpredator->rlw.running_len);\n+\t\t\t} else {\n+\t\t\t\tsize_t index = rlwit_discharge(prey, out,\n+\t\t\t\t\tpredator->rlw.running_len, 0);\n+\t\t\t\tewah_add_empty_words(out, 0,\n+\t\t\t\t\tpredator->rlw.running_len - index);\n+\t\t\t\trlwit_discard_first_words(predator,\n+\t\t\t\t\tpredator->rlw.running_len);\n+\t\t\t}\n+\t\t}\n+\n+\t\tliterals = min_size(\n+\t\t\trlw_i.rlw.literal_words,\n+\t\t\trlw_j.rlw.literal_words);\n+\n+\t\tif (literals) {\n+\t\t\tsize_t k;\n+\n+\t\t\tfor (k = 0; k < literals; ++k) {\n+\t\t\t\tewah_add(out,\n+\t\t\t\t\trlw_i.buffer[rlw_i.literal_word_start + k] |\n+\t\t\t\t\trlw_j.buffer[rlw_j.literal_word_start + k]\n+\t\t\t\t);\n+\t\t\t}\n+\n+\t\t\trlwit_discard_first_words(&rlw_i, literals);\n+\t\t\trlwit_discard_first_words(&rlw_j, literals);\n+\t\t}\n+\t}\n+\n+\tif (rlwit_word_size(&rlw_i) > 0)\n+\t\trlwit_discharge(&rlw_i, out, ~0, 0);\n+\telse\n+\t\trlwit_discharge(&rlw_j, out, ~0, 0);\n+\n+\tout->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);\n+}\n+\n+\n+#define BITMAP_POOL_MAX 16\n+static struct ewah_bitmap *bitmap_pool[BITMAP_POOL_MAX];\n+static size_t bitmap_pool_size;\n+\n+struct ewah_bitmap *ewah_pool_new(void)\n+{\n+\tif (bitmap_pool_size)\n+\t\treturn bitmap_pool[--bitmap_pool_size];\n+\n+\treturn ewah_new();\n+}\n+\n+void ewah_pool_free(struct ewah_bitmap *self)\n+{\n+\tif (self == NULL)\n+\t\treturn;\n+\n+\tif (bitmap_pool_size == BITMAP_POOL_MAX ||\n+\t\tself->alloc_size == 0) {\n+\t\tewah_free(self);\n+\t\treturn;\n+\t}\n+\n+\tewah_clear(self);\n+\tbitmap_pool[bitmap_pool_size++] = self;\n+}\n+\n+uint32_t ewah_checksum(struct ewah_bitmap *self)\n+{\n+\tconst uint8_t *p = (uint8_t *)self->buffer;\n+\tuint32_t crc = (uint32_t)self->bit_size;\n+\tsize_t size = self->buffer_size * sizeof(eword_t);\n+\n+\twhile (size--)\n+\t\tcrc = (crc << 5) - crc + (uint32_t)*p++;\n+\n+\treturn crc;\n+}\ndiff --git a/ewah/ewah_io.c b/ewah/ewah_io.c\nnew file mode 100644\nindex 0000000..fca93f9\n--- /dev/null\n+++ b/ewah/ewah_io.c\n@@ -0,0 +1,197 @@\n+/**\n+ * Copyright 2013, GitHub, Inc\n+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n+ *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; either version 2\n+ * of the License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+ */\n+#include <stdlib.h>\n+#include <unistd.h>\n+#include <stdio.h>\n+\n+#include \"git-compat-util.h\"\n+#include \"ewok.h\"\n+\n+int ewah_serialize_native(struct ewah_bitmap *self, int fd)\n+{\n+\tuint32_t write32;\n+\tsize_t to_write = self->buffer_size * 8;\n+\n+\t/* 32 bit -- bit size fr the map */\n+\twrite32 = (uint32_t)self->bit_size;\n+\tif (write(fd, &write32, 4) != 4)\n+\t\treturn -1;\n+\n+\t/** 32 bit -- number of compressed 64-bit words */\n+\twrite32 = (uint32_t)self->buffer_size;\n+\tif (write(fd, &write32, 4) != 4)\n+\t\treturn -1;\n+\n+\tif (write(fd, self->buffer, to_write) != to_write)\n+\t\treturn -1;\n+\n+\t/** 32 bit -- position for the RLW */\n+\twrite32 = self->rlw - self->buffer;\n+\tif (write(fd, &write32, 4) != 4)\n+\t\treturn -1;\n+\n+\treturn (3 * 4) + to_write;\n+}\n+\n+int ewah_serialize_to(struct ewah_bitmap *self,\n+\t\t      int (*write_fun)(void *, const void *, size_t),\n+\t\t      void *data)\n+{\n+\tsize_t i;\n+\teword_t dump[2048];\n+\tconst size_t words_per_dump = sizeof(dump) / sizeof(eword_t);\n+\tuint32_t bitsize, word_count, rlw_pos;\n+\n+\tconst eword_t *buffer;\n+\tsize_t words_left;\n+\n+\t/* 32 bit -- bit size fr the map */\n+\tbitsize =  htonl((uint32_t)self->bit_size);\n+\tif (write_fun(data, &bitsize, 4) != 4)\n+\t\treturn -1;\n+\n+\t/** 32 bit -- number of compressed 64-bit words */\n+\tword_count =  htonl((uint32_t)self->buffer_size);\n+\tif (write_fun(data, &word_count, 4) != 4)\n+\t\treturn -1;\n+\n+\t/** 64 bit x N -- compressed words */\n+\tbuffer = self->buffer;\n+\twords_left = self->buffer_size;\n+\n+\twhile (words_left >= words_per_dump) {\n+\t\tfor (i = 0; i < words_per_dump; ++i, ++buffer)\n+\t\t\tdump[i] = htonll(*buffer);\n+\n+\t\tif (write_fun(data, dump, sizeof(dump)) != sizeof(dump))\n+\t\t\treturn -1;\n+\n+\t\twords_left -= words_per_dump;\n+\t}\n+\n+\tif (words_left) {\n+\t\tfor (i = 0; i < words_left; ++i, ++buffer)\n+\t\t\tdump[i] = htonll(*buffer);\n+\n+\t\tif (write_fun(data, dump, words_left * 8) != words_left * 8)\n+\t\t\treturn -1;\n+\t}\n+\n+\t/** 32 bit -- position for the RLW */\n+\trlw_pos = (uint8_t*)self->rlw - (uint8_t *)self->buffer;\n+\trlw_pos = htonl(rlw_pos / sizeof(eword_t));\n+\n+\tif (write_fun(data, &rlw_pos, 4) != 4)\n+\t\treturn -1;\n+\n+\treturn (3 * 4) + (self->buffer_size * 8);\n+}\n+\n+static int write_helper(void *fd, const void *buf, size_t len)\n+{\n+\treturn write((intptr_t)fd, buf, len);\n+}\n+\n+int ewah_serialize(struct ewah_bitmap *self, int fd)\n+{\n+\treturn ewah_serialize_to(self, write_helper, (void *)(intptr_t)fd);\n+}\n+\n+int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len)\n+{\n+\tuint32_t *read32 = map;\n+\teword_t *read64;\n+\tsize_t i;\n+\n+\tself->bit_size = ntohl(*read32++);\n+\tself->buffer_size = self->alloc_size = ntohl(*read32++);\n+\tself->buffer = ewah_realloc(self->buffer,\n+\t\tself->alloc_size * sizeof(eword_t));\n+\n+\tif (!self->buffer)\n+\t\treturn -1;\n+\n+\tfor (i = 0, read64 = (void *)read32; i < self->buffer_size; ++i)\n+\t\tself->buffer[i] = ntohll(*read64++);\n+\n+\tread32 = (void *)read64;\n+\tself->rlw = self->buffer + ntohl(*read32++);\n+\n+\treturn (3 * 4) + (self->buffer_size * 8);\n+}\n+\n+int ewah_deserialize(struct ewah_bitmap *self, int fd)\n+{\n+\tsize_t i;\n+\teword_t dump[2048];\n+\tconst size_t words_per_dump = sizeof(dump) / sizeof(eword_t);\n+\tuint32_t bitsize, word_count, rlw_pos;\n+\n+\teword_t *buffer = NULL;\n+\tsize_t words_left;\n+\n+\tewah_clear(self);\n+\n+\t/* 32 bit -- bit size fr the map */\n+\tif (read(fd, &bitsize, 4) != 4)\n+\t\treturn -1;\n+\n+\tself->bit_size = (size_t)ntohl(bitsize);\n+\n+\t/** 32 bit -- number of compressed 64-bit words */\n+\tif (read(fd, &word_count, 4) != 4)\n+\t\treturn -1;\n+\n+\tself->buffer_size = self->alloc_size = (size_t)ntohl(word_count);\n+\tself->buffer = ewah_realloc(self->buffer,\n+\t\tself->alloc_size * sizeof(eword_t));\n+\n+\tif (!self->buffer)\n+\t\treturn -1;\n+\n+\t/** 64 bit x N -- compressed words */\n+\tbuffer = self->buffer;\n+\twords_left = self->buffer_size;\n+\n+\twhile (words_left >= words_per_dump) {\n+\t\tif (read(fd, dump, sizeof(dump)) != sizeof(dump))\n+\t\t\treturn -1;\n+\n+\t\tfor (i = 0; i < words_per_dump; ++i, ++buffer)\n+\t\t\t*buffer = ntohll(dump[i]);\n+\n+\t\twords_left -= words_per_dump;\n+\t}\n+\n+\tif (words_left) {\n+\t\tif (read(fd, dump, words_left * 8) != words_left * 8)\n+\t\t\treturn -1;\n+\n+\t\tfor (i = 0; i < words_left; ++i, ++buffer)\n+\t\t\t*buffer = ntohll(dump[i]);\n+\t}\n+\n+\t/** 32 bit -- position for the RLW */\n+\tif (read(fd, &rlw_pos, 4) != 4)\n+\t\treturn -1;\n+\n+\tself->rlw = self->buffer + ntohl(rlw_pos);\n+\treturn 0;\n+}\ndiff --git a/ewah/ewah_rlw.c b/ewah/ewah_rlw.c\nnew file mode 100644\nindex 0000000..48eb043\n--- /dev/null\n+++ b/ewah/ewah_rlw.c\n@@ -0,0 +1,119 @@\n+/**\n+ * Copyright 2013, GitHub, Inc\n+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n+ *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; either version 2\n+ * of the License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+ */\n+#include <assert.h>\n+#include <stdlib.h>\n+#include <unistd.h>\n+#include <string.h>\n+\n+#include \"ewok.h\"\n+#include \"ewok_rlw.h\"\n+\n+static inline int next_word(struct rlw_iterator *it)\n+{\n+\tif (it->pointer >= it->size)\n+\t\treturn 0;\n+\n+\tit->rlw.word = &it->buffer[it->pointer];\n+\tit->pointer += rlw_get_literal_words(it->rlw.word) + 1;\n+\n+\tit->rlw.literal_words = rlw_get_literal_words(it->rlw.word);\n+\tit->rlw.running_len = rlw_get_running_len(it->rlw.word);\n+\tit->rlw.running_bit = rlw_get_run_bit(it->rlw.word);\n+\tit->rlw.literal_word_offset = 0;\n+\n+\treturn 1;\n+}\n+\n+void rlwit_init(struct rlw_iterator *it, struct ewah_bitmap *from_ewah)\n+{\n+\tit->buffer = from_ewah->buffer;\n+\tit->size = from_ewah->buffer_size;\n+\tit->pointer = 0;\n+\n+\tnext_word(it);\n+\n+\tit->literal_word_start = rlwit_literal_words(it) +\n+\t\tit->rlw.literal_word_offset;\n+}\n+\n+void rlwit_discard_first_words(struct rlw_iterator *it, size_t x)\n+{\n+\twhile (x > 0) {\n+\t\tsize_t discard;\n+\n+\t\tif (it->rlw.running_len > x) {\n+\t\t\tit->rlw.running_len -= x;\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tx -= it->rlw.running_len;\n+\t\tit->rlw.running_len = 0;\n+\n+\t\tdiscard = (x > it->rlw.literal_words) ? it->rlw.literal_words : x;\n+\n+\t\tit->literal_word_start += discard;\n+\t\tit->rlw.literal_words -= discard;\n+\t\tx -= discard;\n+\n+\t\tif (x > 0 || rlwit_word_size(it) == 0) {\n+\t\t\tif (!next_word(it))\n+\t\t\t\tbreak;\n+\n+\t\t\tit->literal_word_start =\n+\t\t\t\trlwit_literal_words(it) + it->rlw.literal_word_offset;\n+\t\t}\n+\t}\n+}\n+\n+size_t rlwit_discharge(\n+\tstruct rlw_iterator *it, struct ewah_bitmap *out, size_t max, int negate)\n+{\n+\tsize_t index = 0;\n+\n+\twhile (index < max && rlwit_word_size(it) > 0) {\n+\t\tsize_t pd, pl = it->rlw.running_len;\n+\n+\t\tif (index + pl > max)\n+\t\t\tpl = max - index;\n+\n+\t\tewah_add_empty_words(out, it->rlw.running_bit ^ negate, pl);\n+\t\tindex += pl;\n+\n+\t\tpd = it->rlw.literal_words;\n+\t\tif (pd + index > max)\n+\t\t\tpd = max - index;\n+\n+\t\tewah_add_dirty_words(out,\n+\t\t\tit->buffer + it->literal_word_start, pd, negate);\n+\n+\t\trlwit_discard_first_words(it, pd + pl);\n+\t\tindex += pd;\n+\t}\n+\n+\treturn index;\n+}\n+\n+void rlwit_discharge_empty(struct rlw_iterator *it, struct ewah_bitmap *out)\n+{\n+\twhile (rlwit_word_size(it) > 0) {\n+\t\tewah_add_empty_words(out, 0, rlwit_word_size(it));\n+\t\trlwit_discard_first_words(it, rlwit_word_size(it));\n+\t}\n+}\ndiff --git a/ewah/ewok.h b/ewah/ewok.h\nnew file mode 100644\nindex 0000000..3db7703\n--- /dev/null\n+++ b/ewah/ewok.h\n@@ -0,0 +1,223 @@\n+/**\n+ * Copyright 2013, GitHub, Inc\n+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n+ *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; either version 2\n+ * of the License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+ */\n+#ifndef __EWOK_BITMAP_C__\n+#define __EWOK_BITMAP_C__\n+\n+#include <stdint.h>\n+\n+#ifndef ewah_malloc\n+#\tdefine ewah_malloc malloc\n+#endif\n+#ifndef ewah_realloc\n+#\tdefine ewah_realloc realloc\n+#endif\n+#ifndef ewah_calloc\n+#\tdefine ewah_calloc calloc\n+#endif\n+\n+typedef uint64_t eword_t;\n+#define BITS_IN_WORD (sizeof(eword_t) * 8)\n+\n+/**\n+ * Do not use __builtin_popcountll. The GCC implementation\n+ * is notoriously slow on all platforms.\n+ *\n+ * See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36041\n+ */\n+static inline uint32_t ewah_bit_popcount64(uint64_t x)\n+{\n+\tx = (x & 0x5555555555555555ULL) + ((x >>  1) & 0x5555555555555555ULL);\n+\tx = (x & 0x3333333333333333ULL) + ((x >>  2) & 0x3333333333333333ULL);\n+\tx = (x & 0x0F0F0F0F0F0F0F0FULL) + ((x >>  4) & 0x0F0F0F0F0F0F0F0FULL);\n+\treturn (x * 0x0101010101010101ULL) >> 56;\n+}\n+\n+#define ewah_bit_ctz64(x) __builtin_ctzll(x)\n+\n+struct ewah_bitmap {\n+\teword_t *buffer;\n+\tsize_t buffer_size;\n+\tsize_t alloc_size;\n+\tsize_t bit_size;\n+\teword_t *rlw;\n+};\n+\n+typedef void (*ewah_callback)(size_t pos, void *);\n+\n+struct ewah_bitmap *ewah_pool_new(void);\n+void ewah_pool_free(struct ewah_bitmap *self);\n+\n+/**\n+ * Allocate a new EWAH Compressed bitmap\n+ */\n+struct ewah_bitmap *ewah_new(void);\n+\n+/**\n+ * Clear all the bits in the bitmap. Does not free or resize\n+ * memory.\n+ */\n+void ewah_clear(struct ewah_bitmap *self);\n+\n+/**\n+ * Free all the memory of the bitmap\n+ */\n+void ewah_free(struct ewah_bitmap *self);\n+\n+int ewah_serialize_to(struct ewah_bitmap *self,\n+\t\t      int (*write_fun)(void *out, const void *buf, size_t len),\n+\t\t      void *out);\n+int ewah_serialize(struct ewah_bitmap *self, int fd);\n+int ewah_serialize_native(struct ewah_bitmap *self, int fd);\n+\n+int ewah_deserialize(struct ewah_bitmap *self, int fd);\n+int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len);\n+int ewah_read_mmap_native(struct ewah_bitmap *self, void *map, size_t len);\n+\n+uint32_t ewah_checksum(struct ewah_bitmap *self);\n+\n+/**\n+ * Logical not (bitwise negation) in-place on the bitmap\n+ *\n+ * This operation is linear time based on the size of the bitmap.\n+ */\n+void ewah_not(struct ewah_bitmap *self);\n+\n+/**\n+ * Call the given callback with the position of every single bit\n+ * that has been set on the bitmap.\n+ *\n+ * This is an efficient operation that does not fully decompress\n+ * the bitmap.\n+ */\n+void ewah_each_bit(struct ewah_bitmap *self, ewah_callback callback, void *payload);\n+\n+/**\n+ * Set a given bit on the bitmap.\n+ *\n+ * The bit at position `pos` will be set to true. Because of the\n+ * way that the bitmap is compressed, a set bit cannot be unset\n+ * later on.\n+ *\n+ * Furthermore, since the bitmap uses streaming compression, bits\n+ * can only set incrementally.\n+ *\n+ * E.g.\n+ *\t\tewah_set(bitmap, 1); // ok\n+ *\t\tewah_set(bitmap, 76); // ok\n+ *\t\tewah_set(bitmap, 77); // ok\n+ *\t\tewah_set(bitmap, 8712800127); // ok\n+ *\t\tewah_set(bitmap, 25); // failed, assert raised\n+ */\n+void ewah_set(struct ewah_bitmap *self, size_t i);\n+\n+struct ewah_iterator {\n+\tconst eword_t *buffer;\n+\tsize_t buffer_size;\n+\n+\tsize_t pointer;\n+\teword_t compressed, literals;\n+\teword_t rl, lw;\n+\tint b;\n+};\n+\n+/**\n+ * Initialize a new iterator to run through the bitmap in uncompressed form.\n+ *\n+ * The iterator can be stack allocated. The underlying bitmap must not be freed\n+ * before the iteration is over.\n+ *\n+ * E.g.\n+ *\n+ *\t\tstruct ewah_bitmap *bitmap = ewah_new();\n+ *\t\tstruct ewah_iterator it;\n+ *\n+ *\t\tewah_iterator_init(&it, bitmap);\n+ */\n+void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent);\n+\n+/**\n+ * Yield every single word in the bitmap in uncompressed form. This is:\n+ * yield single words (32-64 bits) where each bit represents an actual\n+ * bit from the bitmap.\n+ *\n+ * Return: true if a word was yield, false if there are no words left\n+ */\n+int ewah_iterator_next(eword_t *next, struct ewah_iterator *it);\n+\n+void ewah_or(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out);\n+\n+void ewah_and_not(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out);\n+\n+void ewah_xor(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out);\n+\n+void ewah_and(\n+\tstruct ewah_bitmap *ewah_i,\n+\tstruct ewah_bitmap *ewah_j,\n+\tstruct ewah_bitmap *out);\n+\n+void ewah_dump(struct ewah_bitmap *self);\n+\n+/**\n+ * Direct word access\n+ */\n+size_t ewah_add_empty_words(struct ewah_bitmap *self, int v, size_t number);\n+void ewah_add_dirty_words(\n+\tstruct ewah_bitmap *self, const eword_t *buffer, size_t number, int negate);\n+size_t ewah_add(struct ewah_bitmap *self, eword_t word);\n+\n+\n+/**\n+ * Uncompressed, old-school bitmap that can be efficiently compressed\n+ * into an `ewah_bitmap`.\n+ */\n+struct bitmap {\n+\teword_t *words;\n+\tsize_t word_alloc;\n+};\n+\n+struct bitmap *bitmap_new(void);\n+void bitmap_set(struct bitmap *self, size_t pos);\n+void bitmap_clear(struct bitmap *self, size_t pos);\n+int bitmap_get(struct bitmap *self, size_t pos);\n+void bitmap_reset(struct bitmap *self);\n+void bitmap_free(struct bitmap *self);\n+int bitmap_equals(struct bitmap *self, struct bitmap *other);\n+int bitmap_is_subset(struct bitmap *self, struct bitmap *super);\n+\n+struct ewah_bitmap * bitmap_to_ewah(struct bitmap *bitmap);\n+struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah);\n+\n+void bitmap_and_not(struct bitmap *self, struct bitmap *other);\n+void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other);\n+void bitmap_or(struct bitmap *self, const struct bitmap *other);\n+\n+void bitmap_each_bit(struct bitmap *self, ewah_callback callback, void *data);\n+size_t bitmap_popcount(struct bitmap *self);\n+\n+#endif\ndiff --git a/ewah/ewok_rlw.h b/ewah/ewok_rlw.h\nnew file mode 100644\nindex 0000000..63efdf9\n--- /dev/null\n+++ b/ewah/ewok_rlw.h\n@@ -0,0 +1,114 @@\n+/**\n+ * Copyright 2013, GitHub, Inc\n+ * Copyright 2009-2013, Daniel Lemire, Cliff Moon,\n+ *\tDavid McIntosh, Robert Becho, Google Inc. and Veronika Zenz\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; either version 2\n+ * of the License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+ */\n+#ifndef __EWOK_RLW_H__\n+#define __EWOK_RLW_H__\n+\n+#define RLW_RUNNING_BITS (sizeof(eword_t) * 4)\n+#define RLW_LITERAL_BITS (sizeof(eword_t) * 8 - 1 - RLW_RUNNING_BITS)\n+\n+#define RLW_LARGEST_RUNNING_COUNT (((eword_t)1 << RLW_RUNNING_BITS) - 1)\n+#define RLW_LARGEST_LITERAL_COUNT (((eword_t)1 << RLW_LITERAL_BITS) - 1)\n+\n+#define RLW_LARGEST_RUNNING_COUNT_SHIFT (RLW_LARGEST_RUNNING_COUNT << 1)\n+\n+#define RLW_RUNNING_LEN_PLUS_BIT (((eword_t)1 << (RLW_RUNNING_BITS + 1)) - 1)\n+\n+static int rlw_get_run_bit(const eword_t *word)\n+{\n+\treturn *word & (eword_t)1;\n+}\n+\n+static inline void rlw_set_run_bit(eword_t *word, int b)\n+{\n+\tif (b) {\n+\t\t*word |= (eword_t)1;\n+\t} else {\n+\t\t*word &= (eword_t)(~1);\n+\t}\n+}\n+\n+static inline void rlw_xor_run_bit(eword_t *word)\n+{\n+\tif (*word & 1) {\n+\t\t*word &= (eword_t)(~1);\n+\t} else {\n+\t\t*word |= (eword_t)1;\n+\t}\n+}\n+\n+static inline void rlw_set_running_len(eword_t *word, eword_t l)\n+{\n+\t*word |= RLW_LARGEST_RUNNING_COUNT_SHIFT;\n+\t*word &= (l << 1) | (~RLW_LARGEST_RUNNING_COUNT_SHIFT);\n+}\n+\n+static inline eword_t rlw_get_running_len(const eword_t *word)\n+{\n+\treturn (*word >> 1) & RLW_LARGEST_RUNNING_COUNT;\n+}\n+\n+static inline eword_t rlw_get_literal_words(const eword_t *word)\n+{\n+\treturn *word >> (1 + RLW_RUNNING_BITS);\n+}\n+\n+static inline void rlw_set_literal_words(eword_t *word, eword_t l)\n+{\n+\t*word |= ~RLW_RUNNING_LEN_PLUS_BIT;\n+\t*word &= (l << (RLW_RUNNING_BITS + 1)) | RLW_RUNNING_LEN_PLUS_BIT;\n+}\n+\n+static inline eword_t rlw_size(const eword_t *self)\n+{\n+\treturn rlw_get_running_len(self) + rlw_get_literal_words(self);\n+}\n+\n+struct rlw_iterator {\n+\tconst eword_t *buffer;\n+\tsize_t size;\n+\tsize_t pointer;\n+\tsize_t literal_word_start;\n+\n+\tstruct {\n+\t\tconst eword_t *word;\n+\t\tint literal_words;\n+\t\tint running_len;\n+\t\tint literal_word_offset;\n+\t\tint running_bit;\n+\t} rlw;\n+};\n+\n+void rlwit_init(struct rlw_iterator *it, struct ewah_bitmap *bitmap);\n+void rlwit_discard_first_words(struct rlw_iterator *it, size_t x);\n+size_t rlwit_discharge(\n+\tstruct rlw_iterator *it, struct ewah_bitmap *out, size_t max, int negate);\n+void rlwit_discharge_empty(struct rlw_iterator *it, struct ewah_bitmap *out);\n+\n+static inline size_t rlwit_word_size(struct rlw_iterator *it)\n+{\n+\treturn it->rlw.running_len + it->rlw.literal_words;\n+}\n+\n+static inline size_t rlwit_literal_words(struct rlw_iterator *it)\n+{\n+\treturn it->pointer - it->rlw.literal_words;\n+}\n+\n+#endif\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229484","messageId":"20131025060340.GG23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 09/19] documentation: add documentation for the bitmap format","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:03:41Z","receivedAt":"2013-10-25T06:03:41Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThis is the technical documentation for the JGit-compatible Bitmap v1\non-disk format.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/technical/bitmap-format.txt | 131 ++++++++++++++++++++++++++++++\n 1 file changed, 131 insertions(+)\n create mode 100644 Documentation/technical/bitmap-format.txt\n\ndiff --git a/Documentation/technical/bitmap-format.txt b/Documentation/technical/bitmap-format.txt\nnew file mode 100644\nindex 0000000..7a86bd7\n--- /dev/null\n+++ b/Documentation/technical/bitmap-format.txt\n@@ -0,0 +1,131 @@\n+GIT bitmap v1 format\n+====================\n+\n+\t- A header appears at the beginning:\n+\n+\t\t4-byte signature: {'B', 'I', 'T', 'M'}\n+\n+\t\t2-byte version number (network byte order)\n+\t\t\tThe current implementation only supports version 1\n+\t\t\tof the bitmap index (the same one as JGit).\n+\n+\t\t2-byte flags (network byte order)\n+\n+\t\t\tThe following flags are supported:\n+\n+\t\t\t- BITMAP_OPT_FULL_DAG (0x1) REQUIRED\n+\t\t\tThis flag must always be present. It implies that the bitmap\n+\t\t\tindex has been generated for a packfile with full closure\n+\t\t\t(i.e. where every single object in the packfile can find\n+\t\t\t its parent links inside the same packfile). This is a\n+\t\t\trequirement for the bitmap index format, also present in JGit,\n+\t\t\tthat greatly reduces the complexity of the implementation.\n+\n+\t\t4-byte entry count (network byte order)\n+\n+\t\t\tThe total count of entries (bitmapped commits) in this bitmap index.\n+\n+\t\t20-byte checksum\n+\n+\t\t\tThe SHA1 checksum of the pack this bitmap index belongs to.\n+\n+\t- 4 EWAH bitmaps that act as type indexes\n+\n+\t\tType indexes are serialized after the hash cache in the shape\n+\t\tof four EWAH bitmaps stored consecutively (see Appendix A for\n+\t\tthe serialization format of an EWAH bitmap).\n+\n+\t\tThere is a bitmap for each Git object type, stored in the following\n+\t\torder:\n+\n+\t\t\t- Commits\n+\t\t\t- Trees\n+\t\t\t- Blobs\n+\t\t\t- Tags\n+\n+\t\tIn each bitmap, the `n`th bit is set to true if the `n`th object\n+\t\tin the packfile is of that type.\n+\n+\t\tThe obvious consequence is that the OR of all 4 bitmaps will result\n+\t\tin a full set (all bits set), and the AND of all 4 bitmaps will\n+\t\tresult in an empty bitmap (no bits set).\n+\n+\t- N entries with compressed bitmaps, one for each indexed commit\n+\n+\t\tWhere `N` is the total amount of entries in this bitmap index.\n+\t\tEach entry contains the following:\n+\n+\t\t- 4-byte object position (network byte order)\n+\t\t\tThe position **in the index for the packfile** where the\n+\t\t\tbitmap for this commit is found.\n+\n+\t\t- 1-byte XOR-offset\n+\t\t\tThe xor offset used to compress this bitmap. For an entry\n+\t\t\tin position `x`, a XOR offset of `y` means that the actual\n+\t\t\tbitmap representing this commit is composed by XORing the\n+\t\t\tbitmap for this entry with the bitmap in entry `x-y` (i.e.\n+\t\t\tthe bitmap `y` entries before this one).\n+\n+\t\t\tNote that this compression can be recursive. In order to\n+\t\t\tXOR this entry with a previous one, the previous entry needs\n+\t\t\tto be decompressed first, and so on.\n+\n+\t\t\tThe hard-limit for this offset is 160 (an entry can only be\n+\t\t\txor'ed against one of the 160 entries preceding it). This\n+\t\t\tnumber is always positive, and hence entries are always xor'ed\n+\t\t\twith **previous** bitmaps, not bitmaps that will come afterwards\n+\t\t\tin the index.\n+\n+\t\t- 1-byte flags for this bitmap\n+\t\t\tAt the moment the only available flag is `0x1`, which hints\n+\t\t\tthat this bitmap can be re-used when rebuilding bitmap indexes\n+\t\t\tfor the repository.\n+\n+\t\t- The compressed bitmap itself, see Appendix A.\n+\n+== Appendix A: Serialization format for an EWAH bitmap\n+\n+Ewah bitmaps are serialized in the same protocol as the JAVAEWAH\n+library, making them backwards compatible with the JGit\n+implementation:\n+\n+\t- 4-byte number of bits of the resulting UNCOMPRESSED bitmap\n+\n+\t- 4-byte number of words of the COMPRESSED bitmap, when stored\n+\n+\t- N x 8-byte words, as specified by the previous field\n+\n+\t\tThis is the actual content of the compressed bitmap.\n+\n+\t- 4-byte position of the current RLW for the compressed\n+\t\tbitmap\n+\n+All words are stored in network byte order for their corresponding\n+sizes.\n+\n+The compressed bitmap is stored in a form of run-length encoding, as\n+follows.  It consists of a concatenation of an arbitrary number of\n+chunks.  Each chunk consists of one or more 64-bit words\n+\n+     H  L_1  L_2  L_3 .... L_M\n+\n+H is called RLW (run length word).  It consists of (from lower to higher\n+order bits):\n+\n+     - 1 bit: the repeated bit B\n+\n+     - 32 bits: repetition count K (unsigned)\n+\n+     - 31 bits: literal word count M (unsigned)\n+\n+The bitstream represented by the above chunk is then:\n+\n+     - K repetitions of B\n+\n+     - The bits stored in `L_1` through `L_M`.  Within a word, bits at\n+       lower order come earlier in the stream than those at higher\n+       order.\n+\n+The next word after `L_M` (if any) must again be a RLW, for the next\n+chunk.  For efficient appending to the bitstream, the EWAH stores a\n+pointer to the last RLW in the stream.\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229485","messageId":"20131025060345.GH23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:03:45Z","receivedAt":"2013-10-25T06:03:45Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nA bitmap index is a `.bitmap` file that can be found inside\n`$GIT_DIR/objects/pack/`, next to its corresponding packfile, and\ncontains precalculated reachability information for selected commits.\nThe full specification of the format for these bitmap indexes can be found\nin `Documentation/technical/bitmap-format.txt`.\n\nFor a given commit SHA1, if it happens to be available in the bitmap\nindex, its bitmap will represent every single object that is reachable\nfrom the commit itself. The nth bit in the bitmap is the nth object in\nthe packfile; if it's set to 1, the object is reachable.\n\nBy using the bitmaps available in the index, this commit implements\nseveral new functions:\n\n\t- `prepare_bitmap_git`\n\t- `prepare_bitmap_walk`\n\t- `traverse_bitmap_commit_list`\n\t- `reuse_partial_packfile_from_bitmap`\n\nThe `prepare_bitmap_walk` function tries to build a bitmap of all the\nobjects that can be reached from the commit roots of a given `rev_info`\nstruct by using the following algorithm:\n\n- If all the interesting commits for a revision walk are available in\nthe index, the resulting reachability bitmap is the bitwise OR of all\nthe individual bitmaps.\n\n- When the full set of WANTs is not available in the index, we perform a\npartial revision walk using the commits that don't have bitmaps as\nroots, and limiting the revision walk as soon as we reach a commit that\nhas a corresponding bitmap. The earlier OR'ed bitmap with all the\nindexed commits can now be completed as this walk progresses, so the end\nresult is the full reachability list.\n\n- For revision walks with a HAVEs set (a set of commits that are deemed\nuninteresting), first we perform the same method as for the WANTs, but\nusing our HAVEs as roots, in order to obtain a full reachability bitmap\nof all the uninteresting commits. This bitmap then can be used to:\n\n\ta) limit the subsequent walk when building the WANTs bitmap\n\tb) finding the final set of interesting commits by performing an\n\t   AND-NOT of the WANTs and the HAVEs.\n\nIf `prepare_bitmap_walk` runs successfully, the resulting bitmap is\nstored and the equivalent of a `traverse_commit_list` call can be\nperformed by using `traverse_bitmap_commit_list`; the bitmap version\nof this call yields the objects straight from the packfile index\n(without having to look them up or parse them) and hence is several\norders of magnitude faster.\n\nAs an extra optimization, when `prepare_bitmap_walk` succeeds, the\n`reuse_partial_packfile_from_bitmap` call can be attempted: it will find\nthe amount of objects at the beginning of the on-disk packfile that can\nbe reused as-is, and return an offset into the packfile. The source\npackfile can then be loaded and the bytes up to `offset` can be written\ndirectly to the result without having to consider the entires inside the\npackfile individually.\n\nIf the `prepare_bitmap_walk` call fails (e.g. because no bitmap files\nare available), the `rev_info` struct is left untouched, and can be used\nto perform a manual rev-walk using `traverse_commit_list`.\n\nHence, this new set of functions are a generic API that allows to\nperform the equivalent of\n\n\tgit rev-list --objects [roots...] [^uninteresting...]\n\nfor any set of commits, even if they don't have specific bitmaps\ngenerated for them.\n\nIn further patches, we'll use this bitmap traversal optimization to\nspeed up the `pack-objects` and `rev-list` commands.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Makefile      |   2 +\n khash.h       | 342 +++++++++++++++++++++\n pack-bitmap.c | 965 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n pack-bitmap.h |  46 +++\n 4 files changed, 1355 insertions(+)\n create mode 100644 khash.h\n create mode 100644 pack-bitmap.c\n create mode 100644 pack-bitmap.h\n\ndiff --git a/Makefile b/Makefile\nindex eacb940..737766c 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -699,6 +699,7 @@ LIB_H += object.h\n LIB_H += pack-objects.h\n LIB_H += pack-revindex.h\n LIB_H += pack.h\n+LIB_H += pack-bitmap.h\n LIB_H += parse-options.h\n LIB_H += patch-ids.h\n LIB_H += pathspec.h\n@@ -837,6 +838,7 @@ LIB_OBJS += notes-cache.o\n LIB_OBJS += notes-merge.o\n LIB_OBJS += notes-utils.o\n LIB_OBJS += object.o\n+LIB_OBJS += pack-bitmap.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-objects.o\n LIB_OBJS += pack-revindex.o\ndiff --git a/khash.h b/khash.h\nnew file mode 100644\nindex 0000000..0fdf39d\n--- /dev/null\n+++ b/khash.h\n@@ -0,0 +1,342 @@\n+/* The MIT License\n+\n+   Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>\n+\n+   Permission is hereby granted, free of charge, to any person obtaining\n+   a copy of this software and associated documentation files (the\n+   \"Software\"), to deal in the Software without restriction, including\n+   without limitation the rights to use, copy, modify, merge, publish,\n+   distribute, sublicense, and/or sell copies of the Software, and to\n+   permit persons to whom the Software is furnished to do so, subject to\n+   the following conditions:\n+\n+   The above copyright notice and this permission notice shall be\n+   included in all copies or substantial portions of the Software.\n+\n+   THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n+   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n+   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n+   SOFTWARE.\n+*/\n+\n+#ifndef __AC_KHASH_H\n+#define __AC_KHASH_H\n+\n+#define AC_VERSION_KHASH_H \"0.2.8\"\n+\n+#include <stdlib.h>\n+#include <string.h>\n+#include <limits.h>\n+\n+typedef uint32_t khint32_t;\n+typedef uint64_t khint64_t;\n+\n+typedef khint32_t khint_t;\n+typedef khint_t khiter_t;\n+\n+#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)\n+#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)\n+#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)\n+#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))\n+#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))\n+#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))\n+#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))\n+\n+#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)\n+\n+#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))\n+\n+static inline khint_t __ac_X31_hash_string(const char *s)\n+{\n+\tkhint_t h = (khint_t)*s;\n+\tif (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;\n+\treturn h;\n+}\n+\n+#define kh_str_hash_func(key) __ac_X31_hash_string(key)\n+#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)\n+\n+static const double __ac_HASH_UPPER = 0.77;\n+\n+#define __KHASH_TYPE(name, khkey_t, khval_t) \\\n+\ttypedef struct { \\\n+\t\tkhint_t n_buckets, size, n_occupied, upper_bound; \\\n+\t\tkhint32_t *flags; \\\n+\t\tkhkey_t *keys; \\\n+\t\tkhval_t *vals; \\\n+\t} kh_##name##_t;\n+\n+#define __KHASH_PROTOTYPES(name, khkey_t, khval_t)\t \t\t\t\t\t\\\n+\textern kh_##name##_t *kh_init_##name(void);\t\t\t\t\t\t\t\\\n+\textern void kh_destroy_##name(kh_##name##_t *h);\t\t\t\t\t\\\n+\textern void kh_clear_##name(kh_##name##_t *h);\t\t\t\t\t\t\\\n+\textern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \t\\\n+\textern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \\\n+\textern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \\\n+\textern void kh_del_##name(kh_##name##_t *h, khint_t x);\n+\n+#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \\\n+\tSCOPE kh_##name##_t *kh_init_##name(void) {\t\t\t\t\t\t\t\\\n+\t\treturn (kh_##name##_t*)xcalloc(1, sizeof(kh_##name##_t));\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\tSCOPE void kh_destroy_##name(kh_##name##_t *h)\t\t\t\t\t\t\\\n+\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (h) {\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tfree((void *)h->keys); free(h->flags);\t\t\t\t\t\\\n+\t\t\tfree((void *)h->vals);\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tfree(h);\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\tSCOPE void kh_clear_##name(kh_##name##_t *h)\t\t\t\t\t\t\\\n+\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (h && h->flags) {\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tmemset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \\\n+\t\t\th->size = h->n_occupied = 0;\t\t\t\t\t\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\tSCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \t\\\n+\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (h->n_buckets) {\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tkhint_t k, i, last, mask, step = 0; \\\n+\t\t\tmask = h->n_buckets - 1;\t\t\t\t\t\t\t\t\t\\\n+\t\t\tk = __hash_func(key); i = k & mask;\t\t\t\t\t\t\t\\\n+\t\t\tlast = i; \\\n+\t\t\twhile (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \\\n+\t\t\t\ti = (i + (++step)) & mask; \\\n+\t\t\t\tif (i == last) return h->n_buckets;\t\t\t\t\t\t\\\n+\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\treturn __ac_iseither(h->flags, i)? h->n_buckets : i;\t\t\\\n+\t\t} else return 0;\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\tSCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \\\n+\t{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \\\n+\t\tkhint32_t *new_flags = 0;\t\t\t\t\t\t\t\t\t\t\\\n+\t\tkhint_t j = 1;\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tkroundup32(new_n_buckets); \t\t\t\t\t\t\t\t\t\\\n+\t\t\tif (new_n_buckets < 4) new_n_buckets = 4;\t\t\t\t\t\\\n+\t\t\tif (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0;\t/* requested size is too small */ \\\n+\t\t\telse { /* hash table size to be changed (shrink or expand); rehash */ \\\n+\t\t\t\tnew_flags = (khint32_t*)xmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t));\t\\\n+\t\t\t\tif (!new_flags) return -1;\t\t\t\t\t\t\t\t\\\n+\t\t\t\tmemset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \\\n+\t\t\t\tif (h->n_buckets < new_n_buckets) {\t/* expand */\t\t\\\n+\t\t\t\t\tkhkey_t *new_keys = (khkey_t*)xrealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \\\n+\t\t\t\t\tif (!new_keys) return -1;\t\t\t\t\t\t\t\\\n+\t\t\t\t\th->keys = new_keys;\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\tif (kh_is_map) {\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\tkhval_t *new_vals = (khval_t*)xrealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \\\n+\t\t\t\t\t\tif (!new_vals) return -1;\t\t\t\t\t\t\\\n+\t\t\t\t\t\th->vals = new_vals;\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t} /* otherwise shrink */\t\t\t\t\t\t\t\t\\\n+\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (j) { /* rehashing is needed */\t\t\t\t\t\t\t\t\\\n+\t\t\tfor (j = 0; j != h->n_buckets; ++j) {\t\t\t\t\t\t\\\n+\t\t\t\tif (__ac_iseither(h->flags, j) == 0) {\t\t\t\t\t\\\n+\t\t\t\t\tkhkey_t key = h->keys[j];\t\t\t\t\t\t\t\\\n+\t\t\t\t\tkhval_t val;\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\tkhint_t new_mask;\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\tnew_mask = new_n_buckets - 1; \t\t\t\t\t\t\\\n+\t\t\t\t\tif (kh_is_map) val = h->vals[j];\t\t\t\t\t\\\n+\t\t\t\t\t__ac_set_isdel_true(h->flags, j);\t\t\t\t\t\\\n+\t\t\t\t\twhile (1) { /* kick-out process; sort of like in Cuckoo hashing */ \\\n+\t\t\t\t\t\tkhint_t k, i, step = 0; \\\n+\t\t\t\t\t\tk = __hash_func(key);\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\ti = k & new_mask;\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\twhile (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \\\n+\t\t\t\t\t\t__ac_set_isempty_false(new_flags, i);\t\t\t\\\n+\t\t\t\t\t\tif (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \\\n+\t\t\t\t\t\t\t{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \\\n+\t\t\t\t\t\t\tif (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \\\n+\t\t\t\t\t\t\t__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \\\n+\t\t\t\t\t\t} else { /* write the element and jump out of the loop */ \\\n+\t\t\t\t\t\t\th->keys[i] = key;\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\tif (kh_is_map) h->vals[i] = val;\t\t\t\\\n+\t\t\t\t\t\t\tbreak;\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tif (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \\\n+\t\t\t\th->keys = (khkey_t*)xrealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \\\n+\t\t\t\tif (kh_is_map) h->vals = (khval_t*)xrealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \\\n+\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tfree(h->flags); /* free the working space */\t\t\t\t\\\n+\t\t\th->flags = new_flags;\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\th->n_buckets = new_n_buckets;\t\t\t\t\t\t\t\t\\\n+\t\t\th->n_occupied = h->size;\t\t\t\t\t\t\t\t\t\\\n+\t\t\th->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \\\n+\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\treturn 0;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\tSCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \\\n+\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tkhint_t x;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (h->n_occupied >= h->upper_bound) { /* update the hash table */ \\\n+\t\t\tif (h->n_buckets > (h->size<<1)) {\t\t\t\t\t\t\t\\\n+\t\t\t\tif (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear \"deleted\" elements */ \\\n+\t\t\t\t\t*ret = -1; return h->n_buckets;\t\t\t\t\t\t\\\n+\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \\\n+\t\t\t\t*ret = -1; return h->n_buckets;\t\t\t\t\t\t\t\\\n+\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \\\n+\t\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\tkhint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \\\n+\t\t\tx = site = h->n_buckets; k = __hash_func(key); i = k & mask; \\\n+\t\t\tif (__ac_isempty(h->flags, i)) x = i; /* for speed up */\t\\\n+\t\t\telse {\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\tlast = i; \\\n+\t\t\t\twhile (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \\\n+\t\t\t\t\tif (__ac_isdel(h->flags, i)) site = i;\t\t\t\t\\\n+\t\t\t\t\ti = (i + (++step)) & mask; \\\n+\t\t\t\t\tif (i == last) { x = site; break; }\t\t\t\t\t\\\n+\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\tif (x == h->n_buckets) {\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\tif (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \\\n+\t\t\t\t\telse x = i;\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (__ac_isempty(h->flags, x)) { /* not present at all */\t\t\\\n+\t\t\th->keys[x] = key;\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t__ac_set_isboth_false(h->flags, x);\t\t\t\t\t\t\t\\\n+\t\t\t++h->size; ++h->n_occupied;\t\t\t\t\t\t\t\t\t\\\n+\t\t\t*ret = 1;\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t} else if (__ac_isdel(h->flags, x)) { /* deleted */\t\t\t\t\\\n+\t\t\th->keys[x] = key;\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t__ac_set_isboth_false(h->flags, x);\t\t\t\t\t\t\t\\\n+\t\t\t++h->size;\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t\t*ret = 2;\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \\\n+\t\treturn x;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\tSCOPE void kh_del_##name(kh_##name##_t *h, khint_t x)\t\t\t\t\\\n+\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\tif (x != h->n_buckets && !__ac_iseither(h->flags, x)) {\t\t\t\\\n+\t\t\t__ac_set_isdel_true(h->flags, x);\t\t\t\t\t\t\t\\\n+\t\t\t--h->size;\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t}\n+\n+#define KHASH_DECLARE(name, khkey_t, khval_t)\t\t \t\t\t\t\t\\\n+\t__KHASH_TYPE(name, khkey_t, khval_t) \t\t\t\t\t\t\t\t\\\n+\t__KHASH_PROTOTYPES(name, khkey_t, khval_t)\n+\n+#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \\\n+\t__KHASH_TYPE(name, khkey_t, khval_t) \t\t\t\t\t\t\t\t\\\n+\t__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)\n+\n+#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \\\n+\tKHASH_INIT2(name, static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)\n+\n+/* Other convenient macros... */\n+\n+/*! @function\n+  @abstract     Test whether a bucket contains data.\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @param  x     Iterator to the bucket [khint_t]\n+  @return       1 if containing data; 0 otherwise [int]\n+ */\n+#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))\n+\n+/*! @function\n+  @abstract     Get key given an iterator\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @param  x     Iterator to the bucket [khint_t]\n+  @return       Key [type of keys]\n+ */\n+#define kh_key(h, x) ((h)->keys[x])\n+\n+/*! @function\n+  @abstract     Get value given an iterator\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @param  x     Iterator to the bucket [khint_t]\n+  @return       Value [type of values]\n+  @discussion   For hash sets, calling this results in segfault.\n+ */\n+#define kh_val(h, x) ((h)->vals[x])\n+\n+/*! @function\n+  @abstract     Alias of kh_val()\n+ */\n+#define kh_value(h, x) ((h)->vals[x])\n+\n+/*! @function\n+  @abstract     Get the start iterator\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @return       The start iterator [khint_t]\n+ */\n+#define kh_begin(h) (khint_t)(0)\n+\n+/*! @function\n+  @abstract     Get the end iterator\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @return       The end iterator [khint_t]\n+ */\n+#define kh_end(h) ((h)->n_buckets)\n+\n+/*! @function\n+  @abstract     Get the number of elements in the hash table\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @return       Number of elements in the hash table [khint_t]\n+ */\n+#define kh_size(h) ((h)->size)\n+\n+/*! @function\n+  @abstract     Get the number of buckets in the hash table\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @return       Number of buckets in the hash table [khint_t]\n+ */\n+#define kh_n_buckets(h) ((h)->n_buckets)\n+\n+/*! @function\n+  @abstract     Iterate over the entries in the hash table\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @param  kvar  Variable to which key will be assigned\n+  @param  vvar  Variable to which value will be assigned\n+  @param  code  Block of code to execute\n+ */\n+#define kh_foreach(h, kvar, vvar, code) { khint_t __i;\t\t\\\n+\tfor (__i = kh_begin(h); __i != kh_end(h); ++__i) {\t\t\\\n+\t\tif (!kh_exist(h,__i)) continue;\t\t\t\t\t\t\\\n+\t\t(kvar) = kh_key(h,__i);\t\t\t\t\t\t\t\t\\\n+\t\t(vvar) = kh_val(h,__i);\t\t\t\t\t\t\t\t\\\n+\t\tcode;\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t} }\n+\n+/*! @function\n+  @abstract     Iterate over the values in the hash table\n+  @param  h     Pointer to the hash table [khash_t(name)*]\n+  @param  vvar  Variable to which value will be assigned\n+  @param  code  Block of code to execute\n+ */\n+#define kh_foreach_value(h, vvar, code) { khint_t __i;\t\t\\\n+\tfor (__i = kh_begin(h); __i != kh_end(h); ++__i) {\t\t\\\n+\t\tif (!kh_exist(h,__i)) continue;\t\t\t\t\t\t\\\n+\t\t(vvar) = kh_val(h,__i);\t\t\t\t\t\t\t\t\\\n+\t\tcode;\t\t\t\t\t\t\t\t\t\t\t\t\\\n+\t} }\n+\n+static inline khint_t __kh_oid_hash(const unsigned char *oid)\n+{\n+\tkhint_t hash;\n+\tmemcpy(&hash, oid, sizeof(hash));\n+\treturn hash;\n+}\n+\n+#define __kh_oid_cmp(a, b) (hashcmp(a, b) == 0)\n+\n+KHASH_INIT(sha1, const unsigned char *, void *, 1, __kh_oid_hash, __kh_oid_cmp)\n+typedef kh_sha1_t khash_sha1;\n+\n+KHASH_INIT(sha1_pos, const unsigned char *, int, 1, __kh_oid_hash, __kh_oid_cmp)\n+typedef kh_sha1_pos_t khash_sha1_pos;\n+\n+#endif /* __AC_KHASH_H */\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nnew file mode 100644\nindex 0000000..73c52fd\n--- /dev/null\n+++ b/pack-bitmap.c\n@@ -0,0 +1,965 @@\n+#include <stdlib.h>\n+\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"tag.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n+#include \"progress.h\"\n+#include \"list-objects.h\"\n+#include \"pack.h\"\n+#include \"pack-bitmap.h\"\n+#include \"pack-revindex.h\"\n+#include \"pack-objects.h\"\n+\n+/*\n+ * An entry on the bitmap index, representing the bitmap for a given\n+ * commit.\n+ */\n+struct stored_bitmap {\n+\tunsigned char sha1[20];\n+\tstruct ewah_bitmap *root;\n+\tstruct stored_bitmap *xor;\n+\tint flags;\n+};\n+\n+/*\n+ * The currently active bitmap index. By design, repositories only have\n+ * a single bitmap index available (the index for the biggest packfile in\n+ * the repository), since bitmap indexes need full closure.\n+ *\n+ * If there is more than one bitmap index available (e.g. because of alternates),\n+ * the active bitmap index is the largest one.\n+ */\n+static struct bitmap_index {\n+\t/* Packfile to which this bitmap index belongs to */\n+\tstruct packed_git *pack;\n+\n+\t/* reverse index for the packfile */\n+\tstruct pack_revindex *reverse_index;\n+\n+\t/*\n+\t * Mark the first `reuse_objects` in the packfile as reused:\n+\t * they will be sent as-is without using them for repacking\n+\t * calculations\n+\t */\n+\tuint32_t reuse_objects;\n+\n+\t/* mmapped buffer of the whole bitmap index */\n+\tunsigned char *map;\n+\tsize_t map_size; /* size of the mmaped buffer */\n+\tsize_t map_pos; /* current position when loading the index */\n+\n+\t/*\n+\t * Type indexes.\n+\t *\n+\t * Each bitmap marks which objects in the packfile  are of the given\n+\t * type. This provides type information when yielding the objects from\n+\t * the packfile during a walk, which allows for better delta bases.\n+\t */\n+\tstruct ewah_bitmap *commits;\n+\tstruct ewah_bitmap *trees;\n+\tstruct ewah_bitmap *blobs;\n+\tstruct ewah_bitmap *tags;\n+\n+\t/* Map from SHA1 -> `stored_bitmap` for all the bitmapped comits */\n+\tkhash_sha1 *bitmaps;\n+\n+\t/* Number of bitmapped commits */\n+\tuint32_t entry_count;\n+\n+\t/*\n+\t * Extended index.\n+\t *\n+\t * When trying to perform bitmap operations with objects that are not\n+\t * packed in `pack`, these objects are added to this \"fake index\" and\n+\t * are assumed to appear at the end of the packfile for all operations\n+\t */\n+\tstruct eindex {\n+\t\tstruct object **objects;\n+\t\tuint32_t *hashes;\n+\t\tuint32_t count, alloc;\n+\t\tkhash_sha1_pos *positions;\n+\t} ext_index;\n+\n+\t/* Bitmap result of the last performed walk */\n+\tstruct bitmap *result;\n+\n+\t/* Version of the bitmap index */\n+\tunsigned int version;\n+\n+\tunsigned loaded : 1;\n+\n+} bitmap_git;\n+\n+static struct ewah_bitmap *lookup_stored_bitmap(struct stored_bitmap *st)\n+{\n+\tstruct ewah_bitmap *parent;\n+\tstruct ewah_bitmap *composed;\n+\n+\tif (st->xor == NULL)\n+\t\treturn st->root;\n+\n+\tcomposed = ewah_pool_new();\n+\tparent = lookup_stored_bitmap(st->xor);\n+\tewah_xor(st->root, parent, composed);\n+\n+\tewah_pool_free(st->root);\n+\tst->root = composed;\n+\tst->xor = NULL;\n+\n+\treturn composed;\n+}\n+\n+/*\n+ * Read a bitmap from the current read position on the mmaped\n+ * index, and increase the read position accordingly\n+ */\n+static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)\n+{\n+\tstruct ewah_bitmap *b = ewah_pool_new();\n+\n+\tint bitmap_size = ewah_read_mmap(b,\n+\t\tindex->map + index->map_pos,\n+\t\tindex->map_size - index->map_pos);\n+\n+\tif (bitmap_size < 0) {\n+\t\terror(\"Failed to load bitmap index (corrupted?)\");\n+\t\tewah_pool_free(b);\n+\t\treturn NULL;\n+\t}\n+\n+\tindex->map_pos += bitmap_size;\n+\treturn b;\n+}\n+\n+static int load_bitmap_header(struct bitmap_index *index)\n+{\n+\tstruct bitmap_disk_header *header = (void *)index->map;\n+\n+\tif (index->map_size < sizeof(*header) + 20)\n+\t\treturn error(\"Corrupted bitmap index (missing header data)\");\n+\n+\tif (memcmp(header->magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE)) != 0)\n+\t\treturn error(\"Corrupted bitmap index file (wrong header)\");\n+\n+\tindex->version = ntohs(header->version);\n+\tif (index->version != 1)\n+\t\treturn error(\"Unsupported version for bitmap index file (%d)\", index->version);\n+\n+\t/* Parse known bitmap format options */\n+\t{\n+\t\tuint32_t flags = ntohs(header->options);\n+\n+\t\tif ((flags & BITMAP_OPT_FULL_DAG) == 0)\n+\t\t\treturn error(\"Unsupported options for bitmap index file \"\n+\t\t\t\t\"(Git requires BITMAP_OPT_FULL_DAG)\");\n+\t}\n+\n+\tindex->entry_count = ntohl(header->entry_count);\n+\tindex->map_pos += sizeof(*header);\n+\treturn 0;\n+}\n+\n+static struct stored_bitmap *store_bitmap(struct bitmap_index *index,\n+\t\t\t\t\t  struct ewah_bitmap *root,\n+\t\t\t\t\t  const unsigned char *sha1,\n+\t\t\t\t\t  struct stored_bitmap *xor_with,\n+\t\t\t\t\t  int flags)\n+{\n+\tstruct stored_bitmap *stored;\n+\tkhiter_t hash_pos;\n+\tint ret;\n+\n+\tstored = xmalloc(sizeof(struct stored_bitmap));\n+\tstored->root = root;\n+\tstored->xor = xor_with;\n+\tstored->flags = flags;\n+\thashcpy(stored->sha1, sha1);\n+\n+\thash_pos = kh_put_sha1(index->bitmaps, stored->sha1, &ret);\n+\n+\t/* a 0 return code means the insertion succeeded with no changes,\n+\t * because the SHA1 already existed on the map. this is bad, there\n+\t * shouldn't be duplicated commits in the index */\n+\tif (ret == 0) {\n+\t\terror(\"Duplicate entry in bitmap index: %s\", sha1_to_hex(sha1));\n+\t\treturn NULL;\n+\t}\n+\n+\tkh_value(index->bitmaps, hash_pos) = stored;\n+\treturn stored;\n+}\n+\n+static int load_bitmap_entries_v1(struct bitmap_index *index)\n+{\n+\tstatic const size_t MAX_XOR_OFFSET = 160;\n+\n+\tuint32_t i;\n+\tstruct stored_bitmap **recent_bitmaps;\n+\tstruct bitmap_disk_entry *entry;\n+\n+\trecent_bitmaps = xcalloc(MAX_XOR_OFFSET, sizeof(struct stored_bitmap));\n+\n+\tfor (i = 0; i < index->entry_count; ++i) {\n+\t\tint xor_offset, flags;\n+\t\tstruct ewah_bitmap *bitmap = NULL;\n+\t\tstruct stored_bitmap *xor_bitmap = NULL;\n+\t\tuint32_t commit_idx_pos;\n+\t\tconst unsigned char *sha1;\n+\n+\t\tentry = (struct bitmap_disk_entry *)(index->map + index->map_pos);\n+\t\tindex->map_pos += sizeof(struct bitmap_disk_entry);\n+\n+\t\tcommit_idx_pos = ntohl(entry->object_pos);\n+\t\tsha1 = nth_packed_object_sha1(index->pack, commit_idx_pos);\n+\n+\t\txor_offset = (int)entry->xor_offset;\n+\t\tflags = (int)entry->flags;\n+\n+\t\tbitmap = read_bitmap_1(index);\n+\t\tif (!bitmap)\n+\t\t\treturn -1;\n+\n+\t\tif (xor_offset > MAX_XOR_OFFSET || xor_offset > i)\n+\t\t\treturn error(\"Corrupted bitmap pack index\");\n+\n+\t\tif (xor_offset > 0) {\n+\t\t\txor_bitmap = recent_bitmaps[(i - xor_offset) % MAX_XOR_OFFSET];\n+\n+\t\t\tif (xor_bitmap == NULL)\n+\t\t\t\treturn error(\"Invalid XOR offset in bitmap pack index\");\n+\t\t}\n+\n+\t\trecent_bitmaps[i % MAX_XOR_OFFSET] = store_bitmap(\n+\t\t\tindex, bitmap, sha1, xor_bitmap, flags);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int open_pack_bitmap_1(struct packed_git *packfile)\n+{\n+\tint fd;\n+\tstruct stat st;\n+\tchar *idx_name;\n+\n+\tif (open_pack_index(packfile))\n+\t\treturn -1;\n+\n+\tidx_name = pack_bitmap_filename(packfile);\n+\tfd = git_open_noatime(idx_name);\n+\tfree(idx_name);\n+\n+\tif (fd < 0)\n+\t\treturn -1;\n+\n+\tif (fstat(fd, &st)) {\n+\t\tclose(fd);\n+\t\treturn -1;\n+\t}\n+\n+\tbitmap_git.pack = packfile;\n+\tbitmap_git.map_size = xsize_t(st.st_size);\n+\tbitmap_git.map = xmmap(NULL, bitmap_git.map_size, PROT_READ, MAP_PRIVATE, fd, 0);\n+\tbitmap_git.map_pos = 0;\n+\tclose(fd);\n+\n+\tif (load_bitmap_header(&bitmap_git) < 0) {\n+\t\tmunmap(bitmap_git.map, bitmap_git.map_size);\n+\t\tbitmap_git.map = NULL;\n+\t\tbitmap_git.map_size = 0;\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int load_pack_bitmap(void)\n+{\n+\tassert(bitmap_git.map && !bitmap_git.loaded);\n+\n+\tbitmap_git.bitmaps = kh_init_sha1();\n+\tbitmap_git.ext_index.positions = kh_init_sha1_pos();\n+\tbitmap_git.reverse_index = revindex_for_pack(bitmap_git.pack);\n+\n+\tif (!(bitmap_git.commits = read_bitmap_1(&bitmap_git)) ||\n+\t\t!(bitmap_git.trees = read_bitmap_1(&bitmap_git)) ||\n+\t\t!(bitmap_git.blobs = read_bitmap_1(&bitmap_git)) ||\n+\t\t!(bitmap_git.tags = read_bitmap_1(&bitmap_git)))\n+\t\tgoto failed;\n+\n+\tif (load_bitmap_entries_v1(&bitmap_git) < 0)\n+\t\tgoto failed;\n+\n+\tbitmap_git.loaded = 1;\n+\treturn 0;\n+\n+failed:\n+\tmunmap(bitmap_git.map, bitmap_git.map_size);\n+\tbitmap_git.map = NULL;\n+\tbitmap_git.map_size = 0;\n+\treturn -1;\n+}\n+\n+char *pack_bitmap_filename(struct packed_git *p)\n+{\n+\tchar *idx_name;\n+\tint len;\n+\n+\tlen = strlen(p->pack_name) - strlen(\".pack\");\n+\tidx_name = xmalloc(len + strlen(\".bitmap\") + 1);\n+\n+\tmemcpy(idx_name, p->pack_name, len);\n+\tmemcpy(idx_name + len, \".bitmap\", strlen(\".bitmap\") + 1);\n+\n+\treturn idx_name;\n+}\n+\n+static int open_pack_bitmap(void)\n+{\n+\tstruct packed_git *p;\n+\n+\tassert(!bitmap_git.map && !bitmap_git.loaded);\n+\n+\tprepare_packed_git();\n+\tfor (p = packed_git; p; p = p->next) {\n+\t\tif (open_pack_bitmap_1(p) == 0)\n+\t\t\treturn 0;\n+\t}\n+\n+\treturn -1;\n+}\n+\n+int prepare_bitmap_git(void)\n+{\n+\tif (bitmap_git.loaded)\n+\t\treturn 0;\n+\n+\tif (!open_pack_bitmap())\n+\t\treturn load_pack_bitmap();\n+\n+\treturn -1;\n+}\n+\n+struct include_data {\n+\tstruct bitmap *base;\n+\tstruct bitmap *seen;\n+};\n+\n+static inline int bitmap_position_extended(const unsigned char *sha1)\n+{\n+\tkhash_sha1_pos *positions = bitmap_git.ext_index.positions;\n+\tkhiter_t pos = kh_get_sha1_pos(positions, sha1);\n+\n+\tif (pos < kh_end(positions)) {\n+\t\tint bitmap_pos = kh_value(positions, pos);\n+\t\treturn bitmap_pos + bitmap_git.pack->num_objects;\n+\t}\n+\n+\treturn -1;\n+}\n+\n+static inline int bitmap_position_packfile(const unsigned char *sha1)\n+{\n+\toff_t offset = find_pack_entry_one(sha1, bitmap_git.pack);\n+\tif (!offset)\n+\t\treturn -1;\n+\n+\treturn find_revindex_position(bitmap_git.reverse_index, offset);\n+}\n+\n+static int bitmap_position(const unsigned char *sha1)\n+{\n+\tint pos = bitmap_position_packfile(sha1);\n+\treturn (pos >= 0) ? pos : bitmap_position_extended(sha1);\n+}\n+\n+static int ext_index_add_object(struct object *object, const char *name)\n+{\n+\tstruct eindex *eindex = &bitmap_git.ext_index;\n+\n+\tkhiter_t hash_pos;\n+\tint hash_ret;\n+\tint bitmap_pos;\n+\n+\thash_pos = kh_put_sha1_pos(eindex->positions, object->sha1, &hash_ret);\n+\tif (hash_ret > 0) {\n+\t\tif (eindex->count >= eindex->alloc) {\n+\t\t\teindex->alloc = (eindex->alloc + 16) * 3 / 2;\n+\t\t\teindex->objects = xrealloc(eindex->objects,\n+\t\t\t\teindex->alloc * sizeof(struct object *));\n+\t\t\teindex->hashes = xrealloc(eindex->hashes,\n+\t\t\t\teindex->alloc * sizeof(uint32_t));\n+\t\t}\n+\n+\t\tbitmap_pos = eindex->count;\n+\t\teindex->objects[eindex->count] = object;\n+\t\teindex->hashes[eindex->count] = pack_name_hash(name);\n+\t\tkh_value(eindex->positions, hash_pos) = bitmap_pos;\n+\t\teindex->count++;\n+\t} else {\n+\t\tbitmap_pos = kh_value(eindex->positions, hash_pos);\n+\t}\n+\n+\treturn bitmap_pos + bitmap_git.pack->num_objects;\n+}\n+\n+static void show_object(struct object *object, const struct name_path *path,\n+\t\t\tconst char *last, void *data)\n+{\n+\tstruct bitmap *base = data;\n+\tint bitmap_pos;\n+\n+\tbitmap_pos = bitmap_position(object->sha1);\n+\n+\tif (bitmap_pos < 0) {\n+\t\tchar *name = path_name(path, last);\n+\t\tbitmap_pos = ext_index_add_object(object, name);\n+\t\tfree(name);\n+\t}\n+\n+\tbitmap_set(base, bitmap_pos);\n+}\n+\n+static void show_commit(struct commit *commit, void *data)\n+{\n+}\n+\n+static int add_to_include_set(struct include_data *data,\n+\t\t\t      const unsigned char *sha1,\n+\t\t\t      int bitmap_pos)\n+{\n+\tkhiter_t hash_pos;\n+\n+\tif (data->seen && bitmap_get(data->seen, bitmap_pos))\n+\t\treturn 0;\n+\n+\tif (bitmap_get(data->base, bitmap_pos))\n+\t\treturn 0;\n+\n+\thash_pos = kh_get_sha1(bitmap_git.bitmaps, sha1);\n+\tif (hash_pos < kh_end(bitmap_git.bitmaps)) {\n+\t\tstruct stored_bitmap *st = kh_value(bitmap_git.bitmaps, hash_pos);\n+\t\tbitmap_or_ewah(data->base, lookup_stored_bitmap(st));\n+\t\treturn 0;\n+\t}\n+\n+\tbitmap_set(data->base, bitmap_pos);\n+\treturn 1;\n+}\n+\n+static int should_include(struct commit *commit, void *_data)\n+{\n+\tstruct include_data *data = _data;\n+\tint bitmap_pos;\n+\n+\tbitmap_pos = bitmap_position(commit->object.sha1);\n+\tif (bitmap_pos < 0)\n+\t\tbitmap_pos = ext_index_add_object((struct object *)commit, NULL);\n+\n+\tif (!add_to_include_set(data, commit->object.sha1, bitmap_pos)) {\n+\t\tstruct commit_list *parent = commit->parents;\n+\n+\t\twhile (parent) {\n+\t\t\tparent->item->object.flags |= SEEN;\n+\t\t\tparent = parent->next;\n+\t\t}\n+\n+\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+static struct bitmap *find_objects(struct rev_info *revs,\n+\t\t\t\t   struct object_list *roots,\n+\t\t\t\t   struct bitmap *seen)\n+{\n+\tstruct bitmap *base = NULL;\n+\tint needs_walk = 0;\n+\n+\tstruct object_list *not_mapped = NULL;\n+\n+\t/*\n+\t * Go through all the roots for the walk. The ones that have bitmaps\n+\t * on the bitmap index will be `or`ed together to form an initial\n+\t * global reachability analysis.\n+\t *\n+\t * The ones without bitmaps in the index will be stored in the\n+\t * `not_mapped_list` for further processing.\n+\t */\n+\twhile (roots) {\n+\t\tstruct object *object = roots->item;\n+\t\troots = roots->next;\n+\n+\t\tif (object->type == OBJ_COMMIT) {\n+\t\t\tkhiter_t pos = kh_get_sha1(bitmap_git.bitmaps, object->sha1);\n+\n+\t\t\tif (pos < kh_end(bitmap_git.bitmaps)) {\n+\t\t\t\tstruct stored_bitmap *st = kh_value(bitmap_git.bitmaps, pos);\n+\t\t\t\tstruct ewah_bitmap *or_with = lookup_stored_bitmap(st);\n+\n+\t\t\t\tif (base == NULL)\n+\t\t\t\t\tbase = ewah_to_bitmap(or_with);\n+\t\t\t\telse\n+\t\t\t\t\tbitmap_or_ewah(base, or_with);\n+\n+\t\t\t\tobject->flags |= SEEN;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+\n+\t\tobject_list_insert(object, &not_mapped);\n+\t}\n+\n+\t/*\n+\t * Best case scenario: We found bitmaps for all the roots,\n+\t * so the resulting `or` bitmap has the full reachability analysis\n+\t */\n+\tif (not_mapped == NULL)\n+\t\treturn base;\n+\n+\troots = not_mapped;\n+\n+\t/*\n+\t * Let's iterate through all the roots that don't have bitmaps to\n+\t * check if we can determine them to be reachable from the existing\n+\t * global bitmap.\n+\t *\n+\t * If we cannot find them in the existing global bitmap, we'll need\n+\t * to push them to an actual walk and run it until we can confirm\n+\t * they are reachable\n+\t */\n+\twhile (roots) {\n+\t\tstruct object *object = roots->item;\n+\t\tint pos;\n+\n+\t\troots = roots->next;\n+\t\tpos = bitmap_position(object->sha1);\n+\n+\t\tif (pos < 0 || base == NULL || !bitmap_get(base, pos)) {\n+\t\t\tobject->flags &= ~UNINTERESTING;\n+\t\t\tadd_pending_object(revs, object, \"\");\n+\t\t\tneeds_walk = 1;\n+\t\t} else {\n+\t\t\tobject->flags |= SEEN;\n+\t\t}\n+\t}\n+\n+\tif (needs_walk) {\n+\t\tstruct include_data incdata;\n+\n+\t\tif (base == NULL)\n+\t\t\tbase = bitmap_new();\n+\n+\t\tincdata.base = base;\n+\t\tincdata.seen = seen;\n+\n+\t\trevs->include_check = should_include;\n+\t\trevs->include_check_data = &incdata;\n+\n+\t\tif (prepare_revision_walk(revs))\n+\t\t\tdie(\"revision walk setup failed\");\n+\n+\t\ttraverse_commit_list(revs, show_commit, show_object, base);\n+\t}\n+\n+\treturn base;\n+}\n+\n+static void show_extended_objects(struct bitmap *objects,\n+\t\t\t\t  show_reachable_fn show_reach)\n+{\n+\tstruct eindex *eindex = &bitmap_git.ext_index;\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < eindex->count; ++i) {\n+\t\tstruct object *obj;\n+\n+\t\tif (!bitmap_get(objects, bitmap_git.pack->num_objects + i))\n+\t\t\tcontinue;\n+\n+\t\tobj = eindex->objects[i];\n+\t\tshow_reach(obj->sha1, obj->type, 0, eindex->hashes[i], NULL, 0);\n+\t}\n+}\n+\n+static void show_objects_for_type(\n+\tstruct bitmap *objects,\n+\tstruct ewah_bitmap *type_filter,\n+\tenum object_type object_type,\n+\tshow_reachable_fn show_reach)\n+{\n+\tsize_t pos = 0, i = 0;\n+\tuint32_t offset;\n+\n+\tstruct ewah_iterator it;\n+\teword_t filter;\n+\n+\tif (bitmap_git.reuse_objects == bitmap_git.pack->num_objects)\n+\t\treturn;\n+\n+\tewah_iterator_init(&it, type_filter);\n+\n+\twhile (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) {\n+\t\teword_t word = objects->words[i] & filter;\n+\n+\t\tfor (offset = 0; offset < BITS_IN_WORD; ++offset) {\n+\t\t\tconst unsigned char *sha1;\n+\t\t\tstruct revindex_entry *entry;\n+\t\t\tuint32_t hash = 0;\n+\n+\t\t\tif ((word >> offset) == 0)\n+\t\t\t\tbreak;\n+\n+\t\t\toffset += ewah_bit_ctz64(word >> offset);\n+\n+\t\t\tif (pos + offset < bitmap_git.reuse_objects)\n+\t\t\t\tcontinue;\n+\n+\t\t\tentry = &bitmap_git.reverse_index->revindex[pos + offset];\n+\t\t\tsha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);\n+\n+\t\t\tshow_reach(sha1, object_type, 0, hash, bitmap_git.pack, entry->offset);\n+\t\t}\n+\n+\t\tpos += BITS_IN_WORD;\n+\t\ti++;\n+\t}\n+}\n+\n+static int in_bitmapped_pack(struct object_list *roots)\n+{\n+\twhile (roots) {\n+\t\tstruct object *object = roots->item;\n+\t\troots = roots->next;\n+\n+\t\tif (find_pack_entry_one(object->sha1, bitmap_git.pack) > 0)\n+\t\t\treturn 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int prepare_bitmap_walk(struct rev_info *revs)\n+{\n+\tunsigned int i;\n+\tunsigned int pending_nr = revs->pending.nr;\n+\tstruct object_array_entry *pending_e = revs->pending.objects;\n+\n+\tstruct object_list *wants = NULL;\n+\tstruct object_list *haves = NULL;\n+\n+\tstruct bitmap *wants_bitmap = NULL;\n+\tstruct bitmap *haves_bitmap = NULL;\n+\n+\tif (!bitmap_git.loaded) {\n+\t\t/* try to open a bitmapped pack, but don't parse it yet\n+\t\t * because we may not need to use it */\n+\t\tif (open_pack_bitmap() < 0)\n+\t\t\treturn -1;\n+\t}\n+\n+\tfor (i = 0; i < pending_nr; ++i) {\n+\t\tstruct object *object = pending_e[i].item;\n+\n+\t\tif (object->type == OBJ_NONE)\n+\t\t\tparse_object_or_die(object->sha1, NULL);\n+\n+\t\twhile (object->type == OBJ_TAG) {\n+\t\t\tstruct tag *tag = (struct tag *) object;\n+\n+\t\t\tif (object->flags & UNINTERESTING)\n+\t\t\t\tobject_list_insert(object, &haves);\n+\t\t\telse\n+\t\t\t\tobject_list_insert(object, &wants);\n+\n+\t\t\tif (!tag->tagged)\n+\t\t\t\tdie(\"bad tag\");\n+\t\t\tobject = parse_object_or_die(tag->tagged->sha1, NULL);\n+\t\t}\n+\n+\t\tif (object->flags & UNINTERESTING)\n+\t\t\tobject_list_insert(object, &haves);\n+\t\telse\n+\t\t\tobject_list_insert(object, &wants);\n+\t}\n+\n+\t/*\n+\t * if we have a HAVES list, but none of those haves is contained\n+\t * in the packfile that has a bitmap, we don't have anything to\n+\t * optimize here\n+\t */\n+\tif (haves && !in_bitmapped_pack(haves))\n+\t\treturn -1;\n+\n+\t/* if we don't want anything, we're done here */\n+\tif (!wants)\n+\t\treturn -1;\n+\n+\t/*\n+\t * now we're going to use bitmaps, so load the actual bitmap entries\n+\t * from disk. this is the point of no return; after this the rev_list\n+\t * becomes invalidated and we must perform the revwalk through bitmaps\n+\t */\n+\tif (!bitmap_git.loaded && load_pack_bitmap() < 0)\n+\t\treturn -1;\n+\n+\trevs->pending.nr = 0;\n+\trevs->pending.alloc = 0;\n+\trevs->pending.objects = NULL;\n+\n+\tif (haves) {\n+\t\thaves_bitmap = find_objects(revs, haves, NULL);\n+\t\treset_revision_walk();\n+\n+\t\tif (haves_bitmap == NULL)\n+\t\t\tdie(\"BUG: failed to perform bitmap walk\");\n+\t}\n+\n+\twants_bitmap = find_objects(revs, wants, haves_bitmap);\n+\n+\tif (!wants_bitmap)\n+\t\tdie(\"BUG: failed to perform bitmap walk\");\n+\n+\tif (haves_bitmap)\n+\t\tbitmap_and_not(wants_bitmap, haves_bitmap);\n+\n+\tbitmap_git.result = wants_bitmap;\n+\n+\tbitmap_free(haves_bitmap);\n+\treturn 0;\n+}\n+\n+int reuse_partial_packfile_from_bitmap(struct packed_git **packfile,\n+\t\t\t\t       uint32_t *entries,\n+\t\t\t\t       off_t *up_to)\n+{\n+\t/*\n+\t * Reuse the packfile content if we need more than\n+\t * 90% of its objects\n+\t */\n+\tstatic const double REUSE_PERCENT = 0.9;\n+\n+\tstruct bitmap *result = bitmap_git.result;\n+\tuint32_t reuse_threshold;\n+\tuint32_t i, reuse_objects = 0;\n+\n+\tassert(result);\n+\n+\tfor (i = 0; i < result->word_alloc; ++i) {\n+\t\tif (result->words[i] != (eword_t)~0) {\n+\t\t\treuse_objects += ewah_bit_ctz64(~result->words[i]);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\treuse_objects += BITS_IN_WORD;\n+\t}\n+\n+#ifdef GIT_BITMAP_DEBUG\n+\t{\n+\t\tconst unsigned char *sha1;\n+\t\tstruct revindex_entry *entry;\n+\n+\t\tentry = &bitmap_git.reverse_index->revindex[reuse_objects];\n+\t\tsha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);\n+\n+\t\tfprintf(stderr, \"Failed to reuse at %d (%016llx)\\n\",\n+\t\t\treuse_objects, result->words[i]);\n+\t\tfprintf(stderr, \" %s\\n\", sha1_to_hex(sha1));\n+\t}\n+#endif\n+\n+\tif (!reuse_objects)\n+\t\treturn -1;\n+\n+\tif (reuse_objects >= bitmap_git.pack->num_objects) {\n+\t\tbitmap_git.reuse_objects = *entries = bitmap_git.pack->num_objects;\n+\t\t*up_to = -1; /* reuse the full pack */\n+\t\t*packfile = bitmap_git.pack;\n+\t\treturn 0;\n+\t}\n+\n+\treuse_threshold = bitmap_popcount(bitmap_git.result) * REUSE_PERCENT;\n+\n+\tif (reuse_objects < reuse_threshold)\n+\t\treturn -1;\n+\n+\tbitmap_git.reuse_objects = *entries = reuse_objects;\n+\t*up_to = bitmap_git.reverse_index->revindex[reuse_objects].offset;\n+\t*packfile = bitmap_git.pack;\n+\n+\treturn 0;\n+}\n+\n+void traverse_bitmap_commit_list(show_reachable_fn show_reachable)\n+{\n+\tassert(bitmap_git.result);\n+\n+\tshow_objects_for_type(bitmap_git.result, bitmap_git.commits,\n+\t\tOBJ_COMMIT, show_reachable);\n+\tshow_objects_for_type(bitmap_git.result, bitmap_git.trees,\n+\t\tOBJ_TREE, show_reachable);\n+\tshow_objects_for_type(bitmap_git.result, bitmap_git.blobs,\n+\t\tOBJ_BLOB, show_reachable);\n+\tshow_objects_for_type(bitmap_git.result, bitmap_git.tags,\n+\t\tOBJ_TAG, show_reachable);\n+\n+\tshow_extended_objects(bitmap_git.result, show_reachable);\n+\n+\tbitmap_free(bitmap_git.result);\n+\tbitmap_git.result = NULL;\n+}\n+\n+static uint32_t count_object_type(struct bitmap *objects,\n+\t\t\t\t  enum object_type type)\n+{\n+\tstruct eindex *eindex = &bitmap_git.ext_index;\n+\n+\tuint32_t i = 0, count = 0;\n+\tstruct ewah_iterator it;\n+\teword_t filter;\n+\n+\tswitch (type) {\n+\tcase OBJ_COMMIT:\n+\t\tewah_iterator_init(&it, bitmap_git.commits);\n+\t\tbreak;\n+\n+\tcase OBJ_TREE:\n+\t\tewah_iterator_init(&it, bitmap_git.trees);\n+\t\tbreak;\n+\n+\tcase OBJ_BLOB:\n+\t\tewah_iterator_init(&it, bitmap_git.blobs);\n+\t\tbreak;\n+\n+\tcase OBJ_TAG:\n+\t\tewah_iterator_init(&it, bitmap_git.tags);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\n+\twhile (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) {\n+\t\teword_t word = objects->words[i++] & filter;\n+\t\tcount += ewah_bit_popcount64(word);\n+\t}\n+\n+\tfor (i = 0; i < eindex->count; ++i) {\n+\t\tif (eindex->objects[i]->type == type &&\n+\t\t\tbitmap_get(objects, bitmap_git.pack->num_objects + i))\n+\t\t\tcount++;\n+\t}\n+\n+\treturn count;\n+}\n+\n+void count_bitmap_commit_list(uint32_t *commits, uint32_t *trees,\n+\t\t\t      uint32_t *blobs, uint32_t *tags)\n+{\n+\tassert(bitmap_git.result);\n+\n+\tif (commits)\n+\t\t*commits = count_object_type(bitmap_git.result, OBJ_COMMIT);\n+\n+\tif (trees)\n+\t\t*trees = count_object_type(bitmap_git.result, OBJ_TREE);\n+\n+\tif (blobs)\n+\t\t*blobs = count_object_type(bitmap_git.result, OBJ_BLOB);\n+\n+\tif (tags)\n+\t\t*tags = count_object_type(bitmap_git.result, OBJ_TAG);\n+}\n+\n+struct bitmap_test_data {\n+\tstruct bitmap *base;\n+\tstruct progress *prg;\n+\tsize_t seen;\n+};\n+\n+static void test_show_object(struct object *object,\n+\t\t\t     const struct name_path *path,\n+\t\t\t     const char *last, void *data)\n+{\n+\tstruct bitmap_test_data *tdata = data;\n+\tint bitmap_pos;\n+\n+\tbitmap_pos = bitmap_position(object->sha1);\n+\tif (bitmap_pos < 0)\n+\t\tdie(\"Object not in bitmap: %s\\n\", sha1_to_hex(object->sha1));\n+\n+\tbitmap_set(tdata->base, bitmap_pos);\n+\tdisplay_progress(tdata->prg, ++tdata->seen);\n+}\n+\n+static void test_show_commit(struct commit *commit, void *data)\n+{\n+\tstruct bitmap_test_data *tdata = data;\n+\tint bitmap_pos;\n+\n+\tbitmap_pos = bitmap_position(commit->object.sha1);\n+\tif (bitmap_pos < 0)\n+\t\tdie(\"Object not in bitmap: %s\\n\", sha1_to_hex(commit->object.sha1));\n+\n+\tbitmap_set(tdata->base, bitmap_pos);\n+\tdisplay_progress(tdata->prg, ++tdata->seen);\n+}\n+\n+void test_bitmap_walk(struct rev_info *revs)\n+{\n+\tstruct object *root;\n+\tstruct bitmap *result = NULL;\n+\tkhiter_t pos;\n+\tsize_t result_popcnt;\n+\tstruct bitmap_test_data tdata;\n+\n+\tif (prepare_bitmap_git())\n+\t\tdie(\"failed to load bitmap indexes\");\n+\n+\tif (revs->pending.nr != 1)\n+\t\tdie(\"you must specify exactly one commit to test\");\n+\n+\tfprintf(stderr, \"Bitmap v%d test (%d entries loaded)\\n\",\n+\t\tbitmap_git.version, bitmap_git.entry_count);\n+\n+\troot = revs->pending.objects[0].item;\n+\tpos = kh_get_sha1(bitmap_git.bitmaps, root->sha1);\n+\n+\tif (pos < kh_end(bitmap_git.bitmaps)) {\n+\t\tstruct stored_bitmap *st = kh_value(bitmap_git.bitmaps, pos);\n+\t\tstruct ewah_bitmap *bm = lookup_stored_bitmap(st);\n+\n+\t\tfprintf(stderr, \"Found bitmap for %s. %d bits / %08x checksum\\n\",\n+\t\t\tsha1_to_hex(root->sha1), (int)bm->bit_size, ewah_checksum(bm));\n+\n+\t\tresult = ewah_to_bitmap(bm);\n+\t}\n+\n+\tif (result == NULL)\n+\t\tdie(\"Commit %s doesn't have an indexed bitmap\", sha1_to_hex(root->sha1));\n+\n+\trevs->tag_objects = 1;\n+\trevs->tree_objects = 1;\n+\trevs->blob_objects = 1;\n+\n+\tresult_popcnt = bitmap_popcount(result);\n+\n+\tif (prepare_revision_walk(revs))\n+\t\tdie(\"revision walk setup failed\");\n+\n+\ttdata.base = bitmap_new();\n+\ttdata.prg = start_progress(\"Verifying bitmap entries\", result_popcnt);\n+\ttdata.seen = 0;\n+\n+\ttraverse_commit_list(revs, &test_show_commit, &test_show_object, &tdata);\n+\n+\tstop_progress(&tdata.prg);\n+\n+\tif (bitmap_equals(result, tdata.base))\n+\t\tfprintf(stderr, \"OK!\\n\");\n+\telse\n+\t\tfprintf(stderr, \"Mismatch!\\n\");\n+}\ndiff --git a/pack-bitmap.h b/pack-bitmap.h\nnew file mode 100644\nindex 0000000..df45578\n--- /dev/null\n+++ b/pack-bitmap.h\n@@ -0,0 +1,46 @@\n+#ifndef PACK_BITMAP_H\n+#define PACK_BITMAP_H\n+\n+#define ewah_malloc xmalloc\n+#define ewah_calloc xcalloc\n+#define ewah_realloc xrealloc\n+#include \"ewah/ewok.h\"\n+#include \"khash.h\"\n+\n+struct bitmap_disk_entry {\n+\tuint32_t object_pos;\n+\tuint8_t xor_offset;\n+\tuint8_t flags;\n+} __attribute__((packed));\n+\n+struct bitmap_disk_header {\n+\tchar magic[4];\n+\tuint16_t version;\n+\tuint16_t options;\n+\tuint32_t entry_count;\n+\tunsigned char checksum[20];\n+};\n+\n+static const char BITMAP_IDX_SIGNATURE[] = {'B', 'I', 'T', 'M'};;\n+\n+enum pack_bitmap_opts {\n+\tBITMAP_OPT_FULL_DAG = 1,\n+};\n+\n+typedef int (*show_reachable_fn)(\n+\tconst unsigned char *sha1,\n+\tenum object_type type,\n+\tint flags,\n+\tuint32_t hash,\n+\tstruct packed_git *found_pack,\n+\toff_t found_offset);\n+\n+int prepare_bitmap_git(void);\n+void count_bitmap_commit_list(uint32_t *commits, uint32_t *trees, uint32_t *blobs, uint32_t *tags);\n+void traverse_bitmap_commit_list(show_reachable_fn show_reachable);\n+void test_bitmap_walk(struct rev_info *revs);\n+char *pack_bitmap_filename(struct packed_git *p);\n+int prepare_bitmap_walk(struct rev_info *revs);\n+int reuse_partial_packfile_from_bitmap(struct packed_git **packfile, uint32_t *entries, off_t *up_to);\n+\n+#endif\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229486","messageId":"20131025060352.GI23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 11/19] pack-objects: use bitmaps when packing objects","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:03:52Z","receivedAt":"2013-10-25T06:03:52Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nIn this patch, we use the bitmap API to perform the `Counting Objects`\nphase in pack-objects, rather than a traditional walk through the object\ngraph. For a reasonably-packed large repo, the time to fetch and clone\nis often dominated by the full-object revision walk during the Counting\nObjects phase. Using bitmaps can reduce the CPU time required on the\nserver (and therefore start sending the actual pack data with less\ndelay).\n\nFor bitmaps to be used, the following must be true:\n\n  1. We must be packing to stdout (as a normal `pack-objects` from\n     `upload-pack` would do).\n\n  2. There must be a .bitmap index containing at least one of the\n     \"have\" objects that the client is asking for.\n\n  3. Bitmaps must be enabled (they are enabled by default, but can be\n     disabled by setting `pack.usebitmaps` to false, or by using\n     `--no-use-bitmap-index` on the command-line).\n\nIf any of these is not true, we fall back to doing a normal walk of the\nobject graph.\n\nHere are some sample timings from a full pack of `torvalds/linux` (i.e.\nsomething very similar to what would be generated for a clone of the\nrepository) that show the speedup produced by various\nmethods:\n\n    [existing graph traversal]\n    $ time git pack-objects --all --stdout --no-use-bitmap-index \\\n\t\t\t    </dev/null >/dev/null\n    Counting objects: 3237103, done.\n    Compressing objects: 100% (508752/508752), done.\n    Total 3237103 (delta 2699584), reused 3237103 (delta 2699584)\n\n    real    0m44.111s\n    user    0m42.396s\n    sys     0m3.544s\n\n    [bitmaps only, without partial pack reuse; note that\n     pack reuse is automatic, so timing this required a\n     patch to disable it]\n    $ time git pack-objects --all --stdout </dev/null >/dev/null\n    Counting objects: 3237103, done.\n    Compressing objects: 100% (508752/508752), done.\n    Total 3237103 (delta 2699584), reused 3237103 (delta 2699584)\n\n    real    0m5.413s\n    user    0m5.604s\n    sys     0m1.804s\n\n    [bitmaps with pack reuse (what you get with this patch)]\n    $ time git pack-objects --all --stdout </dev/null >/dev/null\n    Reusing existing pack: 3237103, done.\n    Total 3237103 (delta 0), reused 0 (delta 0)\n\n    real    0m1.636s\n    user    0m1.460s\n    sys     0m0.172s\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/config.txt |   6 ++\n builtin/pack-objects.c   | 169 ++++++++++++++++++++++++++++++++++++++++-------\n 2 files changed, 150 insertions(+), 25 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex ab26963..a981369 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1858,6 +1858,12 @@ pack.packSizeLimit::\n \tCommon unit suffixes of 'k', 'm', or 'g' are\n \tsupported.\n \n+pack.useBitmaps::\n+\tWhen true, git will use pack bitmaps (if available) when packing\n+\tto stdout (e.g., during the server side of a fetch). Defaults to\n+\ttrue. You should not generally need to turn this off unless\n+\tyou are debugging pack bitmaps.\n+\n pager.<cmd>::\n \tIf the value is boolean, turns on or off pagination of the\n \toutput of a particular Git subcommand when writing to a tty.\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex faf746b..d15b9db 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -19,6 +19,7 @@\n #include \"refs.h\"\n #include \"streaming.h\"\n #include \"thread-utils.h\"\n+#include \"pack-bitmap.h\"\n \n static const char *pack_usage[] = {\n \tN_(\"git pack-objects --stdout [options...] [< ref-list | < object-list]\"),\n@@ -57,12 +58,23 @@ static struct progress *progress_state;\n static int pack_compression_level = Z_DEFAULT_COMPRESSION;\n static int pack_compression_seen;\n \n+static struct packed_git *reuse_packfile;\n+static uint32_t reuse_packfile_objects;\n+static off_t reuse_packfile_offset;\n+\n+static int use_bitmap_index = 1;\n+\n static unsigned long delta_cache_size = 0;\n static unsigned long max_delta_cache_size = 256 * 1024 * 1024;\n static unsigned long cache_max_small_delta_size = 1000;\n \n static unsigned long window_memory_limit = 0;\n \n+enum {\n+\tOBJECT_ENTRY_EXCLUDE = (1 << 0),\n+\tOBJECT_ENTRY_NO_TRY_DELTA = (1 << 1)\n+};\n+\n /*\n  * stats\n  */\n@@ -678,6 +690,49 @@ static struct object_entry **compute_write_order(void)\n \treturn wo;\n }\n \n+static off_t write_reused_pack(struct sha1file *f)\n+{\n+\tuint8_t buffer[8192];\n+\toff_t to_write;\n+\tint fd;\n+\n+\tif (!is_pack_valid(reuse_packfile))\n+\t\treturn 0;\n+\n+\tfd = git_open_noatime(reuse_packfile->pack_name);\n+\tif (fd < 0)\n+\t\treturn 0;\n+\n+\tif (lseek(fd, sizeof(struct pack_header), SEEK_SET) == -1) {\n+\t\tclose(fd);\n+\t\treturn 0;\n+\t}\n+\n+\tif (reuse_packfile_offset < 0)\n+\t\treuse_packfile_offset = reuse_packfile->pack_size - 20;\n+\n+\tto_write = reuse_packfile_offset - sizeof(struct pack_header);\n+\n+\twhile (to_write) {\n+\t\tint read_pack = xread(fd, buffer, sizeof(buffer));\n+\n+\t\tif (read_pack <= 0) {\n+\t\t\tclose(fd);\n+\t\t\treturn 0;\n+\t\t}\n+\n+\t\tif (read_pack > to_write)\n+\t\t\tread_pack = to_write;\n+\n+\t\tsha1write(f, buffer, read_pack);\n+\t\tto_write -= read_pack;\n+\t}\n+\n+\tclose(fd);\n+\twritten += reuse_packfile_objects;\n+\treturn reuse_packfile_offset - sizeof(struct pack_header);\n+}\n+\n static void write_pack_file(void)\n {\n \tuint32_t i = 0, j;\n@@ -704,6 +759,18 @@ static void write_pack_file(void)\n \t\toffset = write_pack_header(f, nr_remaining);\n \t\tif (!offset)\n \t\t\tdie_errno(\"unable to write pack header\");\n+\n+\t\tif (reuse_packfile) {\n+\t\t\toff_t packfile_size;\n+\t\t\tassert(pack_to_stdout);\n+\n+\t\t\tpackfile_size = write_reused_pack(f);\n+\t\t\tif (!packfile_size)\n+\t\t\t\tdie_errno(\"failed to re-use existing pack\");\n+\n+\t\t\toffset += packfile_size;\n+\t\t}\n+\n \t\tnr_written = 0;\n \t\tfor (; i < to_pack.nr_objects; i++) {\n \t\t\tstruct object_entry *e = write_order[i];\n@@ -800,14 +867,14 @@ static int no_try_delta(const char *path)\n \treturn 0;\n }\n \n-static int add_object_entry(const unsigned char *sha1, enum object_type type,\n-\t\t\t    const char *name, int exclude)\n+static int add_object_entry_1(const unsigned char *sha1, enum object_type type,\n+\t\t\t      int flags, uint32_t name_hash,\n+\t\t\t      struct packed_git *found_pack, off_t found_offset)\n {\n \tstruct object_entry *entry;\n-\tstruct packed_git *p, *found_pack = NULL;\n-\toff_t found_offset = 0;\n-\tuint32_t hash = pack_name_hash(name);\n+\tstruct packed_git *p;\n \tuint32_t index_pos;\n+\tint exclude = (flags & OBJECT_ENTRY_EXCLUDE);\n \n \tentry = packlist_find(&to_pack, sha1, &index_pos);\n \tif (entry) {\n@@ -822,36 +889,42 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,\n \tif (!exclude && local && has_loose_object_nonlocal(sha1))\n \t\treturn 0;\n \n-\tfor (p = packed_git; p; p = p->next) {\n-\t\toff_t offset = find_pack_entry_one(sha1, p);\n-\t\tif (offset) {\n-\t\t\tif (!found_pack) {\n-\t\t\t\tif (!is_pack_valid(p)) {\n-\t\t\t\t\twarning(\"packfile %s cannot be accessed\", p->pack_name);\n-\t\t\t\t\tcontinue;\n+\tif (!found_pack) {\n+\t\tfor (p = packed_git; p; p = p->next) {\n+\t\t\toff_t offset = find_pack_entry_one(sha1, p);\n+\t\t\tif (offset) {\n+\t\t\t\tif (!found_pack) {\n+\t\t\t\t\tif (!is_pack_valid(p)) {\n+\t\t\t\t\t\twarning(\"packfile %s cannot be accessed\", p->pack_name);\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\t}\n+\t\t\t\t\tfound_offset = offset;\n+\t\t\t\t\tfound_pack = p;\n \t\t\t\t}\n-\t\t\t\tfound_offset = offset;\n-\t\t\t\tfound_pack = p;\n+\t\t\t\tif (exclude)\n+\t\t\t\t\tbreak;\n+\t\t\t\tif (incremental)\n+\t\t\t\t\treturn 0;\n+\t\t\t\tif (local && !p->pack_local)\n+\t\t\t\t\treturn 0;\n+\t\t\t\tif (ignore_packed_keep && p->pack_local && p->pack_keep)\n+\t\t\t\t\treturn 0;\n \t\t\t}\n-\t\t\tif (exclude)\n-\t\t\t\tbreak;\n-\t\t\tif (incremental)\n-\t\t\t\treturn 0;\n-\t\t\tif (local && !p->pack_local)\n-\t\t\t\treturn 0;\n-\t\t\tif (ignore_packed_keep && p->pack_local && p->pack_keep)\n-\t\t\t\treturn 0;\n \t\t}\n \t}\n \n \tentry = packlist_alloc(&to_pack, sha1, index_pos);\n-\tentry->hash = hash;\n+\tentry->hash = name_hash;\n \tif (type)\n \t\tentry->type = type;\n \tif (exclude)\n \t\tentry->preferred_base = 1;\n \telse\n \t\tnr_result++;\n+\n+\tif (flags & OBJECT_ENTRY_NO_TRY_DELTA)\n+\t\tentry->no_try_delta = 1;\n+\n \tif (found_pack) {\n \t\tentry->in_pack = found_pack;\n \t\tentry->in_pack_offset = found_offset;\n@@ -859,10 +932,21 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,\n \n \tdisplay_progress(progress_state, to_pack.nr_objects);\n \n+\treturn 1;\n+}\n+\n+static int add_object_entry(const unsigned char *sha1, enum object_type type,\n+\t\t\t    const char *name, int exclude)\n+{\n+\tint flags = 0;\n+\n+\tif (exclude)\n+\t\tflags |= OBJECT_ENTRY_EXCLUDE;\n+\n \tif (name && no_try_delta(name))\n-\t\tentry->no_try_delta = 1;\n+\t\tflags |= OBJECT_ENTRY_NO_TRY_DELTA;\n \n-\treturn 1;\n+\treturn add_object_entry_1(sha1, type, flags, pack_name_hash(name), NULL, 0);\n }\n \n struct pbase_tree_cache {\n@@ -2027,6 +2111,10 @@ static int git_pack_config(const char *k, const char *v, void *cb)\n \t\tcache_max_small_delta_size = git_config_int(k, v);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(k, \"pack.usebitmaps\")) {\n+\t\tuse_bitmap_index = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(k, \"pack.threads\")) {\n \t\tdelta_search_threads = git_config_int(k, v);\n \t\tif (delta_search_threads < 0)\n@@ -2235,6 +2323,29 @@ static void loosen_unused_packed_objects(struct rev_info *revs)\n \t}\n }\n \n+static int get_object_list_from_bitmap(struct rev_info *revs)\n+{\n+\tif (prepare_bitmap_walk(revs) < 0)\n+\t\treturn -1;\n+\n+\tif (!reuse_partial_packfile_from_bitmap(\n+\t\t\t&reuse_packfile,\n+\t\t\t&reuse_packfile_objects,\n+\t\t\t&reuse_packfile_offset)) {\n+\t\tassert(reuse_packfile_objects);\n+\t\tnr_result += reuse_packfile_objects;\n+\n+\t\tif (progress) {\n+\t\t\tfprintf(stderr, \"Reusing existing pack: %d, done.\\n\",\n+\t\t\t\treuse_packfile_objects);\n+\t\t\tfflush(stderr);\n+\t\t}\n+\t}\n+\n+\ttraverse_bitmap_commit_list(&add_object_entry_1);\n+\treturn 0;\n+}\n+\n static void get_object_list(int ac, const char **av)\n {\n \tstruct rev_info revs;\n@@ -2262,6 +2373,9 @@ static void get_object_list(int ac, const char **av)\n \t\t\tdie(\"bad revision '%s'\", line);\n \t}\n \n+\tif (use_bitmap_index && !get_object_list_from_bitmap(&revs))\n+\t\treturn;\n+\n \tif (prepare_revision_walk(&revs))\n \t\tdie(\"revision walk setup failed\");\n \tmark_edges_uninteresting(&revs, show_edge);\n@@ -2391,6 +2505,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t    N_(\"pack compression level\")),\n \t\tOPT_SET_INT(0, \"keep-true-parents\", &grafts_replace_parents,\n \t\t\t    N_(\"do not hide commits by grafts\"), 0),\n+\t\tOPT_BOOL(0, \"use-bitmap-index\", &use_bitmap_index,\n+\t\t\t N_(\"use a bitmap index if available to speed up counting objects\")),\n \t\tOPT_END(),\n \t};\n \n@@ -2457,6 +2573,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \tif (keep_unreachable && unpack_unreachable)\n \t\tdie(\"--keep-unreachable and --unpack-unreachable are incompatible.\");\n \n+\tif (!use_internal_rev_list || !pack_to_stdout)\n+\t\tuse_bitmap_index = 0;\n+\n \tif (progress && all_progress_implied)\n \t\tprogress = 2;\n \n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229487","messageId":"20131025060411.GJ23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 12/19] rev-list: add bitmap mode to speed up object lists","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:04:11Z","receivedAt":"2013-10-25T06:04:11Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThe bitmap reachability index used to speed up the counting objects\nphase during `pack-objects` can also be used to optimize a normal\nrev-list if the only thing required are the SHA1s of the objects during\nthe list (i.e., not the path names at which trees and blobs were found).\n\nCalling `git rev-list --objects --use-bitmap-index [committish]` will\nperform an object iteration based on a bitmap result instead of actually\nwalking the object graph.\n\nThese are some example timings for `torvalds/linux` (warm cache,\nbest-of-five):\n\n    $ time git rev-list --objects master > /dev/null\n\n    real    0m34.191s\n    user    0m33.904s\n    sys     0m0.268s\n\n    $ time git rev-list --objects --use-bitmap-index master > /dev/null\n\n    real    0m1.041s\n    user    0m0.976s\n    sys     0m0.064s\n\nLikewise, using `git rev-list --count --use-bitmap-index` will speed up\nthe counting operation by building the resulting bitmap and performing a\nfast popcount (number of bits set on the bitmap) on the result.\n\nHere are some sample timings of different ways to count commits in\n`torvalds/linux`:\n\n    $ time git rev-list master | wc -l\n        399882\n\n        real    0m6.524s\n        user    0m6.060s\n        sys     0m3.284s\n\n    $ time git rev-list --count master\n        399882\n\n        real    0m4.318s\n        user    0m4.236s\n        sys     0m0.076s\n\n    $ time git rev-list --use-bitmap-index --count master\n        399882\n\n        real    0m0.217s\n        user    0m0.176s\n        sys     0m0.040s\n\nThis also respects negative refs, so you can use it to count\na slice of history:\n\n        $ time git rev-list --count v3.0..master\n        144843\n\n        real    0m1.971s\n        user    0m1.932s\n        sys     0m0.036s\n\n        $ time git rev-list --use-bitmap-index --count v3.0..master\n        real    0m0.280s\n        user    0m0.220s\n        sys     0m0.056s\n\nThough note that the closer the endpoints, the less it helps. In the\ntraversal case, we have fewer commits to cross, so we take less time.\nBut the bitmap time is dominated by generating the pack revindex, which\nis constant with respect to the refs given.\n\nNote that you cannot yet get a fast --left-right count of a symmetric\ndifference (e.g., \"--count --left-right master...topic\"). The slow part\nof that walk actually happens during the merge-base determination when\nwe parse \"master...topic\". Even though a count does not actually need to\nknow the real merge base (it only needs to take the symmetric difference\nof the bitmaps), the revision code would require some refactoring to\nhandle this case.\n\nAdditionally, a `--test-bitmap` flag has been added that will perform\nthe same rev-list manually (i.e. using a normal revwalk) and using\nbitmaps, and verify that the results are the same. This can be used to\nexercise the bitmap code, and also to verify that the contents of the\n.bitmap file are sane.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/git-rev-list.txt     |  1 +\n Documentation/rev-list-options.txt |  8 ++++++++\n builtin/rev-list.c                 | 39 ++++++++++++++++++++++++++++++++++++++\n 3 files changed, 48 insertions(+)\n\ndiff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt\nindex 045b37b..7a1585d 100644\n--- a/Documentation/git-rev-list.txt\n+++ b/Documentation/git-rev-list.txt\n@@ -55,6 +55,7 @@ SYNOPSIS\n \t     [ \\--reverse ]\n \t     [ \\--walk-reflogs ]\n \t     [ \\--no-walk ] [ \\--do-walk ]\n+\t     [ \\--use-bitmap-index ]\n \t     <commit>... [ \\-- <paths>... ]\n \n DESCRIPTION\ndiff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt\nindex 5bdfb42..c236b85 100644\n--- a/Documentation/rev-list-options.txt\n+++ b/Documentation/rev-list-options.txt\n@@ -274,6 +274,14 @@ See also linkgit:git-reflog[1].\n \tOutput excluded boundary commits. Boundary commits are\n \tprefixed with `-`.\n \n+ifdef::git-rev-list[]\n+--use-bitmap-index::\n+\n+\tTry to speed up the traversal using the pack bitmap index (if\n+\tone is available). Note that when traversing with `--objects`,\n+\ttrees and blobs will not have their associated path printed.\n+endif::git-rev-list[]\n+\n --\n \n History Simplification\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 4fc1616..5209255 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -3,6 +3,8 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"list-objects.h\"\n+#include \"pack.h\"\n+#include \"pack-bitmap.h\"\n #include \"builtin.h\"\n #include \"log-tree.h\"\n #include \"graph.h\"\n@@ -257,6 +259,18 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)\n \treturn 0;\n }\n \n+static int show_object_fast(\n+\tconst unsigned char *sha1,\n+\tenum object_type type,\n+\tint exclude,\n+\tuint32_t name_hash,\n+\tstruct packed_git *found_pack,\n+\toff_t found_offset)\n+{\n+\tfprintf(stdout, \"%s\\n\", sha1_to_hex(sha1));\n+\treturn 1;\n+}\n+\n int cmd_rev_list(int argc, const char **argv, const char *prefix)\n {\n \tstruct rev_info revs;\n@@ -265,6 +279,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tint bisect_list = 0;\n \tint bisect_show_vars = 0;\n \tint bisect_find_all = 0;\n+\tint use_bitmap_index = 0;\n \n \tgit_config(git_default_config, NULL);\n \tinit_revisions(&revs, prefix);\n@@ -306,6 +321,14 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \t\t\tbisect_show_vars = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--use-bitmap-index\")) {\n+\t\t\tuse_bitmap_index = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--test-bitmap\")) {\n+\t\t\ttest_bitmap_walk(&revs);\n+\t\t\treturn 0;\n+\t\t}\n \t\tusage(rev_list_usage);\n \n \t}\n@@ -333,6 +356,22 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tif (bisect_list)\n \t\trevs.limited = 1;\n \n+\tif (use_bitmap_index) {\n+\t\tif (revs.count && !revs.left_right && !revs.cherry_mark) {\n+\t\t\tuint32_t commit_count;\n+\t\t\tif (!prepare_bitmap_walk(&revs)) {\n+\t\t\t\tcount_bitmap_commit_list(&commit_count, NULL, NULL, NULL);\n+\t\t\t\tprintf(\"%d\\n\", commit_count);\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t} else if (revs.tag_objects && revs.tree_objects && revs.blob_objects) {\n+\t\t\tif (!prepare_bitmap_walk(&revs)) {\n+\t\t\t\ttraverse_bitmap_commit_list(&show_object_fast);\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n \tif (prepare_revision_walk(&revs))\n \t\tdie(\"revision walk setup failed\");\n \tif (revs.tree_objects)\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229488","messageId":"20131025060418.GK23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 13/19] pack-objects: implement bitmap writing","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:04:19Z","receivedAt":"2013-10-25T06:04:19Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nThis commit extends more the functionality of `pack-objects` by allowing\nit to write out a `.bitmap` index next to any written packs, together\nwith the `.idx` index that currently gets written.\n\nIf bitmap writing is enabled for a given repository (either by calling\n`pack-objects` with the `--write-bitmap-index` flag or by having\n`pack.writebitmaps` set to `true` in the config) and pack-objects is\nwriting a packfile that would normally be indexed (i.e. not piping to\nstdout), we will attempt to write the corresponding bitmap index for the\npackfile.\n\nBitmap index writing happens after the packfile and its index has been\nsuccessfully written to disk (`finish_tmp_packfile`). The process is\nperformed in several steps:\n\n    1. `bitmap_writer_set_checksum`: this call stores the partial\n       checksum for the packfile being written; the checksum will be\n       written in the resulting bitmap index to verify its integrity\n\n    2. `bitmap_writer_build_type_index`: this call uses the array of\n       `struct object_entry` that has just been sorted when writing out\n       the actual packfile index to disk to generate 4 type-index bitmaps\n       (one for each object type).\n\n       These bitmaps have their nth bit set if the given object is of\n       the bitmap's type. E.g. the nth bit of the Commits bitmap will be\n       1 if the nth object in the packfile index is a commit.\n\n       This is a very cheap operation because the bitmap writing code has\n       access to the metadata stored in the `struct object_entry` array,\n       and hence the real type for each object in the packfile.\n\n    3. `bitmap_writer_reuse_bitmaps`: if there exists an existing bitmap\n       index for one of the packfiles we're trying to repack, this call\n       will efficiently rebuild the existing bitmaps so they can be\n       reused on the new index. All the existing bitmaps will be stored\n       in a `reuse` hash table, and the commit selection phase will\n       prioritize these when selecting, as they can be written directly\n       to the new index without having to perform a revision walk to\n       fill the bitmap. This can greatly speed up the repack of a\n       repository that already has bitmaps.\n\n    4. `bitmap_writer_select_commits`: if bitmap writing is enabled for\n       a given `pack-objects` run, the sequence of commits generated\n       during the Counting Objects phase will be stored in an array.\n\n       We then use that array to build up the list of selected commits.\n       Writing a bitmap in the index for each object in the repository\n       would be cost-prohibitive, so we use a simple heuristic to pick\n       the commits that will be indexed with bitmaps.\n\n       The current heuristics are a simplified version of JGit's\n       original implementation. We select a higher density of commits\n       depending on their age: the 100 most recent commits are always\n       selected, after that we pick 1 commit of each 100, and the gap\n       increases as the commits grow older. On top of that, we make sure\n       that every single branch that has not been merged (all the tips\n       that would be required from a clone) gets their own bitmap, and\n       when selecting commits between a gap, we tend to prioritize the\n       commit with the most parents.\n\n       Do note that there is no right/wrong way to perform commit\n       selection; different selection algorithms will result in\n       different commits being selected, but there's no such thing as\n       \"missing a commit\". The bitmap walker algorithm implemented in\n       `prepare_bitmap_walk` is able to adapt to missing bitmaps by\n       performing manual walks that complete the bitmap: the ideal\n       selection algorithm, however, would select the commits that are\n       more likely to be used as roots for a walk in the future (e.g.\n       the tips of each branch, and so on) to ensure a bitmap for them\n       is always available.\n\n    5. `bitmap_writer_build`: this is the computationally expensive part\n       of bitmap generation. Based on the list of commits that were\n       selected in the previous step, we perform several incremental\n       walks to generate the bitmap for each commit.\n\n       The walks begin from the oldest commit, and are built up\n       incrementally for each branch. E.g. consider this dag where A, B,\n       C, D, E, F are the selected commits, and a, b, c, e are a chunk\n       of simplified history that will not receive bitmaps.\n\n            A---a---B--b--C--c--D\n                     \\\n                      E--e--F\n\n       We start by building the bitmap for A, using A as the root for a\n       revision walk and marking all the objects that are reachable\n       until the walk is over. Once this bitmap is stored, we reuse the\n       bitmap walker to perform the walk for B, assuming that once we\n       reach A again, the walk will be terminated because A has already\n       been SEEN on the previous walk.\n\n       This process is repeated for C, and D, but when we try to\n       generate the bitmaps for E, we can reuse neither the current walk\n       nor the bitmap we have generated so far.\n\n       What we do now is resetting both the walk and clearing the\n       bitmap, and performing the walk from scratch using E as the\n       origin. This new walk, however, does not need to be completed.\n       Once we hit B, we can lookup the bitmap we have already stored\n       for that commit and OR it with the existing bitmap we've composed\n       so far, allowing us to limit the walk early.\n\n       After all the bitmaps have been generated, another iteration\n       through the list of commits is performed to find the best XOR\n       offsets for compression before writing them to disk. Because of\n       the incremental nature of these bitmaps, XORing one of them with\n       its predecesor results in a minimal \"bitmap delta\" most of the\n       time. We can write this delta to the on-disk bitmap index, and\n       then re-compose the original bitmaps by XORing them again when\n       loaded.\n\n       This is a phase very similar to pack-object's `find_delta` (using\n       bitmaps instead of objects, of course), except the heuristics\n       have been greatly simplified: we only check the 10 bitmaps before\n       any given one to find best compressing one. This gives good\n       results in practice, because there is locality in the ordering of\n       the objects (and therefore bitmaps) in the packfile.\n\n     6. `bitmap_writer_finish`: the last step in the process is\n\tserializing to disk all the bitmap data that has been generated\n\tin the two previous steps.\n\n\tThe bitmap is written to a tmp file and then moved atomically to\n\tits final destination, using the same process as\n\t`pack-write.c:write_idx_file`.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/config.txt |   8 +\n Makefile                 |   1 +\n builtin/pack-objects.c   |  53 +++++\n pack-bitmap-write.c      | 535 +++++++++++++++++++++++++++++++++++++++++++++++\n pack-bitmap.c            |  92 ++++++++\n pack-bitmap.h            |  18 ++\n pack-objects.h           |   1 +\n pack-write.c             |   2 +\n 8 files changed, 710 insertions(+)\n create mode 100644 pack-bitmap-write.c\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex a981369..a439a4c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1864,6 +1864,14 @@ pack.useBitmaps::\n \ttrue. You should not generally need to turn this off unless\n \tyou are debugging pack bitmaps.\n \n+pack.writebitmaps::\n+\tWhen true, git will write a bitmap index when packing all\n+\tobjects to disk (e.g., as when `git repack -a` is run).  This\n+\tindex can speed up the \"counting objects\" phase of subsequent\n+\tpacks created for clones and fetches, at the cost of some disk\n+\tspace and extra time spent on the initial repack.  Defaults to\n+\tfalse.\n+\n pager.<cmd>::\n \tIf the value is boolean, turns on or off pagination of the\n \toutput of a particular Git subcommand when writing to a tty.\ndiff --git a/Makefile b/Makefile\nindex 737766c..9ece13b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -839,6 +839,7 @@ LIB_OBJS += notes-merge.o\n LIB_OBJS += notes-utils.o\n LIB_OBJS += object.o\n LIB_OBJS += pack-bitmap.o\n+LIB_OBJS += pack-bitmap-write.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-objects.o\n LIB_OBJS += pack-revindex.o\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex d15b9db..423e85a 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -63,6 +63,7 @@ static uint32_t reuse_packfile_objects;\n static off_t reuse_packfile_offset;\n \n static int use_bitmap_index = 1;\n+static int write_bitmap_index;\n \n static unsigned long delta_cache_size = 0;\n static unsigned long max_delta_cache_size = 256 * 1024 * 1024;\n@@ -81,6 +82,24 @@ enum {\n static uint32_t written, written_delta;\n static uint32_t reused, reused_delta;\n \n+/*\n+ * Indexed commits\n+ */\n+struct commit **indexed_commits;\n+unsigned int indexed_commits_nr;\n+unsigned int indexed_commits_alloc;\n+\n+static void index_commit_for_bitmap(struct commit *commit)\n+{\n+\tif (indexed_commits_nr >= indexed_commits_alloc) {\n+\t\tindexed_commits_alloc = (indexed_commits_alloc + 32) * 2;\n+\t\tindexed_commits = xrealloc(indexed_commits,\n+\t\t\tindexed_commits_alloc * sizeof(struct commit *));\n+\t}\n+\n+\tindexed_commits[indexed_commits_nr++] = commit;\n+}\n+\n static void *get_delta(struct object_entry *entry)\n {\n \tunsigned long size, base_size, delta_size;\n@@ -823,9 +842,30 @@ static void write_pack_file(void)\n \t\t\tif (sizeof(tmpname) <= strlen(base_name) + 50)\n \t\t\t\tdie(\"pack base name '%s' too long\", base_name);\n \t\t\tsnprintf(tmpname, sizeof(tmpname), \"%s-\", base_name);\n+\n+\t\t\tif (write_bitmap_index) {\n+\t\t\t\tbitmap_writer_set_checksum(sha1);\n+\t\t\t\tbitmap_writer_build_type_index(written_list, nr_written);\n+\t\t\t}\n+\n \t\t\tfinish_tmp_packfile(tmpname, pack_tmp_name,\n \t\t\t\t\t    written_list, nr_written,\n \t\t\t\t\t    &pack_idx_opts, sha1);\n+\n+\t\t\tif (write_bitmap_index) {\n+\t\t\t\tchar *end_of_name_prefix = strrchr(tmpname, 0);\n+\t\t\t\tsprintf(end_of_name_prefix, \"%s.bitmap\", sha1_to_hex(sha1));\n+\n+\t\t\t\tstop_progress(&progress_state);\n+\n+\t\t\t\tbitmap_writer_show_progress(progress);\n+\t\t\t\tbitmap_writer_reuse_bitmaps(&to_pack);\n+\t\t\t\tbitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1);\n+\t\t\t\tbitmap_writer_build(&to_pack);\n+\t\t\t\tbitmap_writer_finish(written_list, nr_written, tmpname);\n+\t\t\t\twrite_bitmap_index = 0;\n+\t\t\t}\n+\n \t\t\tfree(pack_tmp_name);\n \t\t\tputs(sha1_to_hex(sha1));\n \t\t}\n@@ -2111,6 +2151,10 @@ static int git_pack_config(const char *k, const char *v, void *cb)\n \t\tcache_max_small_delta_size = git_config_int(k, v);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(k, \"pack.writebitmaps\")) {\n+\t\twrite_bitmap_index = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(k, \"pack.usebitmaps\")) {\n \t\tuse_bitmap_index = git_config_bool(k, v);\n \t\treturn 0;\n@@ -2173,6 +2217,9 @@ static void show_commit(struct commit *commit, void *data)\n {\n \tadd_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);\n \tcommit->object.flags |= OBJECT_ADDED;\n+\n+\tif (write_bitmap_index)\n+\t\tindex_commit_for_bitmap(commit);\n }\n \n static void show_object(struct object *obj,\n@@ -2365,6 +2412,7 @@ static void get_object_list(int ac, const char **av)\n \t\tif (*line == '-') {\n \t\t\tif (!strcmp(line, \"--not\")) {\n \t\t\t\tflags ^= UNINTERESTING;\n+\t\t\t\twrite_bitmap_index = 0;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tdie(\"not a rev '%s'\", line);\n@@ -2507,6 +2555,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t    N_(\"do not hide commits by grafts\"), 0),\n \t\tOPT_BOOL(0, \"use-bitmap-index\", &use_bitmap_index,\n \t\t\t N_(\"use a bitmap index if available to speed up counting objects\")),\n+\t\tOPT_BOOL(0, \"write-bitmap-index\", &write_bitmap_index,\n+\t\t\t N_(\"write a bitmap index together with the pack index\")),\n \t\tOPT_END(),\n \t};\n \n@@ -2576,6 +2626,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \tif (!use_internal_rev_list || !pack_to_stdout)\n \t\tuse_bitmap_index = 0;\n \n+\tif (pack_to_stdout || !rev_list_all)\n+\t\twrite_bitmap_index = 0;\n+\n \tif (progress && all_progress_implied)\n \t\tprogress = 2;\n \ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nnew file mode 100644\nindex 0000000..954a74d\n--- /dev/null\n+++ b/pack-bitmap-write.c\n@@ -0,0 +1,535 @@\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"tag.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n+#include \"list-objects.h\"\n+#include \"progress.h\"\n+#include \"pack-revindex.h\"\n+#include \"pack.h\"\n+#include \"pack-bitmap.h\"\n+#include \"sha1-lookup.h\"\n+#include \"pack-objects.h\"\n+\n+struct bitmapped_commit {\n+\tstruct commit *commit;\n+\tstruct ewah_bitmap *bitmap;\n+\tstruct ewah_bitmap *write_as;\n+\tint flags;\n+\tint xor_offset;\n+\tuint32_t commit_pos;\n+};\n+\n+struct bitmap_writer {\n+\tstruct ewah_bitmap *commits;\n+\tstruct ewah_bitmap *trees;\n+\tstruct ewah_bitmap *blobs;\n+\tstruct ewah_bitmap *tags;\n+\n+\tkhash_sha1 *bitmaps;\n+\tkhash_sha1 *reused;\n+\tstruct packing_data *to_pack;\n+\n+\tstruct bitmapped_commit *selected;\n+\tunsigned int selected_nr, selected_alloc;\n+\n+\tstruct progress *progress;\n+\tint show_progress;\n+\tunsigned char pack_checksum[20];\n+};\n+\n+static struct bitmap_writer writer;\n+\n+void bitmap_writer_show_progress(int show)\n+{\n+\twriter.show_progress = show;\n+}\n+\n+/**\n+ * Build the initial type index for the packfile\n+ */\n+void bitmap_writer_build_type_index(struct pack_idx_entry **index,\n+\t\t\t\t    uint32_t index_nr)\n+{\n+\tuint32_t i;\n+\n+\twriter.commits = ewah_new();\n+\twriter.trees = ewah_new();\n+\twriter.blobs = ewah_new();\n+\twriter.tags = ewah_new();\n+\n+\tfor (i = 0; i < index_nr; ++i) {\n+\t\tstruct object_entry *entry = (struct object_entry *)index[i];\n+\t\tenum object_type real_type;\n+\n+\t\tentry->in_pack_pos = i;\n+\n+\t\tswitch (entry->type) {\n+\t\tcase OBJ_COMMIT:\n+\t\tcase OBJ_TREE:\n+\t\tcase OBJ_BLOB:\n+\t\tcase OBJ_TAG:\n+\t\t\treal_type = entry->type;\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\treal_type = sha1_object_info(entry->idx.sha1, NULL);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tswitch (real_type) {\n+\t\tcase OBJ_COMMIT:\n+\t\t\tewah_set(writer.commits, i);\n+\t\t\tbreak;\n+\n+\t\tcase OBJ_TREE:\n+\t\t\tewah_set(writer.trees, i);\n+\t\t\tbreak;\n+\n+\t\tcase OBJ_BLOB:\n+\t\t\tewah_set(writer.blobs, i);\n+\t\t\tbreak;\n+\n+\t\tcase OBJ_TAG:\n+\t\t\tewah_set(writer.tags, i);\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tdie(\"Missing type information for %s (%d/%d)\",\n+\t\t\t    sha1_to_hex(entry->idx.sha1), real_type, entry->type);\n+\t\t}\n+\t}\n+}\n+\n+/**\n+ * Compute the actual bitmaps\n+ */\n+static struct object **seen_objects;\n+static unsigned int seen_objects_nr, seen_objects_alloc;\n+\n+static inline void push_bitmapped_commit(struct commit *commit, struct ewah_bitmap *reused)\n+{\n+\tif (writer.selected_nr >= writer.selected_alloc) {\n+\t\twriter.selected_alloc = (writer.selected_alloc + 32) * 2;\n+\t\twriter.selected = xrealloc(writer.selected,\n+\t\t\t\t\t   writer.selected_alloc * sizeof(struct bitmapped_commit));\n+\t}\n+\n+\twriter.selected[writer.selected_nr].commit = commit;\n+\twriter.selected[writer.selected_nr].bitmap = reused;\n+\twriter.selected[writer.selected_nr].flags = 0;\n+\n+\twriter.selected_nr++;\n+}\n+\n+static inline void mark_as_seen(struct object *object)\n+{\n+\tALLOC_GROW(seen_objects, seen_objects_nr + 1, seen_objects_alloc);\n+\tseen_objects[seen_objects_nr++] = object;\n+}\n+\n+static inline void reset_all_seen(void)\n+{\n+\tunsigned int i;\n+\tfor (i = 0; i < seen_objects_nr; ++i) {\n+\t\tseen_objects[i]->flags &= ~(SEEN | ADDED | SHOWN);\n+\t}\n+\tseen_objects_nr = 0;\n+}\n+\n+static uint32_t find_object_pos(const unsigned char *sha1)\n+{\n+\tstruct object_entry *entry = packlist_find(writer.to_pack, sha1, NULL);\n+\n+\tif (!entry) {\n+\t\tdie(\"Failed to write bitmap index. Packfile doesn't have full closure \"\n+\t\t\t\"(object %s is missing)\", sha1_to_hex(sha1));\n+\t}\n+\n+\treturn entry->in_pack_pos;\n+}\n+\n+static void show_object(struct object *object, const struct name_path *path,\n+\t\t\tconst char *last, void *data)\n+{\n+\tstruct bitmap *base = data;\n+\tbitmap_set(base, find_object_pos(object->sha1));\n+\tmark_as_seen(object);\n+}\n+\n+static void show_commit(struct commit *commit, void *data)\n+{\n+\tmark_as_seen((struct object *)commit);\n+}\n+\n+static int\n+add_to_include_set(struct bitmap *base, struct commit *commit)\n+{\n+\tkhiter_t hash_pos;\n+\tuint32_t bitmap_pos = find_object_pos(commit->object.sha1);\n+\n+\tif (bitmap_get(base, bitmap_pos))\n+\t\treturn 0;\n+\n+\thash_pos = kh_get_sha1(writer.bitmaps, commit->object.sha1);\n+\tif (hash_pos < kh_end(writer.bitmaps)) {\n+\t\tstruct bitmapped_commit *bc = kh_value(writer.bitmaps, hash_pos);\n+\t\tbitmap_or_ewah(base, bc->bitmap);\n+\t\treturn 0;\n+\t}\n+\n+\tbitmap_set(base, bitmap_pos);\n+\treturn 1;\n+}\n+\n+static int\n+should_include(struct commit *commit, void *_data)\n+{\n+\tstruct bitmap *base = _data;\n+\n+\tif (!add_to_include_set(base, commit)) {\n+\t\tstruct commit_list *parent = commit->parents;\n+\n+\t\tmark_as_seen((struct object *)commit);\n+\n+\t\twhile (parent) {\n+\t\t\tparent->item->object.flags |= SEEN;\n+\t\t\tmark_as_seen((struct object *)parent->item);\n+\t\t\tparent = parent->next;\n+\t\t}\n+\n+\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+static void compute_xor_offsets(void)\n+{\n+\tstatic const int MAX_XOR_OFFSET_SEARCH = 10;\n+\n+\tint i, next = 0;\n+\n+\twhile (next < writer.selected_nr) {\n+\t\tstruct bitmapped_commit *stored = &writer.selected[next];\n+\n+\t\tint best_offset = 0;\n+\t\tstruct ewah_bitmap *best_bitmap = stored->bitmap;\n+\t\tstruct ewah_bitmap *test_xor;\n+\n+\t\tfor (i = 1; i <= MAX_XOR_OFFSET_SEARCH; ++i) {\n+\t\t\tint curr = next - i;\n+\n+\t\t\tif (curr < 0)\n+\t\t\t\tbreak;\n+\n+\t\t\ttest_xor = ewah_pool_new();\n+\t\t\tewah_xor(writer.selected[curr].bitmap, stored->bitmap, test_xor);\n+\n+\t\t\tif (test_xor->buffer_size < best_bitmap->buffer_size) {\n+\t\t\t\tif (best_bitmap != stored->bitmap)\n+\t\t\t\t\tewah_pool_free(best_bitmap);\n+\n+\t\t\t\tbest_bitmap = test_xor;\n+\t\t\t\tbest_offset = i;\n+\t\t\t} else {\n+\t\t\t\tewah_pool_free(test_xor);\n+\t\t\t}\n+\t\t}\n+\n+\t\tstored->xor_offset = best_offset;\n+\t\tstored->write_as = best_bitmap;\n+\n+\t\tnext++;\n+\t}\n+}\n+\n+void bitmap_writer_build(struct packing_data *to_pack)\n+{\n+\tstatic const double REUSE_BITMAP_THRESHOLD = 0.2;\n+\n+\tint i, reuse_after, need_reset;\n+\tstruct bitmap *base = bitmap_new();\n+\tstruct rev_info revs;\n+\n+\twriter.bitmaps = kh_init_sha1();\n+\twriter.to_pack = to_pack;\n+\n+\tif (writer.show_progress)\n+\t\twriter.progress = start_progress(\"Building bitmaps\", writer.selected_nr);\n+\n+\tinit_revisions(&revs, NULL);\n+\trevs.tag_objects = 1;\n+\trevs.tree_objects = 1;\n+\trevs.blob_objects = 1;\n+\trevs.no_walk = 0;\n+\n+\trevs.include_check = should_include;\n+\treset_revision_walk();\n+\n+\treuse_after = writer.selected_nr * REUSE_BITMAP_THRESHOLD;\n+\tneed_reset = 0;\n+\n+\tfor (i = writer.selected_nr - 1; i >= 0; --i) {\n+\t\tstruct bitmapped_commit *stored;\n+\t\tstruct object *object;\n+\n+\t\tkhiter_t hash_pos;\n+\t\tint hash_ret;\n+\n+\t\tstored = &writer.selected[i];\n+\t\tobject = (struct object *)stored->commit;\n+\n+\t\tif (stored->bitmap == NULL) {\n+\t\t\tif (i < writer.selected_nr - 1 &&\n+\t\t\t    (need_reset ||\n+\t\t\t     !in_merge_bases(writer.selected[i + 1].commit,\n+\t\t\t\t\t     stored->commit))) {\n+\t\t\t    bitmap_reset(base);\n+\t\t\t    reset_all_seen();\n+\t\t\t}\n+\n+\t\t\tadd_pending_object(&revs, object, \"\");\n+\t\t\trevs.include_check_data = base;\n+\n+\t\t\tif (prepare_revision_walk(&revs))\n+\t\t\t\tdie(\"revision walk setup failed\");\n+\n+\t\t\ttraverse_commit_list(&revs, show_commit, show_object, base);\n+\n+\t\t\trevs.pending.nr = 0;\n+\t\t\trevs.pending.alloc = 0;\n+\t\t\trevs.pending.objects = NULL;\n+\n+\t\t\tstored->bitmap = bitmap_to_ewah(base);\n+\t\t\tneed_reset = 0;\n+\t\t} else\n+\t\t\tneed_reset = 1;\n+\n+\t\tif (i >= reuse_after)\n+\t\t\tstored->flags |= BITMAP_FLAG_REUSE;\n+\n+\t\thash_pos = kh_put_sha1(writer.bitmaps, object->sha1, &hash_ret);\n+\t\tif (hash_ret == 0)\n+\t\t\tdie(\"Duplicate entry when writing index: %s\",\n+\t\t\t    sha1_to_hex(object->sha1));\n+\n+\t\tkh_value(writer.bitmaps, hash_pos) = stored;\n+\t\tdisplay_progress(writer.progress, writer.selected_nr - i);\n+\t}\n+\n+\tbitmap_free(base);\n+\tstop_progress(&writer.progress);\n+\n+\tcompute_xor_offsets();\n+}\n+\n+/**\n+ * Select the commits that will be bitmapped\n+ */\n+static inline unsigned int next_commit_index(unsigned int idx)\n+{\n+\tstatic const unsigned int MIN_COMMITS = 100;\n+\tstatic const unsigned int MAX_COMMITS = 5000;\n+\n+\tstatic const unsigned int MUST_REGION = 100;\n+\tstatic const unsigned int MIN_REGION = 20000;\n+\n+\tunsigned int offset, next;\n+\n+\tif (idx <= MUST_REGION)\n+\t\treturn 0;\n+\n+\tif (idx <= MIN_REGION) {\n+\t\toffset = idx - MUST_REGION;\n+\t\treturn (offset < MIN_COMMITS) ? offset : MIN_COMMITS;\n+\t}\n+\n+\toffset = idx - MIN_REGION;\n+\tnext = (offset < MAX_COMMITS) ? offset : MAX_COMMITS;\n+\n+\treturn (next > MIN_COMMITS) ? next : MIN_COMMITS;\n+}\n+\n+static int date_compare(const void *_a, const void *_b)\n+{\n+\tstruct commit *a = *(struct commit **)_a;\n+\tstruct commit *b = *(struct commit **)_b;\n+\treturn (long)b->date - (long)a->date;\n+}\n+\n+void bitmap_writer_reuse_bitmaps(struct packing_data *to_pack)\n+{\n+\tif (prepare_bitmap_git() < 0)\n+\t\treturn;\n+\n+\twriter.reused = kh_init_sha1();\n+\trebuild_existing_bitmaps(to_pack, writer.reused, writer.show_progress);\n+}\n+\n+static struct ewah_bitmap *find_reused_bitmap(const unsigned char *sha1)\n+{\n+\tkhiter_t hash_pos;\n+\n+\tif (!writer.reused)\n+\t\treturn NULL;\n+\n+\thash_pos = kh_get_sha1(writer.reused, sha1);\n+\tif (hash_pos >= kh_end(writer.reused))\n+\t\treturn NULL;\n+\n+\treturn kh_value(writer.reused, hash_pos);\n+}\n+\n+void bitmap_writer_select_commits(struct commit **indexed_commits,\n+\t\t\t\t  unsigned int indexed_commits_nr,\n+\t\t\t\t  int max_bitmaps)\n+{\n+\tunsigned int i = 0, j, next;\n+\n+\tqsort(indexed_commits, indexed_commits_nr, sizeof(indexed_commits[0]),\n+\t      date_compare);\n+\n+\tif (writer.show_progress)\n+\t\twriter.progress = start_progress(\"Selecting bitmap commits\", 0);\n+\n+\tif (indexed_commits_nr < 100) {\n+\t\tfor (i = 0; i < indexed_commits_nr; ++i)\n+\t\t\tpush_bitmapped_commit(indexed_commits[i], NULL);\n+\t\treturn;\n+\t}\n+\n+\tfor (;;) {\n+\t\tstruct ewah_bitmap *reused_bitmap = NULL;\n+\t\tstruct commit *chosen = NULL;\n+\n+\t\tnext = next_commit_index(i);\n+\n+\t\tif (i + next >= indexed_commits_nr)\n+\t\t\tbreak;\n+\n+\t\tif (max_bitmaps > 0 && writer.selected_nr >= max_bitmaps) {\n+\t\t\twriter.selected_nr = max_bitmaps;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (next == 0) {\n+\t\t\tchosen = indexed_commits[i];\n+\t\t\treused_bitmap = find_reused_bitmap(chosen->object.sha1);\n+\t\t} else {\n+\t\t\tchosen = indexed_commits[i + next];\n+\n+\t\t\tfor (j = 0; j <= next; ++j) {\n+\t\t\t\tstruct commit *cm = indexed_commits[i + j];\n+\n+\t\t\t\treused_bitmap = find_reused_bitmap(cm->object.sha1);\n+\t\t\t\tif (reused_bitmap || (cm->object.flags & NEEDS_BITMAP) != 0) {\n+\t\t\t\t\tchosen = cm;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\n+\t\t\t\tif (cm->parents && cm->parents->next)\n+\t\t\t\t\tchosen = cm;\n+\t\t\t}\n+\t\t}\n+\n+\t\tpush_bitmapped_commit(chosen, reused_bitmap);\n+\n+\t\ti += next + 1;\n+\t\tdisplay_progress(writer.progress, i);\n+\t}\n+\n+\tstop_progress(&writer.progress);\n+}\n+\n+\n+static int sha1write_ewah_helper(void *f, const void *buf, size_t len)\n+{\n+\t/* sha1write will die on error */\n+\tsha1write(f, buf, len);\n+\treturn len;\n+}\n+\n+/**\n+ * Write the bitmap index to disk\n+ */\n+static inline void dump_bitmap(struct sha1file *f, struct ewah_bitmap *bitmap)\n+{\n+\tif (ewah_serialize_to(bitmap, sha1write_ewah_helper, f) < 0)\n+\t\tdie(\"Failed to write bitmap index\");\n+}\n+\n+static const unsigned char *sha1_access(size_t pos, void *table)\n+{\n+\tstruct pack_idx_entry **index = table;\n+\treturn index[pos]->sha1;\n+}\n+\n+static void write_selected_commits_v1(struct sha1file *f,\n+\t\t\t\t      struct pack_idx_entry **index,\n+\t\t\t\t      uint32_t index_nr)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < writer.selected_nr; ++i) {\n+\t\tstruct bitmapped_commit *stored = &writer.selected[i];\n+\t\tstruct bitmap_disk_entry on_disk;\n+\n+\t\tint commit_pos =\n+\t\t\tsha1_pos(stored->commit->object.sha1, index, index_nr, sha1_access);\n+\n+\t\tif (commit_pos < 0)\n+\t\t\tdie(\"BUG: trying to write commit not in index\");\n+\n+\t\ton_disk.object_pos = htonl(commit_pos);\n+\t\ton_disk.xor_offset = stored->xor_offset;\n+\t\ton_disk.flags = stored->flags;\n+\n+\t\tsha1write(f, &on_disk, sizeof(on_disk));\n+\t\tdump_bitmap(f, stored->write_as);\n+\t}\n+}\n+\n+void bitmap_writer_set_checksum(unsigned char *sha1)\n+{\n+\thashcpy(writer.pack_checksum, sha1);\n+}\n+\n+void bitmap_writer_finish(struct pack_idx_entry **index,\n+\t\t\t  uint32_t index_nr,\n+\t\t\t  const char *filename)\n+{\n+\tstatic char tmp_file[PATH_MAX];\n+\tstatic uint16_t default_version = 1;\n+\tstatic uint16_t flags = BITMAP_OPT_FULL_DAG;\n+\tstruct sha1file *f;\n+\n+\tstruct bitmap_disk_header header;\n+\n+\tint fd = odb_mkstemp(tmp_file, sizeof(tmp_file), \"pack/tmp_bitmap_XXXXXX\");\n+\n+\tif (fd < 0)\n+\t\tdie_errno(\"unable to create '%s'\", tmp_file);\n+\tf = sha1fd(fd, tmp_file);\n+\n+\tmemcpy(header.magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE));\n+\theader.version = htons(default_version);\n+\theader.options = htons(flags);\n+\theader.entry_count = htonl(writer.selected_nr);\n+\tmemcpy(header.checksum, writer.pack_checksum, 20);\n+\n+\tsha1write(f, &header, sizeof(header));\n+\tdump_bitmap(f, writer.commits);\n+\tdump_bitmap(f, writer.trees);\n+\tdump_bitmap(f, writer.blobs);\n+\tdump_bitmap(f, writer.tags);\n+\twrite_selected_commits_v1(f, index, index_nr);\n+\n+\tsha1close(f, NULL, CSUM_FSYNC);\n+\n+\tif (adjust_shared_perm(tmp_file))\n+\t\tdie_errno(\"unable to make temporary bitmap file readable\");\n+\n+\tif (rename(tmp_file, filename))\n+\t\tdie_errno(\"unable to rename temporary bitmap file to '%s'\", filename);\n+}\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex 73c52fd..0044ecc 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -963,3 +963,95 @@ void test_bitmap_walk(struct rev_info *revs)\n \telse\n \t\tfprintf(stderr, \"Mismatch!\\n\");\n }\n+\n+static int rebuild_bitmap(uint32_t *reposition,\n+\t\t\t  struct ewah_bitmap *source,\n+\t\t\t  struct bitmap *dest)\n+{\n+\tuint32_t pos = 0;\n+\tstruct ewah_iterator it;\n+\teword_t word;\n+\n+\tewah_iterator_init(&it, source);\n+\n+\twhile (ewah_iterator_next(&word, &it)) {\n+\t\tuint32_t offset, bit_pos;\n+\n+\t\tfor (offset = 0; offset < BITS_IN_WORD; ++offset) {\n+\t\t\tif ((word >> offset) == 0)\n+\t\t\t\tbreak;\n+\n+\t\t\toffset += ewah_bit_ctz64(word >> offset);\n+\n+\t\t\tbit_pos = reposition[pos + offset];\n+\t\t\tif (bit_pos > 0)\n+\t\t\t\tbitmap_set(dest, bit_pos - 1);\n+\t\t\telse /* can't reuse, we don't have the object */\n+\t\t\t\treturn -1;\n+\t\t}\n+\n+\t\tpos += BITS_IN_WORD;\n+\t}\n+\treturn 0;\n+}\n+\n+int rebuild_existing_bitmaps(struct packing_data *mapping,\n+\t\t\t     khash_sha1 *reused_bitmaps,\n+\t\t\t     int show_progress)\n+{\n+\tuint32_t i, num_objects;\n+\tuint32_t *reposition;\n+\tstruct bitmap *rebuild;\n+\tstruct stored_bitmap *stored;\n+\tstruct progress *progress = NULL;\n+\n+\tkhiter_t hash_pos;\n+\tint hash_ret;\n+\n+\tif (prepare_bitmap_git() < 0)\n+\t\treturn -1;\n+\n+\tnum_objects = bitmap_git.pack->num_objects;\n+\treposition = xcalloc(num_objects, sizeof(uint32_t));\n+\n+\tfor (i = 0; i < num_objects; ++i) {\n+\t\tconst unsigned char *sha1;\n+\t\tstruct revindex_entry *entry;\n+\t\tstruct object_entry *oe;\n+\n+\t\tentry = &bitmap_git.reverse_index->revindex[i];\n+\t\tsha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);\n+\t\toe = packlist_find(mapping, sha1, NULL);\n+\n+\t\tif (oe)\n+\t\t\treposition[i] = oe->in_pack_pos + 1;\n+\t}\n+\n+\trebuild = bitmap_new();\n+\ti = 0;\n+\n+\tif (show_progress)\n+\t\tprogress = start_progress(\"Reusing bitmaps\", 0);\n+\n+\tkh_foreach_value(bitmap_git.bitmaps, stored, {\n+\t\tif (stored->flags & BITMAP_FLAG_REUSE) {\n+\t\t\tif (!rebuild_bitmap(reposition,\n+\t\t\t\t\t    lookup_stored_bitmap(stored),\n+\t\t\t\t\t    rebuild)) {\n+\t\t\t\thash_pos = kh_put_sha1(reused_bitmaps,\n+\t\t\t\t\t\t       stored->sha1,\n+\t\t\t\t\t\t       &hash_ret);\n+\t\t\t\tkh_value(reused_bitmaps, hash_pos) =\n+\t\t\t\t\tbitmap_to_ewah(rebuild);\n+\t\t\t}\n+\t\t\tbitmap_reset(rebuild);\n+\t\t\tdisplay_progress(progress, ++i);\n+\t\t}\n+\t});\n+\n+\tstop_progress(&progress);\n+\n+\tfree(reposition);\n+\tbitmap_free(rebuild);\n+\treturn 0;\n+}\ndiff --git a/pack-bitmap.h b/pack-bitmap.h\nindex df45578..18f4d4c 100644\n--- a/pack-bitmap.h\n+++ b/pack-bitmap.h\n@@ -6,6 +6,7 @@\n #define ewah_realloc xrealloc\n #include \"ewah/ewok.h\"\n #include \"khash.h\"\n+#include \"pack-objects.h\"\n \n struct bitmap_disk_entry {\n \tuint32_t object_pos;\n@@ -23,10 +24,16 @@ struct bitmap_disk_header {\n \n static const char BITMAP_IDX_SIGNATURE[] = {'B', 'I', 'T', 'M'};;\n \n+#define NEEDS_BITMAP (1u<<22)\n+\n enum pack_bitmap_opts {\n \tBITMAP_OPT_FULL_DAG = 1,\n };\n \n+enum pack_bitmap_flags {\n+\tBITMAP_FLAG_REUSE = 0x1\n+};\n+\n typedef int (*show_reachable_fn)(\n \tconst unsigned char *sha1,\n \tenum object_type type,\n@@ -42,5 +49,16 @@ void test_bitmap_walk(struct rev_info *revs);\n char *pack_bitmap_filename(struct packed_git *p);\n int prepare_bitmap_walk(struct rev_info *revs);\n int reuse_partial_packfile_from_bitmap(struct packed_git **packfile, uint32_t *entries, off_t *up_to);\n+int rebuild_existing_bitmaps(struct packing_data *mapping, khash_sha1 *reused_bitmaps, int show_progress);\n+\n+void bitmap_writer_show_progress(int show);\n+void bitmap_writer_set_checksum(unsigned char *sha1);\n+void bitmap_writer_build_type_index(struct pack_idx_entry **index, uint32_t index_nr);\n+void bitmap_writer_reuse_bitmaps(struct packing_data *to_pack);\n+void bitmap_writer_select_commits(struct commit **indexed_commits,\n+\t\tunsigned int indexed_commits_nr, int max_bitmaps);\n+void bitmap_writer_build(struct packing_data *to_pack);\n+void bitmap_writer_finish(\n+\t struct pack_idx_entry **index, uint32_t index_nr, const char *filename);\n \n #endif\ndiff --git a/pack-objects.h b/pack-objects.h\nindex 90ad0a8..d1b98b3 100644\n--- a/pack-objects.h\n+++ b/pack-objects.h\n@@ -17,6 +17,7 @@ struct object_entry {\n \tenum object_type type;\n \tenum object_type in_pack_type;\t/* could be delta */\n \tuint32_t hash;\t\t\t/* name hint hash */\n+\tunsigned int in_pack_pos;\n \tunsigned char in_pack_header_size;\n \tunsigned preferred_base:1; /*\n \t\t\t\t    * we do not pack this, but is available\ndiff --git a/pack-write.c b/pack-write.c\nindex ca9e63b..6203d37 100644\n--- a/pack-write.c\n+++ b/pack-write.c\n@@ -371,5 +371,7 @@ void finish_tmp_packfile(char *name_buffer,\n \tif (rename(idx_tmp_name, name_buffer))\n \t\tdie_errno(\"unable to rename temporary index file\");\n \n+\t*end_of_name_prefix = '\\0';\n+\n \tfree((void *)idx_tmp_name);\n }\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229489","messageId":"20131025060422.GL23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 14/19] repack: stop using magic number for ARRAY_SIZE(exts)","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:04:23Z","receivedAt":"2013-10-25T06:04:23Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We have a static array of extensions, but hardcode the size\nof the array in our loops. Let's pull out this magic number,\nwhich will make it easier to change.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/repack.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex a0ff5c7..2e88975 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -115,7 +115,7 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name)\n \n int cmd_repack(int argc, const char **argv, const char *prefix)\n {\n-\tconst char *exts[2] = {\".pack\", \".idx\"};\n+\tconst char *exts[] = {\".pack\", \".idx\"};\n \tstruct child_process cmd;\n \tstruct string_list_item *item;\n \tstruct argv_array cmd_args = ARGV_ARRAY_INIT;\n@@ -258,7 +258,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t */\n \tfailed = 0;\n \tfor_each_string_list_item(item, &names) {\n-\t\tfor (ext = 0; ext < 2; ext++) {\n+\t\tfor (ext = 0; ext < ARRAY_SIZE(exts); ext++) {\n \t\t\tchar *fname, *fname_old;\n \t\t\tfname = mkpathdup(\"%s/%s%s\", packdir,\n \t\t\t\t\t\titem->string, exts[ext]);\n@@ -315,7 +315,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \n \t/* Now the ones with the same name are out of the way... */\n \tfor_each_string_list_item(item, &names) {\n-\t\tfor (ext = 0; ext < 2; ext++) {\n+\t\tfor (ext = 0; ext < ARRAY_SIZE(exts); ext++) {\n \t\t\tchar *fname, *fname_old;\n \t\t\tstruct stat statbuffer;\n \t\t\tfname = mkpathdup(\"%s/pack-%s%s\",\n@@ -335,7 +335,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \n \t/* Remove the \"old-\" files */\n \tfor_each_string_list_item(item, &names) {\n-\t\tfor (ext = 0; ext < 2; ext++) {\n+\t\tfor (ext = 0; ext < ARRAY_SIZE(exts); ext++) {\n \t\t\tchar *fname;\n \t\t\tfname = mkpath(\"%s/old-pack-%s%s\",\n \t\t\t\t\tpackdir,\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229493","messageId":"20131025060426.GM23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 15/19] repack: turn exts array into array-of-struct","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:04:26Z","receivedAt":"2013-10-25T06:04:26Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This is slightly more verbose, but will let us annotate the\nextensions with further options in future commits.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/repack.c | 17 +++++++++++------\n 1 file changed, 11 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex 2e88975..a176de2 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -115,7 +115,12 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name)\n \n int cmd_repack(int argc, const char **argv, const char *prefix)\n {\n-\tconst char *exts[] = {\".pack\", \".idx\"};\n+\tstruct {\n+\t\tconst char *name;\n+\t} exts[] = {\n+\t\t{\".pack\"},\n+\t\t{\".idx\"},\n+\t};\n \tstruct child_process cmd;\n \tstruct string_list_item *item;\n \tstruct argv_array cmd_args = ARGV_ARRAY_INIT;\n@@ -261,14 +266,14 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\tfor (ext = 0; ext < ARRAY_SIZE(exts); ext++) {\n \t\t\tchar *fname, *fname_old;\n \t\t\tfname = mkpathdup(\"%s/%s%s\", packdir,\n-\t\t\t\t\t\titem->string, exts[ext]);\n+\t\t\t\t\t\titem->string, exts[ext].name);\n \t\t\tif (!file_exists(fname)) {\n \t\t\t\tfree(fname);\n \t\t\t\tcontinue;\n \t\t\t}\n \n \t\t\tfname_old = mkpath(\"%s/old-%s%s\", packdir,\n-\t\t\t\t\t\titem->string, exts[ext]);\n+\t\t\t\t\t\titem->string, exts[ext].name);\n \t\t\tif (file_exists(fname_old))\n \t\t\t\tif (unlink(fname_old))\n \t\t\t\t\tfailed = 1;\n@@ -319,9 +324,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\t\tchar *fname, *fname_old;\n \t\t\tstruct stat statbuffer;\n \t\t\tfname = mkpathdup(\"%s/pack-%s%s\",\n-\t\t\t\t\tpackdir, item->string, exts[ext]);\n+\t\t\t\t\tpackdir, item->string, exts[ext].name);\n \t\t\tfname_old = mkpathdup(\"%s-%s%s\",\n-\t\t\t\t\tpacktmp, item->string, exts[ext]);\n+\t\t\t\t\tpacktmp, item->string, exts[ext].name);\n \t\t\tif (!stat(fname_old, &statbuffer)) {\n \t\t\t\tstatbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);\n \t\t\t\tchmod(fname_old, statbuffer.st_mode);\n@@ -340,7 +345,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\t\tfname = mkpath(\"%s/old-pack-%s%s\",\n \t\t\t\t\tpackdir,\n \t\t\t\t\titem->string,\n-\t\t\t\t\texts[ext]);\n+\t\t\t\t\texts[ext].name);\n \t\t\tif (remove_path(fname))\n \t\t\t\twarning(_(\"removing '%s' failed\"), fname);\n \t\t}\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229490","messageId":"20131025060430.GN23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 16/19] repack: handle optional files created by pack-objects","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:04:31Z","receivedAt":"2013-10-25T06:04:31Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We ask pack-objects to pack to a set of temporary files, and\nthen rename them into place. Some files that pack-objects\ncreates may be optional (like a .bitmap file), in which case\nwe would not want to call rename(). We already call stat()\nand make the chmod optional if the file cannot be accessed.\nWe could simply skip the rename step in this case, but that\nwould be a minor regression in noticing problems with\nnon-optional files (like the .pack and .idx files).\n\nInstead, we can now annotate extensions as optional, and\nskip them if they don't exist (and otherwise rely on\nrename() to barf).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/repack.c | 9 +++++++--\n 1 file changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex a176de2..8b7dfd0 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -117,6 +117,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n {\n \tstruct {\n \t\tconst char *name;\n+\t\tunsigned optional:1;\n \t} exts[] = {\n \t\t{\".pack\"},\n \t\t{\".idx\"},\n@@ -323,6 +324,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\tfor (ext = 0; ext < ARRAY_SIZE(exts); ext++) {\n \t\t\tchar *fname, *fname_old;\n \t\t\tstruct stat statbuffer;\n+\t\t\tint exists = 0;\n \t\t\tfname = mkpathdup(\"%s/pack-%s%s\",\n \t\t\t\t\tpackdir, item->string, exts[ext].name);\n \t\t\tfname_old = mkpathdup(\"%s-%s%s\",\n@@ -330,9 +332,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\t\tif (!stat(fname_old, &statbuffer)) {\n \t\t\t\tstatbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);\n \t\t\t\tchmod(fname_old, statbuffer.st_mode);\n+\t\t\t\texists = 1;\n+\t\t\t}\n+\t\t\tif (exists || !exts[ext].optional) {\n+\t\t\t\tif (rename(fname_old, fname))\n+\t\t\t\t\tdie_errno(_(\"renaming '%s' failed\"), fname_old);\n \t\t\t}\n-\t\t\tif (rename(fname_old, fname))\n-\t\t\t\tdie_errno(_(\"renaming '%s' failed\"), fname_old);\n \t\t\tfree(fname);\n \t\t\tfree(fname_old);\n \t\t}\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229492","messageId":"20131025060434.GO23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 17/19] repack: consider bitmaps when performing repacks","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:04:34Z","receivedAt":"2013-10-25T06:04:34Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nSince `pack-objects` will write a `.bitmap` file next to the `.pack` and\n`.idx` files, this commit teaches `git-repack` to consider the new\nbitmap indexes (if they exist) when performing repack operations.\n\nThis implies moving old bitmap indexes out of the way if we are\nrepacking a repository that already has them, and moving the newly\ngenerated bitmap indexes into the `objects/pack` directory, next to\ntheir corresponding packfiles.\n\nSince `git repack` is now capable of handling these `.bitmap` files,\na normal `git gc` run on a repository that has `pack.writebitmaps` set\nto true in its config file will generate bitmap indexes as part of the\ngarbage collection process.\n\nAlternatively, `git repack` can be called with the `-b` switch to\nexplicitly generate bitmap indexes if you are experimenting\nand don't want them on all the time.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/git-repack.txt | 9 ++++++++-\n builtin/repack.c             | 9 ++++++++-\n 2 files changed, 16 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt\nindex 4c1aff6..dad186c 100644\n--- a/Documentation/git-repack.txt\n+++ b/Documentation/git-repack.txt\n@@ -9,7 +9,7 @@ git-repack - Pack unpacked objects in a repository\n SYNOPSIS\n --------\n [verse]\n-'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [--window=<n>] [--depth=<n>]\n+'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>]\n \n DESCRIPTION\n -----------\n@@ -110,6 +110,13 @@ other objects in that pack they already have locally.\n \tThe default is unlimited, unless the config variable\n \t`pack.packSizeLimit` is set.\n \n+-b::\n+--write-bitmap-index::\n+\tWrite a reachability bitmap index as part of the repack. This\n+\tonly makes sense when used with `-a` or `-A`, as the bitmaps\n+\tmust be able to refer to all reachable objects. This option\n+\toverrides the setting of `pack.writebitmaps`.\n+\n \n Configuration\n -------------\ndiff --git a/builtin/repack.c b/builtin/repack.c\nindex 8b7dfd0..239f278 100644\n--- a/builtin/repack.c\n+++ b/builtin/repack.c\n@@ -94,7 +94,7 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)\n \n static void remove_redundant_pack(const char *dir_name, const char *base_name)\n {\n-\tconst char *exts[] = {\".pack\", \".idx\", \".keep\"};\n+\tconst char *exts[] = {\".pack\", \".idx\", \".keep\", \".bitmap\"};\n \tint i;\n \tstruct strbuf buf = STRBUF_INIT;\n \tsize_t plen;\n@@ -121,6 +121,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t} exts[] = {\n \t\t{\".pack\"},\n \t\t{\".idx\"},\n+\t\t{\".bitmap\", 1},\n \t};\n \tstruct child_process cmd;\n \tstruct string_list_item *item;\n@@ -143,6 +144,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \tint no_update_server_info = 0;\n \tint quiet = 0;\n \tint local = 0;\n+\tint write_bitmap = -1;\n \n \tstruct option builtin_repack_options[] = {\n \t\tOPT_BIT('a', NULL, &pack_everything,\n@@ -161,6 +163,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\tOPT__QUIET(&quiet, N_(\"be quiet\")),\n \t\tOPT_BOOL('l', \"local\", &local,\n \t\t\t\tN_(\"pass --local to git-pack-objects\")),\n+\t\tOPT_BOOL('b', \"write-bitmap-index\", &write_bitmap,\n+\t\t\t\tN_(\"write bitmap index\")),\n \t\tOPT_STRING(0, \"unpack-unreachable\", &unpack_unreachable, N_(\"approxidate\"),\n \t\t\t\tN_(\"with -A, do not loosen objects older than this\")),\n \t\tOPT_INTEGER(0, \"window\", &window,\n@@ -202,6 +206,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)\n \t\targv_array_pushf(&cmd_args, \"--no-reuse-delta\");\n \tif (no_reuse_object)\n \t\targv_array_pushf(&cmd_args, \"--no-reuse-object\");\n+\tif (write_bitmap >= 0)\n+\t\targv_array_pushf(&cmd_args, \"--%swrite-bitmap-index\",\n+\t\t\t\t write_bitmap ? \"\" : \"no-\");\n \n \tif (pack_everything & ALL_INTO_ONE) {\n \t\tget_non_kept_pack_filenames(&existing_packs);\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229491","messageId":"20131025060438.GP23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 18/19] t: add basic bitmap functionality tests","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:04:38Z","receivedAt":"2013-10-25T06:04:38Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Now that we can read and write bitmaps, we can exercise them\nwith some basic functionality tests. These tests aren't\nparticularly useful for seeing the benefit, as the test\nrepo is too small for it to make a difference. However, we\ncan at least check that using bitmaps does not break anything.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n t/t5310-pack-bitmaps.sh | 114 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 114 insertions(+)\n create mode 100755 t/t5310-pack-bitmaps.sh\n\ndiff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh\nnew file mode 100755\nindex 0000000..0868725\n--- /dev/null\n+++ b/t/t5310-pack-bitmaps.sh\n@@ -0,0 +1,114 @@\n+#!/bin/sh\n+\n+test_description='exercise basic bitmap functionality'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup repo with moderate-sized history' '\n+\tfor i in $(test_seq 1 10); do\n+\t\ttest_commit $i\n+\tdone &&\n+\tgit checkout -b other HEAD~5 &&\n+\tfor i in `test_seq 1 10`; do\n+\t\ttest_commit side-$i\n+\tdone &&\n+\tgit checkout master &&\n+\tblob=$(echo tagged-blob | git hash-object -w --stdin) &&\n+\tgit tag tagged-blob $blob &&\n+\tgit config pack.writebitmaps true\n+'\n+\n+test_expect_success 'full repack creates bitmaps' '\n+\tgit repack -ad &&\n+\tls .git/objects/pack/ | grep bitmap >output &&\n+\ttest_line_count = 1 output\n+'\n+\n+test_expect_success 'rev-list --test-bitmap verifies bitmaps' '\n+\tgit rev-list --test-bitmap HEAD\n+'\n+\n+rev_list_tests() {\n+\tstate=$1\n+\n+\ttest_expect_success \"counting commits via bitmap ($state)\" '\n+\t\tgit rev-list --count HEAD >expect &&\n+\t\tgit rev-list --use-bitmap-index --count HEAD >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+\n+\ttest_expect_success \"counting partial commits via bitmap ($state)\" '\n+\t\tgit rev-list --count HEAD~5..HEAD >expect &&\n+\t\tgit rev-list --use-bitmap-index --count HEAD~5..HEAD >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+\n+\ttest_expect_success \"counting non-linear history ($state)\" '\n+\t\tgit rev-list --count other...master >expect &&\n+\t\tgit rev-list --use-bitmap-index --count other...master >actual &&\n+\t\ttest_cmp expect actual\n+\t'\n+\n+\ttest_expect_success \"enumerate --objects ($state)\" '\n+\t\tgit rev-list --objects --use-bitmap-index HEAD >tmp &&\n+\t\tcut -d\" \" -f1 <tmp >tmp2 &&\n+\t\tsort <tmp2 >actual &&\n+\t\tgit rev-list --objects HEAD >tmp &&\n+\t\tcut -d\" \" -f1 <tmp >tmp2 &&\n+\t\tsort <tmp2 >expect &&\n+\t\ttest_cmp expect actual\n+\t'\n+\n+\ttest_expect_success \"bitmap --objects handles non-commit objects ($state)\" '\n+\t\tgit rev-list --objects --use-bitmap-index HEAD tagged-blob >actual &&\n+\t\tgrep $blob actual\n+\t'\n+}\n+\n+rev_list_tests 'full bitmap'\n+\n+test_expect_success 'clone from bitmapped repository' '\n+\tgit clone --no-local --bare . clone.git &&\n+\tgit rev-parse HEAD >expect &&\n+\tgit --git-dir=clone.git rev-parse HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'setup further non-bitmapped commits' '\n+\tfor i in `test_seq 1 10`; do\n+\t\ttest_commit further-$i\n+\tdone\n+'\n+\n+rev_list_tests 'partial bitmap'\n+\n+test_expect_success 'fetch (partial bitmap)' '\n+\tgit --git-dir=clone.git fetch origin master:master &&\n+\tgit rev-parse HEAD >expect &&\n+\tgit --git-dir=clone.git rev-parse HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'incremental repack cannot create bitmaps' '\n+\ttest_commit more-1 &&\n+\ttest_must_fail git repack -d\n+'\n+\n+test_expect_success 'incremental repack can disable bitmaps' '\n+\ttest_commit more-2 &&\n+\tgit repack -d --no-write-bitmap-index\n+'\n+\n+test_expect_success 'full repack, reusing previous bitmaps' '\n+\tgit repack -ad &&\n+\tls .git/objects/pack/ | grep bitmap >output &&\n+\ttest_line_count = 1 output\n+'\n+\n+test_expect_success 'fetch (full bitmap)' '\n+\tgit --git-dir=clone.git fetch origin master:master &&\n+\tgit rev-parse HEAD >expect &&\n+\tgit --git-dir=clone.git rev-parse HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229494","messageId":"20131025060442.GQ23098@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131025055521.GD11810@sigill.intra.peff.net","subject":"[PATCH v2 19/19] pack-bitmap: implement optional name_hash cache","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-25T06:04:42Z","receivedAt":"2013-10-25T06:04:42Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"From: Vicent Marti <tanoku@gmail.com>\n\nWhen we use pack bitmaps rather than walking the object\ngraph, we end up with the list of objects to include in the\npackfile, but we do not know the path at which any tree or\nblob objects would be found.\n\nIn a recently packed repository, this is fine. A fetch would\nuse the paths only as a heuristic in the delta compression\nphase, and a fully packed repository should not need to do\nmuch delta compression.\n\nAs time passes, though, we may acquire more objects on top\nof our large bitmapped pack. If clients fetch frequently,\nthen they never even look at the bitmapped history, and all\nworks as usual. However, a client who has not fetched since\nthe last bitmap repack will have \"have\" tips in the\nbitmapped history, but \"want\" newer objects.\n\nThe bitmaps themselves degrade gracefully in this\ncircumstance. We manually walk the more recent bits of\nhistory, and then use bitmaps when we hit them.\n\nBut we would also like to perform delta compression between\nthe newer objects and the bitmapped objects (both to delta\nagainst what we know the user already has, but also between\n\"new\" and \"old\" objects that the user is fetching). The lack\nof pathnames makes our delta heuristics much less effective.\n\nThis patch adds an optional cache of the 32-bit name_hash\nvalues to the end of the bitmap file. If present, a reader\ncan use it to match bitmapped and non-bitmapped names during\ndelta compression.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/technical/bitmap-format.txt | 33 +++++++++++++++++++++++++++++++\n pack-bitmap-write.c                       | 18 ++++++++++++++++-\n pack-bitmap.c                             | 11 +++++++++++\n pack-bitmap.h                             |  1 +\n 4 files changed, 62 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/technical/bitmap-format.txt b/Documentation/technical/bitmap-format.txt\nindex 7a86bd7..f8c18a0 100644\n--- a/Documentation/technical/bitmap-format.txt\n+++ b/Documentation/technical/bitmap-format.txt\n@@ -21,6 +21,12 @@ GIT bitmap v1 format\n \t\t\trequirement for the bitmap index format, also present in JGit,\n \t\t\tthat greatly reduces the complexity of the implementation.\n \n+\t\t\t- BITMAP_OPT_HASH_CACHE (0x4)\n+\t\t\tIf present, the end of the bitmap file contains\n+\t\t\t`N` 32-bit name-hash values, one per object in the\n+\t\t\tpack. The format and meaning of the name-hash is\n+\t\t\tdescribed below.\n+\n \t\t4-byte entry count (network byte order)\n \n \t\t\tThe total count of entries (bitmapped commits) in this bitmap index.\n@@ -129,3 +135,30 @@ The bitstream represented by the above chunk is then:\n The next word after `L_M` (if any) must again be a RLW, for the next\n chunk.  For efficient appending to the bitstream, the EWAH stores a\n pointer to the last RLW in the stream.\n+\n+\n+== Appendix B: Optional Bitmap Sections\n+\n+These sections may or may not be present in the `.bitmap` file; their\n+presence is indicated by the header flags section described above.\n+\n+Name-hash cache\n+---------------\n+\n+If the BITMAP_OPT_HASH_CACHE flag is set, the end of the bitmap contains\n+a cache of 32-bit values, one per object in the pack. The value at\n+position `i` is the hash of the pathname at which the `i`th object\n+(counting in index order) in the pack can be found.  This can be fed\n+into the delta heuristics to compare objects with similar pathnames.\n+\n+The hash algorithm used is:\n+\n+    hash = 0;\n+    while ((c = *name++))\n+\t    if (!isspace(c))\n+\t\t    hash = (hash >> 2) + (c << 24);\n+\n+Note that this hashing scheme is tied to the BITMAP_OPT_HASH_CACHE flag.\n+If implementations want to choose a different hashing scheme, they are\n+free to do so, but MUST allocate a new header flag (because comparing\n+hashes made under two different schemes would be pointless).\ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex 954a74d..48087f1 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -490,6 +490,19 @@ static void write_selected_commits_v1(struct sha1file *f,\n \t}\n }\n \n+static void write_hash_cache(struct sha1file *f,\n+\t\t\t     struct pack_idx_entry **index,\n+\t\t\t     uint32_t index_nr)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < index_nr; ++i) {\n+\t\tstruct object_entry *entry = (struct object_entry *)index[i];\n+\t\tuint32_t hash_value = htonl(entry->hash);\n+\t\tsha1write(f, &hash_value, sizeof(hash_value));\n+\t}\n+}\n+\n void bitmap_writer_set_checksum(unsigned char *sha1)\n {\n \thashcpy(writer.pack_checksum, sha1);\n@@ -501,7 +514,7 @@ void bitmap_writer_finish(struct pack_idx_entry **index,\n {\n \tstatic char tmp_file[PATH_MAX];\n \tstatic uint16_t default_version = 1;\n-\tstatic uint16_t flags = BITMAP_OPT_FULL_DAG;\n+\tstatic uint16_t flags = BITMAP_OPT_FULL_DAG | BITMAP_OPT_HASH_CACHE;\n \tstruct sha1file *f;\n \n \tstruct bitmap_disk_header header;\n@@ -525,6 +538,9 @@ void bitmap_writer_finish(struct pack_idx_entry **index,\n \tdump_bitmap(f, writer.tags);\n \twrite_selected_commits_v1(f, index, index_nr);\n \n+\tif (flags & BITMAP_OPT_HASH_CACHE)\n+\t\twrite_hash_cache(f, index, index_nr);\n+\n \tsha1close(f, NULL, CSUM_FSYNC);\n \n \tif (adjust_shared_perm(tmp_file))\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex 0044ecc..1fe2688 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -68,6 +68,9 @@ static struct bitmap_index {\n \t/* Number of bitmapped commits */\n \tuint32_t entry_count;\n \n+\t/* Name-hash cache (or NULL if not present). */\n+\tuint32_t *hashes;\n+\n \t/*\n \t * Extended index.\n \t *\n@@ -154,6 +157,11 @@ static int load_bitmap_header(struct bitmap_index *index)\n \t\tif ((flags & BITMAP_OPT_FULL_DAG) == 0)\n \t\t\treturn error(\"Unsupported options for bitmap index file \"\n \t\t\t\t\"(Git requires BITMAP_OPT_FULL_DAG)\");\n+\n+\t\tif (flags & BITMAP_OPT_HASH_CACHE) {\n+\t\t\tunsigned char *end = index->map + index->map_size - 20;\n+\t\t\tindex->hashes = ((uint32_t *)end) - index->pack->num_objects;\n+\t\t}\n \t}\n \n \tindex->entry_count = ntohl(header->entry_count);\n@@ -621,6 +629,9 @@ static void show_objects_for_type(\n \t\t\tentry = &bitmap_git.reverse_index->revindex[pos + offset];\n \t\t\tsha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);\n \n+\t\t\tif (bitmap_git.hashes)\n+\t\t\t\thash = ntohl(bitmap_git.hashes[entry->nr]);\n+\n \t\t\tshow_reach(sha1, object_type, 0, hash, bitmap_git.pack, entry->offset);\n \t\t}\n \ndiff --git a/pack-bitmap.h b/pack-bitmap.h\nindex 18f4d4c..6053453 100644\n--- a/pack-bitmap.h\n+++ b/pack-bitmap.h\n@@ -28,6 +28,7 @@ static const char BITMAP_IDX_SIGNATURE[] = {'B', 'I', 'T', 'M'};;\n \n enum pack_bitmap_opts {\n \tBITMAP_OPT_FULL_DAG = 1,\n+\tBITMAP_OPT_HASH_CACHE = 4,\n };\n \n enum pack_bitmap_flags {\n-- \n1.8.4.1.898.g8bf8a41.dirty\n"},{"id":"229514","messageId":"CAJo=hJvmL0=MN7W-NW4bACK0m+Y39+LBVZ+ufXTEh8KzyjFbKg@mail.gmail.com","threadId":"35192","inReplyTo":"20131025032144.GB26283@sigill.intra.peff.net","subject":"Re: [PATCH 09/19] documentation: add documentation for the bitmap format","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2013-10-25T13:47:06Z","receivedAt":"2013-10-25T13:47:06Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"On Fri, Oct 25, 2013 at 3:21 AM, Jeff King <peff@peff.net> wrote:\n> [+cc spearce; sorry, I really should have cc'd the whole series to you\n>      in the first place]\n>\n> On Fri, Oct 25, 2013 at 08:16:18AM +0700, Nguyen Thai Ngoc Duy wrote:\n>\n>> > +       - A header appears at the beginning:\n>> > +\n>> > +               4-byte signature: {'B', 'I', 'T', 'M'}\n>> > +\n>> > +               2-byte version number (network byte order)\n>> > +                       The current implementation only supports version 1\n>> > +                       of the bitmap index (the same one as JGit).\n>>\n>> I suppose this means if I want to extend pack bitmaps to be used on\n>> shallow clones, I need to step version to 2 before adding the shallow\n>> points in .bitmap file as there's no chance of modifying v1 anymore,\n>> correct?\n>\n> It depends on how you want to change it. And what Shawn says. :)\n\nCorrect, I think we need to bump to version 2 for any changes.\n\n> This is an attempt to document the JGit format. My feeling is that there\n> should be a version bump if an existing implementation would barf on it,\n> and I assume Shawn would agree.  But if you want to simply add extra\n> data that would be ignored by an existing implementation, it would be OK\n> to add the data and mark it with a flag:\n>\n>> > +               2-byte flags (network byte order)\n>> > +\n>> > +                       The following flags are supported:\n>> [...]\n>\n> That's how we added the name-hash cache in the final patch.\n>\n> That being said, JGit is _not_ happy with that, and complains when any\n> flags besides OPT_FULL are used.  Whether that is because I am\n> misinterpreting the intent of the flags field, or because JGit is being\n> overly strict is up for debate.\n\nFlags was created by Colby, I don't think he intended to use it for\ndetermining if an optional section exists in the file or not. There is\ninsufficient information using only 1 bit to skip over an optional\nsection. The dircache file uses a length field with each extension\nname to allow a reader to skip over sections it does not recognize\n(and whose name is lowercase).\n\nI think Colby and I talked about having additional optional sections\nin this file, but Colby didn't want to overcomplicate the format early\non. So v1 is probably not very extensible and we may have to go to v2\nto safely create an extension with the name hash cache used in this\nseries.\n\nGiven that the JGit v1 bitmap format has been shipping since JGit 3.0\nand in Gerrit Code Review 2.6, its in use in the wild. So we aren't\ngoing to go back and redefine v1.\n"},{"id":"229515","messageId":"CAJo=hJvw-UNWVDADcGzA1P3GGOKJGh8h4LrETPYnjBNYmfkxjQ@mail.gmail.com","threadId":"35192","inReplyTo":"20131024180357.GJ24180@sigill.intra.peff.net","subject":"Re: [PATCH 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2013-10-25T13:55:13Z","receivedAt":"2013-10-25T13:55:13Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"On Thu, Oct 24, 2013 at 6:03 PM, Jeff King <peff@peff.net> wrote:\n> If `prepare_bitmap_walk` runs successfully, the resulting bitmap is\n> stored and the equivalent of a `traverse_commit_list` call can be\n> performed by using `traverse_bitmap_commit_list`; the bitmap version\n> of this call yields the objects straight from the packfile index\n> (without having to look them up or parse them) and hence is several\n> orders of magnitude faster.\n\nThis is interesting, we didn't attempt it in JGit, but I can see how\nit could be very useful.\n\n> As an extra optimization, when `prepare_bitmap_walk` succeeds, the\n> `reuse_partial_packfile_from_bitmap` call can be attempted: it will find\n> the amount of objects at the beginning of the on-disk packfile that can\n> be reused as-is, and return an offset into the packfile. The source\n> packfile can then be loaded and the bytes up to `offset` can be written\n> directly to the result without having to consider the entires inside the\n> packfile individually.\n\nYay! This is similar to the optimization we use in JGit to send the\nentire pack, but the part about sending a leading prefix is new. Do\nyou have any data showing how well this works in practice for cases\nwhere offset is before than length-20?\n"},{"id":"229516","messageId":"CAJo=hJvKG2sjWasO4YfExbbSN+U-q_it6gb43v_YJoL60XKwUg@mail.gmail.com","threadId":"35192","inReplyTo":"20131024180627.GL24180@sigill.intra.peff.net","subject":"Re: [PATCH 12/19] rev-list: add bitmap mode to speed up object lists","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2013-10-25T14:00:48Z","receivedAt":"2013-10-25T14:00:48Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"On Thu, Oct 24, 2013 at 6:06 PM, Jeff King <peff@peff.net> wrote:\n> Note that most of the time we spend for --count invocations is on\n> generating the pack revindex. It may be worth storing a revidx (either\n> in a separate file, as part of the .idx, or as an optional section in\n> the .bitmap file).\n\nThis was discussed before, and it may make sense to cache the revidx.\nI'm glad to see the timings are already better with bitmaps, even\nthough the revidx has to be computed on the fly for each invocation.\n"},{"id":"229517","messageId":"CAJo=hJuDx=3AOoz2oEORVOzeBYBwvOWO_ye8D5d8PcDc3Zm+Ew@mail.gmail.com","threadId":"35192","inReplyTo":"20131024180419.GK24180@sigill.intra.peff.net","subject":"Re: [PATCH 11/19] pack-objects: use bitmaps when packing objects","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2013-10-25T14:14:11Z","receivedAt":"2013-10-25T14:14:11Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"On Thu, Oct 24, 2013 at 6:04 PM, Jeff King <peff@peff.net> wrote:\n> For bitmaps to be used, the following must be true:\n>\n>   1. We must be packing to stdout (as a normal `pack-objects` from\n>      `upload-pack` would do).\n>\n>   2. There must be a .bitmap index containing at least one of the\n>      \"have\" objects that the client is asking for.\n\nThe client must explicitly \"have\" a commit that has a bitmap? In JGit\nwe allow the client to have anything, and walk backwards using\ntraditional graph traversal until a bitmap is found.\n\n> @@ -704,6 +759,18 @@ static void write_pack_file(void)\n>                 offset = write_pack_header(f, nr_remaining);\n>                 if (!offset)\n>                         die_errno(\"unable to write pack header\");\n> +\n> +               if (reuse_packfile) {\n> +                       off_t packfile_size;\n> +                       assert(pack_to_stdout);\n> +\n> +                       packfile_size = write_reused_pack(f);\n> +                       if (!packfile_size)\n> +                               die_errno(\"failed to re-use existing pack\");\n> +\n> +                       offset += packfile_size;\n> +               }\n> +\n>                 nr_written = 0;\n>                 for (; i < to_pack.nr_objects; i++) {\n>                         struct object_entry *e = write_order[i];\n\nCan reuse_packfile be true at the same time as to_pack.nr_objects > 0?\n\nIn JGit we write the to_pack list first, then the reuse pack. Our\nrationale was the to_pack list is recent objects that are newer and\nwould appear first in a traditional traversal, so they should go at\nthe front of the stream. This does mean if they delta compress against\nan object in that reuse_packfile slice they have to use REF_DELTA\ninstead of OFS_DELTA.\n\n\nIs this series running on github.com/torvalds/linux? Last Saturday I\nran a live demo clone comparing github.com/torvalds/linux to a JGit\nbitmap clone and some guy heckled me because GitHub was only a few\nseconds slower. :-)\n"},{"id":"229536","messageId":"xmqqk3h1hrx0.fsf@gitster.dls.corp.google.com","threadId":"35192","inReplyTo":"20131025060345.GH23098@sigill.intra.peff.net","subject":"Re: [PATCH v2 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-10-25T23:06:19Z","receivedAt":"2013-10-25T23:06:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> diff --git a/pack-bitmap.c b/pack-bitmap.c\n> new file mode 100644\n> index 0000000..73c52fd\n> --- /dev/null\n> +++ b/pack-bitmap.c\n> @@ -0,0 +1,965 @@\n> +#include <stdlib.h>\n> +\n> +#include \"cache.h\"\n\nYou among all people already know why this is bad, no?\n\nI'll remove the first two lines while queuing.  Thanks.\n"},{"id":"229540","messageId":"20131026002629.GA18324@sigill.intra.peff.net","threadId":"35192","inReplyTo":"xmqqk3h1hrx0.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-26T00:26:30Z","receivedAt":"2013-10-26T00:26:30Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 25, 2013 at 04:06:19PM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > diff --git a/pack-bitmap.c b/pack-bitmap.c\n> > new file mode 100644\n> > index 0000000..73c52fd\n> > --- /dev/null\n> > +++ b/pack-bitmap.c\n> > @@ -0,0 +1,965 @@\n> > +#include <stdlib.h>\n> > +\n> > +#include \"cache.h\"\n> \n> You among all people already know why this is bad, no?\n\nYes, I am well aware of why we do not want it. I thought I removed that,\nbut it appears I missed one. Sorry.\n\n-Peff\n"},{"id":"229547","messageId":"20131026062507.GA23963@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131026002629.GA18324@sigill.intra.peff.net","subject":"Re: [PATCH v2 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-26T06:25:07Z","receivedAt":"2013-10-26T06:25:07Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 25, 2013 at 08:26:29PM -0400, Jeff King wrote:\n\n> On Fri, Oct 25, 2013 at 04:06:19PM -0700, Junio C Hamano wrote:\n> \n> > Jeff King <peff@peff.net> writes:\n> > \n> > > diff --git a/pack-bitmap.c b/pack-bitmap.c\n> > > new file mode 100644\n> > > index 0000000..73c52fd\n> > > --- /dev/null\n> > > +++ b/pack-bitmap.c\n> > > @@ -0,0 +1,965 @@\n> > > +#include <stdlib.h>\n> > > +\n> > > +#include \"cache.h\"\n> > \n> > You among all people already know why this is bad, no?\n> \n> Yes, I am well aware of why we do not want it. I thought I removed that,\n> but it appears I missed one. Sorry.\n\nIn addition to that one, there are a few other system header includes:\n\n  - the ewah/*.c files include the necessary standard headers, and do\n    not include git-compat-util.h at all. I do not really consider these\n    \"git code\", but rather a black box we have imported into the tree\n    to ease the dependency chain. So in that sense, they operate like\n    xdiff/*.c or compat/regex/*.c, which also compile on\n    their own (and can get away with it because they are mostly standard\n    C and do not do \"system\" things.\n\n    However, the code in the ewah/ directory has been hacked up a bit\n    from its original, and ewah_io.c _does_ include \"git-compat-util.h\".\n    So it may make sense to consider our copy a fork and git-ify it\n    more. The upstream Java EWAH library is active and separate, but the\n    C port done by Vicent, while theoretically a separate project, does\n    not actually have any users besides git (though presumably libgit2\n    will build on it at some point).\n\n  - Regardless of the decision above, ewah/ewok.h includes stdlib.h,\n    which will pollute git source files which use it.\n\n  - Ditto for khash.h.\n\n-Peff\n"},{"id":"229550","messageId":"87txg4sbyf.fsf@linux-k42r.v.cablecom.net","threadId":"35192","inReplyTo":"20131024180157.GG24180@sigill.intra.peff.net","subject":"Re: [PATCH 07/19] compat: add endianness helpers","fromName":"Thomas Rast","fromEmail":"tr@thomasrast.ch","sentAt":"2013-10-26T07:55:36Z","receivedAt":"2013-10-26T07:55:36Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> From: Vicent Marti <tanoku@gmail.com>\n>\n> The POSIX standard doesn't currently define a `nothll`/`htonll`\n\ntypo: ntohll\n\n> function pair to perform network-to-host and host-to-network\n> swaps of 64-bit data. These 64-bit swaps are necessary for the on-disk\n> storage of EWAH bitmaps if they are not in native byte order.\n[...]\n> +#\t\tinclude <byteswap.h>\n\nDo we need a hack on top similar to what ntoh_l and hton_l do, for\nplatforms that do not support unaligned access?\n\n---- read-cache.c:1316 ----\n#ifndef NEEDS_ALIGNED_ACCESS\n#define ntoh_s(var) ntohs(var)\n#define ntoh_l(var) ntohl(var)\n#else\nstatic inline uint16_t ntoh_s_force_align(void *p)\n{\n\tuint16_t x;\n\tmemcpy(&x, p, sizeof(x));\n\treturn ntohs(x);\n}\nstatic inline uint32_t ntoh_l_force_align(void *p)\n{\n\tuint32_t x;\n\tmemcpy(&x, p, sizeof(x));\n\treturn ntohl(x);\n}\n#define ntoh_s(var) ntoh_s_force_align(&(var))\n#define ntoh_l(var) ntoh_l_force_align(&(var))\n#endif\n\n-- \nThomas Rast\ntr@thomasrast.ch\n"},{"id":"229551","messageId":"87sivosby8.fsf@linux-k42r.v.cablecom.net","threadId":"35192","inReplyTo":"20131024180217.GH24180@sigill.intra.peff.net","subject":"Re: [PATCH 08/19] ewah: compressed bitmap implementation","fromName":"Thomas Rast","fromEmail":"tr@thomasrast.ch","sentAt":"2013-10-26T07:55:43Z","receivedAt":"2013-10-26T07:55:43Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> From: Vicent Marti <tanoku@gmail.com>\n>\n> EWAH is a word-aligned compressed variant of a bitset (i.e. a data\n> structure that acts as a 0-indexed boolean array for many entries).\n\nDoes this contain any changes in response to\n\n  http://thread.gmane.org/gmane.comp.version-control.git/228918/focus=228996\n\nOtherwise that old review still applies ;-)\n\n-- \nThomas Rast\ntr@thomasrast.ch\n"},{"id":"229559","messageId":"CACsJy8Cv5WMB=L+fQCj-ZURZC3ZdEqXaqqW_O2ZD-HNcC_V3yg@mail.gmail.com","threadId":"35192","inReplyTo":"20131025060345.GH23098@sigill.intra.peff.net","subject":"Re: [PATCH v2 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-10-26T10:14:16Z","receivedAt":"2013-10-26T10:14:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"If it's not mentioned yet, maybe you should note that this code\ncurrently supports only one pack with .bitmap file.\n\nOn Fri, Oct 25, 2013 at 1:03 PM, Jeff King <peff@peff.net> wrote:\n> diff --git a/khash.h b/khash.h\n> new file mode 100644\n> index 0000000..0fdf39d\n> --- /dev/null\n> +++ b/khash.h\n> @@ -0,0 +1,342 @@\n\nI notice the line continuations '\\' in this file look more aligned if\ntab length is set to 4. No idea how many emacs users out there but it\nprobably does not harm to put\n\n/* -*- mode: c; tab-width: 4; -*- */\n\nat the beginning of this file? Another option is realign the file,\nwhich I doubt is good because this file is imported.\n\n> +static int load_pack_bitmap(void)\n> +{\n> +       assert(bitmap_git.map && !bitmap_git.loaded);\n> +\n> +       bitmap_git.bitmaps = kh_init_sha1();\n> +       bitmap_git.ext_index.positions = kh_init_sha1_pos();\n> +       bitmap_git.reverse_index = revindex_for_pack(bitmap_git.pack);\n> +\n> +       if (!(bitmap_git.commits = read_bitmap_1(&bitmap_git)) ||\n> +               !(bitmap_git.trees = read_bitmap_1(&bitmap_git)) ||\n> +               !(bitmap_git.blobs = read_bitmap_1(&bitmap_git)) ||\n> +               !(bitmap_git.tags = read_bitmap_1(&bitmap_git)))\n> +               goto failed;\n> +\n> +       if (load_bitmap_entries_v1(&bitmap_git) < 0)\n> +               goto failed;\n\nI don't see any mechanism to protect us from corrupt .bitmap files. If\n.bitmap is not very large, maybe just check the trailing checksum in\nthe file when we open it? Else maybe add a crc32 or something after\neach commit bitmap in .bitmap v2 and only verify the ones we actually\nuse?\n\n> +static int open_pack_bitmap(void)\n> +{\n> +       struct packed_git *p;\n> +\n> +       assert(!bitmap_git.map && !bitmap_git.loaded);\n> +\n> +       prepare_packed_git();\n> +       for (p = packed_git; p; p = p->next) {\n> +               if (open_pack_bitmap_1(p) == 0)\n> +                       return 0;\n\nIt maybe a good idea to go on anyway, checking for another .bitmap.\nJust warn the user about that if found.\n\n> +       }\n> +\n> +       return -1;\n> +}\n> +\n-- \nDuy\n"},{"id":"229560","messageId":"1382782796-5495-1-git-send-email-pclouds@gmail.com","threadId":"35192","inReplyTo":"20131025060442.GQ23098@sigill.intra.peff.net","subject":"[PATCH 20/19] count-objects: consider .bitmap without .pack/.idx pair garbage","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2013-10-26T10:19:56Z","receivedAt":"2013-10-26T10:19:56Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Maybe squash this in some place, or leave it separate. I'm fine either way.\n\n sha1_file.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/sha1_file.c b/sha1_file.c\nindex 4714bd8..1294962 100644\n--- a/sha1_file.c\n+++ b/sha1_file.c\n@@ -1194,6 +1194,7 @@ static void prepare_packed_git_one(char *objdir, int local)\n \n \t\tif (has_extension(de->d_name, \".idx\") ||\n \t\t    has_extension(de->d_name, \".pack\") ||\n+\t\t    has_extension(de->d_name, \".bitmap\") ||\n \t\t    has_extension(de->d_name, \".keep\"))\n \t\t\tstring_list_append(&garbage, path);\n \t\telse\n-- \n1.8.2.83.gc99314b\n"},{"id":"229561","messageId":"CACsJy8DMOfZu+2DS=-J9jfiP796XYi=e7B28cdV=ck9J-VOTtA@mail.gmail.com","threadId":"35192","inReplyTo":"20131025060352.GI23098@sigill.intra.peff.net","subject":"Re: [PATCH v2 11/19] pack-objects: use bitmaps when packing objects","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-10-26T10:25:14Z","receivedAt":"2013-10-26T10:25:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Oct 25, 2013 at 1:03 PM, Jeff King <peff@peff.net> wrote:\n> From: Vicent Marti <tanoku@gmail.com>\n>\n> In this patch, we use the bitmap API to perform the `Counting Objects`\n> phase in pack-objects, rather than a traditional walk through the object\n> graph. For a reasonably-packed large repo, the time to fetch and clone\n> is often dominated by the full-object revision walk during the Counting\n> Objects phase. Using bitmaps can reduce the CPU time required on the\n> server (and therefore start sending the actual pack data with less\n> delay).\n>\n> For bitmaps to be used, the following must be true:\n>\n>   1. We must be packing to stdout (as a normal `pack-objects` from\n>      `upload-pack` would do).\n>\n>   2. There must be a .bitmap index containing at least one of the\n>      \"have\" objects that the client is asking for.\n>\n>   3. Bitmaps must be enabled (they are enabled by default, but can be\n>      disabled by setting `pack.usebitmaps` to false, or by using\n>      `--no-use-bitmap-index` on the command-line).\n>\n> If any of these is not true, we fall back to doing a normal walk of the\n> object graph.\n\nI haven't read the bitmap creation code yet. But it probably does not\nmatter. If the client requests a shallow fetch, you probably want to\nfall back to normal walk too.\n\nBitmaps may be made work with shallow fetches too, I'm not sure. We\ncould substract the shallow'd commits out. The problem is if some\nother commits share parts of the shallow'd commits, I'm not sure how\nto detect that.\n-- \nDuy\n"},{"id":"229642","messageId":"xmqqiowhh140.fsf@gitster.dls.corp.google.com","threadId":"35192","inReplyTo":"20131026062507.GA23963@sigill.intra.peff.net","subject":"Re: [PATCH v2 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-10-28T15:22:07Z","receivedAt":"2013-10-28T15:22:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Fri, Oct 25, 2013 at 08:26:29PM -0400, Jeff King wrote:\n>\n>> On Fri, Oct 25, 2013 at 04:06:19PM -0700, Junio C Hamano wrote:\n>> \n>> > Jeff King <peff@peff.net> writes:\n>> > \n>> > > diff --git a/pack-bitmap.c b/pack-bitmap.c\n>> > > new file mode 100644\n>> > > index 0000000..73c52fd\n>> > > --- /dev/null\n>> > > +++ b/pack-bitmap.c\n>> > > @@ -0,0 +1,965 @@\n>> > > +#include <stdlib.h>\n>> > > +\n>> > > +#include \"cache.h\"\n>> > \n>> > You among all people already know why this is bad, no?\n>> \n>> Yes, I am well aware of why we do not want it. I thought I removed that,\n>> but it appears I missed one. Sorry.\n>\n> In addition to that one, there are a few other system header includes:\n>\n>   - the ewah/*.c files include the necessary standard headers, and do\n>     not include git-compat-util.h at all. I do not really consider these\n>     \"git code\", but rather a black box we have imported into the tree\n>     to ease the dependency chain. So in that sense, they operate like\n>     xdiff/*.c or compat/regex/*.c, which also compile on\n>     their own (and can get away with it because they are mostly standard\n>     C and do not do \"system\" things.\n\nRight; I didn't comment on the bare inclusions of system headers in\nthese files exactly because I shared that reasoning.\n\n>     However, the code in the ewah/ directory has been hacked up a bit\n>     from its original, and ewah_io.c _does_ include \"git-compat-util.h\".\n>     So it may make sense to consider our copy a fork and git-ify it\n>     more.\n\nYeah, sounds very sensible.\n"},{"id":"229681","messageId":"20131028221347.GA32479@goldbirke","threadId":"35192","inReplyTo":"20131025060438.GP23098@sigill.intra.peff.net","subject":"Re: [PATCH v2 18/19] t: add basic bitmap functionality tests","fromName":"SZEDER Gábor","fromEmail":"szeder@ira.uka.de","sentAt":"2013-10-28T22:13:47Z","receivedAt":"2013-10-28T22:13:47Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Fri, Oct 25, 2013 at 02:04:38AM -0400, Jeff King wrote:\n> diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh\n> new file mode 100755\n> index 0000000..0868725\n> --- /dev/null\n> +++ b/t/t5310-pack-bitmaps.sh\n> @@ -0,0 +1,114 @@\n> +#!/bin/sh\n> +\n> +test_description='exercise basic bitmap functionality'\n> +. ./test-lib.sh\n> +\n> +test_expect_success 'setup repo with moderate-sized history' '\n> +\tfor i in $(test_seq 1 10); do\n> +\t\ttest_commit $i\n> +\tdone &&\n> +\tgit checkout -b other HEAD~5 &&\n> +\tfor i in `test_seq 1 10`; do\n> +\t\ttest_commit side-$i\n> +\tdone &&\n\nSorry, style nitpick: could you rewrite this command substitution\nusing $() like a few lines above?\n\n> +test_expect_success 'setup further non-bitmapped commits' '\n> +\tfor i in `test_seq 1 10`; do\n> +\t\ttest_commit further-$i\n> +\tdone\n> +'\n\nLikewise.\n\n\nBest,\nGábor\n"},{"id":"229785","messageId":"20131030065922.GC11317@sigill.intra.peff.net","threadId":"35192","inReplyTo":"1382782796-5495-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 20/19] count-objects: consider .bitmap without .pack/.idx pair garbage","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-30T06:59:22Z","receivedAt":"2013-10-30T06:59:22Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Oct 26, 2013 at 05:19:56PM +0700, Nguyen Thai Ngoc Duy wrote:\n\n>  Maybe squash this in some place, or leave it separate. I'm fine either way.\n\nThanks, this makes sense.  I've left it as a separate commit.\n\nYour subject did not quite make sense to me, as the effect is to _not_\nreport .bitmap files as garbage. I updated the commit message to this:\n\n    count-objects: recognize .bitmap in garbage-checking\n    \n    Count-objects will report any \"garbage\" files in the packs\n    directory, including files whose extensions it does not\n    know (case 1), and files whose matching \".pack\" file is\n    missing (case 2).  Without having learned about \".bitmap\"\n    files, the current code reports all such files as garbage\n    (case 1), even if their pack exists. Instead, they should be\n    treated as case 2.\n\n-Peff\n"},{"id":"229787","messageId":"20131030070055.GE11317@sigill.intra.peff.net","threadId":"35192","inReplyTo":"xmqqiowhh140.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-30T07:00:55Z","receivedAt":"2013-10-30T07:00:55Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Oct 28, 2013 at 08:22:07AM -0700, Junio C Hamano wrote:\n\n> >     However, the code in the ewah/ directory has been hacked up a bit\n> >     from its original, and ewah_io.c _does_ include \"git-compat-util.h\".\n> >     So it may make sense to consider our copy a fork and git-ify it\n> >     more.\n> \n> Yeah, sounds very sensible.\n\nI'll make them more git-ish for the next re-roll, then.\n\n-Peff\n"},{"id":"229789","messageId":"20131030072727.GF11317@sigill.intra.peff.net","threadId":"35192","inReplyTo":"CACsJy8Cv5WMB=L+fQCj-ZURZC3ZdEqXaqqW_O2ZD-HNcC_V3yg@mail.gmail.com","subject":"Re: [PATCH v2 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-30T07:27:27Z","receivedAt":"2013-10-30T07:27:27Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Oct 26, 2013 at 05:14:16PM +0700, Nguyen Thai Ngoc Duy wrote:\n\n> If it's not mentioned yet, maybe you should note that this code\n> currently supports only one pack with .bitmap file.\n\nYes, there's really not any point in having multiple .bitmap files, as\nthe bitmaps need to be closed over the set of objects. So you would have\none \"base\" pack with a bunch of bitmaps. But any pack that builds on\nthat cannot be bitmapped.\n\nJGit has the same restriction, mostly because it keeps the\nimplementation much saner, and there is not really a good reason to lift\nit (if you want more things bitmapped, you probably also want them\npacked to share deltas).\n\nI'm happy to mention that somewhere, but I'm not sure where is most\nappropriate.\n\n> > diff --git a/khash.h b/khash.h\n> [...]\n> I notice the line continuations '\\' in this file look more aligned if\n> tab length is set to 4. No idea how many emacs users out there but it\n> probably does not harm to put\n> \n> /* -*- mode: c; tab-width: 4; -*- */\n> \n> at the beginning of this file? Another option is realign the file,\n> which I doubt is good because this file is imported.\n\nYes, it doesn't follow our normal whitespace guidelines. I refrained\nfrom tweaking it for the reason you mentioned (kwset.c is in a similar\nboat).\n\nI'd use:\n\n  /* vim: set ts=4 */\n\nmyself. :) We have resisted having such modelines so far, leaving it\ninstead to developers to configure their editor as appropriate. It is\nharder when there is an oddball file like this, though. I don't have a\nstrong opinion either way.\n\n> I don't see any mechanism to protect us from corrupt .bitmap files. If\n> .bitmap is not very large, maybe just check the trailing checksum in\n> the file when we open it? Else maybe add a crc32 or something after\n> each commit bitmap in .bitmap v2 and only verify the ones we actually\n> use?\n\nThe .bitmap for the kernel is about 6MB of bitmaps, plus about 12MB for\nthe name-hash cache. The loading procedure needs to read through the\nwhole bitmap section, as the records are variable width and there is no\nindex. But to verify the sha1 we'd have to go through the extra 12MB of\nname-hash cache, which we would otherwise not need to touch.\n\nAdding a crc would not be too big a deal. However, I'd really like to\nfocus first on getting the v1 reader/writer merged. That's set in stone\ndue to JGit, so anything we do for v2 would want to build on top anyway.\n\n> > +static int open_pack_bitmap(void)\n> > +{\n> > +       struct packed_git *p;\n> > +\n> > +       assert(!bitmap_git.map && !bitmap_git.loaded);\n> > +\n> > +       prepare_packed_git();\n> > +       for (p = packed_git; p; p = p->next) {\n> > +               if (open_pack_bitmap_1(p) == 0)\n> > +                       return 0;\n> \n> It maybe a good idea to go on anyway, checking for another .bitmap.\n> Just warn the user about that if found.\n\nYeah, that sounds reasonable. I'll squash this in:\n\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex 459b587..078f7c6 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -265,6 +265,12 @@ static int open_pack_bitmap_1(struct packed_git *packfile)\n \t\treturn -1;\n \t}\n \n+\tif (bitmap_git.pack) {\n+\t\twarning(\"ignoring extra bitmap file: %s\", idx_name);\n+\t\tclose(fd);\n+\t\treturn -1;\n+\t}\n+\n \tbitmap_git.pack = packfile;\n \tbitmap_git.map_size = xsize_t(st.st_size);\n \tbitmap_git.map = xmmap(NULL, bitmap_git.map_size, PROT_READ, MAP_PRIVATE, fd, 0);\n@@ -325,16 +331,17 @@ char *pack_bitmap_filename(struct packed_git *p)\n static int open_pack_bitmap(void)\n {\n \tstruct packed_git *p;\n+\tint ret = -1;\n \n \tassert(!bitmap_git.map && !bitmap_git.loaded);\n \n \tprepare_packed_git();\n \tfor (p = packed_git; p; p = p->next) {\n \t\tif (open_pack_bitmap_1(p) == 0)\n-\t\t\treturn 0;\n+\t\t\tret = 0;\n \t}\n \n-\treturn -1;\n+\treturn ret;\n }\n \n int prepare_bitmap_git(void)\n\n-Peff\n"},{"id":"229790","messageId":"20131030073627.GG11317@sigill.intra.peff.net","threadId":"35192","inReplyTo":"CACsJy8DMOfZu+2DS=-J9jfiP796XYi=e7B28cdV=ck9J-VOTtA@mail.gmail.com","subject":"Re: [PATCH v2 11/19] pack-objects: use bitmaps when packing objects","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-30T07:36:27Z","receivedAt":"2013-10-30T07:36:27Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Oct 26, 2013 at 05:25:14PM +0700, Nguyen Thai Ngoc Duy wrote:\n\n> > For bitmaps to be used, the following must be true:\n> >\n> >   1. We must be packing to stdout (as a normal `pack-objects` from\n> >      `upload-pack` would do).\n> >\n> >   2. There must be a .bitmap index containing at least one of the\n> >      \"have\" objects that the client is asking for.\n> >\n> >   3. Bitmaps must be enabled (they are enabled by default, but can be\n> >      disabled by setting `pack.usebitmaps` to false, or by using\n> >      `--no-use-bitmap-index` on the command-line).\n> >\n> > If any of these is not true, we fall back to doing a normal walk of the\n> > object graph.\n> \n> I haven't read the bitmap creation code yet. But it probably does not\n> matter. If the client requests a shallow fetch, you probably want to\n> fall back to normal walk too.\n\nOne other criterion I should have mentioned: we must be using the\ninternal rev-list. That prevented us in v1.8.4.1 and earlier from using\nbitmaps for shallow fetches. But as of v1.8.4.2, we always use\npack-objects' rev-walker. We may need to pass --no-use-bitmap-index for\nshallow fetches.\n\nAs for repos that are themselves shallow, I do not know how doing a\n\"repack -b\" would fare. Probably not well.\n\n> Bitmaps may be made work with shallow fetches too, I'm not sure. We\n> could substract the shallow'd commits out. The problem is if some\n> other commits share parts of the shallow'd commits, I'm not sure how\n> to detect that.\n\nYeah, I do not think shallow follows the same have/want reachability\nrules that we rely on for taking the set difference via bitmaps. It may\nbe made to work eventually, but I think the important thing at this\npoint is making sure we properly fall back to the slow method when\nshallow.\n\n-Peff\n"},{"id":"229791","messageId":"20131030073913.GI11317@sigill.intra.peff.net","threadId":"35192","inReplyTo":"20131028221347.GA32479@goldbirke","subject":"Re: [PATCH v2 18/19] t: add basic bitmap functionality tests","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-30T07:39:14Z","receivedAt":"2013-10-30T07:39:14Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Oct 28, 2013 at 11:13:47PM +0100, SZEDER Gábor wrote:\n\n> > +\tfor i in `test_seq 1 10`; do\n> > +\t\ttest_commit side-$i\n> > +\tdone &&\n> \n> Sorry, style nitpick: could you rewrite this command substitution\n> using $() like a few lines above?\n\nThat is a nitpick. :P\n\nBut sure, I've squashed it in.\n\n-Peff\n"},{"id":"229793","messageId":"20131030075040.GJ11317@sigill.intra.peff.net","threadId":"35192","inReplyTo":"CAJo=hJvmL0=MN7W-NW4bACK0m+Y39+LBVZ+ufXTEh8KzyjFbKg@mail.gmail.com","subject":"Re: [PATCH 09/19] documentation: add documentation for the bitmap format","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-30T07:50:40Z","receivedAt":"2013-10-30T07:50:40Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 25, 2013 at 01:47:06PM +0000, Shawn O. Pearce wrote:\n\n> I think Colby and I talked about having additional optional sections\n> in this file, but Colby didn't want to overcomplicate the format early\n> on. So v1 is probably not very extensible and we may have to go to v2\n> to safely create an extension with the name hash cache used in this\n> series.\n> \n> Given that the JGit v1 bitmap format has been shipping since JGit 3.0\n> and in Gerrit Code Review 2.6, its in use in the wild. So we aren't\n> going to go back and redefine v1.\n\nI don't think either course of action affects how JGit in the wild will\nreact. If we add a new flag to v1, existing JGit barfs. If we move to\nv2, existing JGit barfs.  In either case, the simplest fix for JGit is\nto ignore the new section.\n\nIf we want to define a v2 format and make it clear how to add optional\nsections, that's fine. But what I really want to avoid is a big v2\nbikeshedding discussion where other features get added because it's a\nversion bump. This feature has been a long-time coming, and I'd like to\nat least get us on par with JGit's v1 bitmaps. It's a big series, and I\nfear that v2 discussions would end up as a distraction.\n\nIf that means dropping the name-hash cache from the series, so be it\n(that's part of why I pulled it out into its own patch at the end). We\ncould also make it optional with a documentation warning that existing\nJGit will not like the resulting bitmap files. In practice, I do not\nexpect it to be a big deal, as most sites will not mix-and-match serving\nfrom the two implementations (though you might repack with C git and\nserve with JGit, which means \"off\" would be the sane default for the\nfeature).\n\n-Peff\n"},{"id":"229795","messageId":"20131030081023.GK11317@sigill.intra.peff.net","threadId":"35192","inReplyTo":"CAJo=hJvw-UNWVDADcGzA1P3GGOKJGh8h4LrETPYnjBNYmfkxjQ@mail.gmail.com","subject":"Re: [PATCH 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-30T08:10:23Z","receivedAt":"2013-10-30T08:10:23Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 25, 2013 at 01:55:13PM +0000, Shawn O. Pearce wrote:\n\n> > As an extra optimization, when `prepare_bitmap_walk` succeeds, the\n> > `reuse_partial_packfile_from_bitmap` call can be attempted: it will find\n> > the amount of objects at the beginning of the on-disk packfile that can\n> > be reused as-is, and return an offset into the packfile. The source\n> > packfile can then be loaded and the bytes up to `offset` can be written\n> > directly to the result without having to consider the entires inside the\n> > packfile individually.\n> \n> Yay! This is similar to the optimization we use in JGit to send the\n> entire pack, but the part about sending a leading prefix is new. Do\n> you have any data showing how well this works in practice for cases\n> where offset is before than length-20?\n\nActually, I don't think it kicks in very much due to packfile ordering.\nYou have all of the commits at the front of the pack, then all of the\ntrees, then all of the blobs. So if you want the whole thing, it is easy\nto reuse a big chunk. But if you want only the most recent slice, we can\nreuse the early bit with the new commits, but we stop partway through\nthe commit list. You still have to handle all of the trees and blobs\nseparately.\n\nSo in practice, I think this really only kicks in for clones anyway.\n\nIn theory, you could find \"islands\" of ones in the bitmap and send whole\nslices of packfile at once. But you need to be careful not to send a\ndelta without its base. Which I think means you end up having to\ngenerate the whole sha1 list anyway, and check that the other side has\neach base before reusing a delta (i.e., the normal code path).\n\nIn fact, I'm not quite sure that even a partial reuse up to an offset is\n100% safe. In a newly packed git repo it is, because we always put bases\nbefore deltas (and OFS_DELTA objects need this). But if you had a bitmap\ngenerated from a fixed thin pack, we would have REF_DELTA objects early\non that depend on bases appended to the end of the pack. So I really\nwonder if we should scrap this partial reuse and either just have full\nreuse, or go through the regular object_entry construction.\n\nVicent, you've thought about the reuse code a lot more than I have. Any\nthoughts?\n\n-Peff\n"},{"id":"229796","messageId":"20131030081201.GL11317@sigill.intra.peff.net","threadId":"35192","inReplyTo":"CAJo=hJvKG2sjWasO4YfExbbSN+U-q_it6gb43v_YJoL60XKwUg@mail.gmail.com","subject":"Re: [PATCH 12/19] rev-list: add bitmap mode to speed up object lists","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-30T08:12:01Z","receivedAt":"2013-10-30T08:12:01Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 25, 2013 at 02:00:48PM +0000, Shawn O. Pearce wrote:\n\n> On Thu, Oct 24, 2013 at 6:06 PM, Jeff King <peff@peff.net> wrote:\n> > Note that most of the time we spend for --count invocations is on\n> > generating the pack revindex. It may be worth storing a revidx (either\n> > in a separate file, as part of the .idx, or as an optional section in\n> > the .bitmap file).\n> \n> This was discussed before, and it may make sense to cache the revidx.\n> I'm glad to see the timings are already better with bitmaps, even\n> though the revidx has to be computed on the fly for each invocation.\n\nThe radix sort makes a _huge_ difference there. But I would still like\nan mmap-able .revidx cache. I think we can get at least another\norder-of-magnitude speedup.\n\nI'm intentionally leaving that out for now, though. I want to get this\nseries merged before trying to go much further.\n\n-Peff\n"},{"id":"229797","messageId":"20131030082143.GM11317@sigill.intra.peff.net","threadId":"35192","inReplyTo":"CAJo=hJuDx=3AOoz2oEORVOzeBYBwvOWO_ye8D5d8PcDc3Zm+Ew@mail.gmail.com","subject":"Re: [PATCH 11/19] pack-objects: use bitmaps when packing objects","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-30T08:21:43Z","receivedAt":"2013-10-30T08:21:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 25, 2013 at 02:14:11PM +0000, Shawn O. Pearce wrote:\n\n> On Thu, Oct 24, 2013 at 6:04 PM, Jeff King <peff@peff.net> wrote:\n> > For bitmaps to be used, the following must be true:\n> >\n> >   1. We must be packing to stdout (as a normal `pack-objects` from\n> >      `upload-pack` would do).\n> >\n> >   2. There must be a .bitmap index containing at least one of the\n> >      \"have\" objects that the client is asking for.\n> \n> The client must explicitly \"have\" a commit that has a bitmap? In JGit\n> we allow the client to have anything, and walk backwards using\n> traditional graph traversal until a bitmap is found.\n\nIf the bitmaps contain the full set of reachable objects and the client\ndoes not have any \"haves\" that are bitmapped , then we know that either:\n\n  1. Their \"haves\" are not reachable from the \"wants\"\n\n     or\n\n  2. Their \"wants\" are not bitmapped, and so the slice of \"haves..wants\"\n     has no bitmaps\n\nSince (1) is relatively rare, I think we are using this as a proxy for\n(2), so that we can do a regular walk rather than looking around for\nbitmaps that don't exist. But I may be misremembering the reasoning.\nVicent?\n\n> > @@ -704,6 +759,18 @@ static void write_pack_file(void)\n> >                 offset = write_pack_header(f, nr_remaining);\n> >                 if (!offset)\n> >                         die_errno(\"unable to write pack header\");\n> > +\n> > +               if (reuse_packfile) {\n> > +                       off_t packfile_size;\n> > +                       assert(pack_to_stdout);\n> > +\n> > +                       packfile_size = write_reused_pack(f);\n> > +                       if (!packfile_size)\n> > +                               die_errno(\"failed to re-use existing pack\");\n> > +\n> > +                       offset += packfile_size;\n> > +               }\n> > +\n> >                 nr_written = 0;\n> >                 for (; i < to_pack.nr_objects; i++) {\n> >                         struct object_entry *e = write_order[i];\n> \n> Can reuse_packfile be true at the same time as to_pack.nr_objects > 0?\n\nYes, if there are new, non-bitmapped objects to send on top of the\nreused packfile.\n\n> In JGit we write the to_pack list first, then the reuse pack. Our\n> rationale was the to_pack list is recent objects that are newer and\n> would appear first in a traditional traversal, so they should go at\n> the front of the stream. This does mean if they delta compress against\n> an object in that reuse_packfile slice they have to use REF_DELTA\n> instead of OFS_DELTA.\n\nThat's a good point. In our case I think we do not delta against the\nreused packfile objects at all, as we simply send out the whole slice of\npackfile without making an entry for each object.\n\n> Is this series running on github.com/torvalds/linux? Last Saturday I\n> ran a live demo clone comparing github.com/torvalds/linux to a JGit\n> bitmap clone and some guy heckled me because GitHub was only a few\n> seconds slower. :-)\n\nIt is. Use kernel.org if you want to make fun of someone. :)\n\n-Peff\n"},{"id":"229798","messageId":"20131030082516.GN11317@sigill.intra.peff.net","threadId":"35192","inReplyTo":"87txg4sbyf.fsf@linux-k42r.v.cablecom.net","subject":"Re: [PATCH 07/19] compat: add endianness helpers","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-30T08:25:16Z","receivedAt":"2013-10-30T08:25:16Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Oct 26, 2013 at 09:55:36AM +0200, Thomas Rast wrote:\n\n> > The POSIX standard doesn't currently define a `nothll`/`htonll`\n> \n> typo: ntohll\n\nThanks.\n\n> > function pair to perform network-to-host and host-to-network\n> > swaps of 64-bit data. These 64-bit swaps are necessary for the on-disk\n> > storage of EWAH bitmaps if they are not in native byte order.\n> [...]\n> > +#\t\tinclude <byteswap.h>\n> \n> Do we need a hack on top similar to what ntoh_l and hton_l do, for\n> platforms that do not support unaligned access?\n\nUgh, probably. I didn't even know about those. But we do use them when\nreading the ewah bitmaps, which I believe can be at random offsets. We\nshould be able to use the same ntoh_l solution.\n\n-Peff\n"},{"id":"229803","messageId":"CAJo=hJvOb=oooJ3O0wjxt77caGOMeKwT0VRaV=znMAzm0yNCYw@mail.gmail.com","threadId":"35192","inReplyTo":"20131030075040.GJ11317@sigill.intra.peff.net","subject":"Re: [PATCH 09/19] documentation: add documentation for the bitmap format","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2013-10-30T10:23:10Z","receivedAt":"2013-10-30T10:23:10Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"On Wed, Oct 30, 2013 at 7:50 AM, Jeff King <peff@peff.net> wrote:\n> On Fri, Oct 25, 2013 at 01:47:06PM +0000, Shawn O. Pearce wrote:\n>\n>> I think Colby and I talked about having additional optional sections\n>> in this file, but Colby didn't want to overcomplicate the format early\n>> on. So v1 is probably not very extensible and we may have to go to v2\n>> to safely create an extension with the name hash cache used in this\n>> series.\n>>\n>> Given that the JGit v1 bitmap format has been shipping since JGit 3.0\n>> and in Gerrit Code Review 2.6, its in use in the wild. So we aren't\n>> going to go back and redefine v1.\n>\n> I don't think either course of action affects how JGit in the wild will\n> react. If we add a new flag to v1, existing JGit barfs. If we move to\n> v2, existing JGit barfs.  In either case, the simplest fix for JGit is\n> to ignore the new section.\n\nFair point. Then we can use v1 with the flag for now, JGit will barf and...\n\n> If we want to define a v2 format and make it clear how to add optional\n> sections, that's fine. But what I really want to avoid is a big v2\n> bikeshedding discussion where other features get added because it's a\n> version bump.\n\nWe can defer the v2 bikeshedding until after v1 lands and we get more\nexperience with the format.\n\n> If that means dropping the name-hash cache from the series, so be it\n> (that's part of why I pulled it out into its own patch at the end). We\n> could also make it optional with a documentation warning that existing\n> JGit will not like the resulting bitmap files. In practice, I do not\n> expect it to be a big deal, as most sites will not mix-and-match serving\n> from the two implementations (though you might repack with C git and\n> serve with JGit, which means \"off\" would be the sane default for the\n> feature).\n\nThe name-hash cache is probably important, but it would be nice to\nhave a variable or flag we can use to disable the name-cache\ngeneration and thus permit Git to create JGit style v1 indexes, and\nalso use JGit v1 indexes if the name-cache is not available.\n"},{"id":"229804","messageId":"CAJo=hJudyxLP-pSu-=Py6tSHsnbODDFCZ6JxAoRPtfe3opf=5A@mail.gmail.com","threadId":"35192","inReplyTo":"20131030081023.GK11317@sigill.intra.peff.net","subject":"Re: [PATCH 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2013-10-30T10:27:26Z","receivedAt":"2013-10-30T10:27:26Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"On Wed, Oct 30, 2013 at 8:10 AM, Jeff King <peff@peff.net> wrote:\n> On Fri, Oct 25, 2013 at 01:55:13PM +0000, Shawn O. Pearce wrote:\n>>\n>> Yay! This is similar to the optimization we use in JGit to send the\n>> entire pack, but the part about sending a leading prefix is new. Do\n>> you have any data showing how well this works in practice for cases\n>> where offset is before than length-20?\n>\n> Actually, I don't think it kicks in very much due to packfile ordering.\n> You have all of the commits at the front of the pack, then all of the\n> trees, then all of the blobs. So if you want the whole thing, it is easy\n> to reuse a big chunk. But if you want only the most recent slice, we can\n> reuse the early bit with the new commits, but we stop partway through\n> the commit list. You still have to handle all of the trees and blobs\n> separately.\n>\n> So in practice, I think this really only kicks in for clones anyway.\n>\n> In theory, you could find \"islands\" of ones in the bitmap and send whole\n> slices of packfile at once. But you need to be careful not to send a\n> delta without its base. Which I think means you end up having to\n> generate the whole sha1 list anyway, and check that the other side has\n> each base before reusing a delta (i.e., the normal code path).\n>\n> In fact, I'm not quite sure that even a partial reuse up to an offset is\n> 100% safe. In a newly packed git repo it is, because we always put bases\n> before deltas (and OFS_DELTA objects need this). But if you had a bitmap\n> generated from a fixed thin pack, we would have REF_DELTA objects early\n> on that depend on bases appended to the end of the pack. So I really\n> wonder if we should scrap this partial reuse and either just have full\n> reuse, or go through the regular object_entry construction.\n\nYes. This is why JGit only does whole pack file reuse (minus the 12\nbyte header and 20 byte SHA-1 trailer). Any other case has so many\ncorner cases that we just punt into the object entry construction path\nand rely on individual object reuse.\n"},{"id":"229805","messageId":"CACsJy8B3VS=WSNeF35_JTiF6byZiefvEYnadkC8BAmKG5Z7gQQ@mail.gmail.com","threadId":"35192","inReplyTo":"20131030073627.GG11317@sigill.intra.peff.net","subject":"Re: [PATCH v2 11/19] pack-objects: use bitmaps when packing objects","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-10-30T10:28:08Z","receivedAt":"2013-10-30T10:28:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Oct 30, 2013 at 2:36 PM, Jeff King <peff@peff.net> wrote:\n> On Sat, Oct 26, 2013 at 05:25:14PM +0700, Nguyen Thai Ngoc Duy wrote:\n>\n>> > For bitmaps to be used, the following must be true:\n>> >\n>> >   1. We must be packing to stdout (as a normal `pack-objects` from\n>> >      `upload-pack` would do).\n>> >\n>> >   2. There must be a .bitmap index containing at least one of the\n>> >      \"have\" objects that the client is asking for.\n>> >\n>> >   3. Bitmaps must be enabled (they are enabled by default, but can be\n>> >      disabled by setting `pack.usebitmaps` to false, or by using\n>> >      `--no-use-bitmap-index` on the command-line).\n>> >\n>> > If any of these is not true, we fall back to doing a normal walk of the\n>> > object graph.\n>>\n>> I haven't read the bitmap creation code yet. But it probably does not\n>> matter. If the client requests a shallow fetch, you probably want to\n>> fall back to normal walk too.\n>\n> One other criterion I should have mentioned: we must be using the\n> internal rev-list. That prevented us in v1.8.4.1 and earlier from using\n> bitmaps for shallow fetches. But as of v1.8.4.2, we always use\n> pack-objects' rev-walker. We may need to pass --no-use-bitmap-index for\n> shallow fetches.\n\nI don't think a new option is needed. The code just needs to check if\nthere are any commit grafts. If there are, fall back to the old way.\nThat covers both shallow fetches and some rare grafted repos. I think\nrefs/replace/* does not impact rev walking, so we should be fine if\nit's used.\n\n> As for repos that are themselves shallow, I do not know how doing a\n> \"repack -b\" would fare. Probably not well.\n-- \nDuy\n"},{"id":"229806","messageId":"CAJo=hJs0uUdDfdo9g-FeUmed5Z4+S+spPb+4OL8NipN-GXxuxQ@mail.gmail.com","threadId":"35192","inReplyTo":"20131030082143.GM11317@sigill.intra.peff.net","subject":"Re: [PATCH 11/19] pack-objects: use bitmaps when packing objects","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2013-10-30T10:38:57Z","receivedAt":"2013-10-30T10:38:57Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"On Wed, Oct 30, 2013 at 8:21 AM, Jeff King <peff@peff.net> wrote:\n> On Fri, Oct 25, 2013 at 02:14:11PM +0000, Shawn O. Pearce wrote:\n>> On Thu, Oct 24, 2013 at 6:04 PM, Jeff King <peff@peff.net> wrote:\n>> > For bitmaps to be used, the following must be true:\n>> >\n>> >   1. We must be packing to stdout (as a normal `pack-objects` from\n>> >      `upload-pack` would do).\n>> >\n>> >   2. There must be a .bitmap index containing at least one of the\n>> >      \"have\" objects that the client is asking for.\n>>\n>> The client must explicitly \"have\" a commit that has a bitmap? In JGit\n>> we allow the client to have anything, and walk backwards using\n>> traditional graph traversal until a bitmap is found.\n>\n> If the bitmaps contain the full set of reachable objects and the client\n> does not have any \"haves\" that are bitmapped , then we know that either:\n>\n>   1. Their \"haves\" are not reachable from the \"wants\"\n>\n>      or\n>\n>   2. Their \"wants\" are not bitmapped, and so the slice of \"haves..wants\"\n>      has no bitmaps\n>\n> Since (1) is relatively rare, I think we are using this as a proxy for\n> (2), so that we can do a regular walk rather than looking around for\n> bitmaps that don't exist. But I may be misremembering the reasoning.\n> Vicent?\n\nAh. I am not sure if we do this in JGit. I think JGit's approach is to\nlook if the have appears in a pack with bitmaps, this is a simple\nlookup in the .idx file and does not require expanding any data from\nthe .bitmap file.\n\n\nBut it wasn't my question. :-)\n\nClient sends \"want B ; have E\". What if E appears in the bitmapped\npack, but does not itself have a bitmap? Do you walk backwards from B\nand switch to the bitmap algorithm when you find a commit that has a\nbitmap and that bitmap contains E?\n\n>> In JGit we write the to_pack list first, then the reuse pack. Our\n>> rationale was the to_pack list is recent objects that are newer and\n>> would appear first in a traditional traversal, so they should go at\n>> the front of the stream. This does mean if they delta compress against\n>> an object in that reuse_packfile slice they have to use REF_DELTA\n>> instead of OFS_DELTA.\n>\n> That's a good point. In our case I think we do not delta against the\n> reused packfile objects at all, as we simply send out the whole slice of\n> packfile without making an entry for each object.\n\nJGit also doesn't use the reused packfile as delta bases, because\nthere are no object entries to shove through the delta window. So\nthere is never any risk of a reference to a base later in the file. It\nalso means that \"thin pack\" at the front of the stream is less\noptimally compressed. At Google we side step that by doing GC at the\nserver very often, to try and keep the number of objects in that pack\nlow.\n\nIt might make sense to use a commit that covers the majority of the\nreused pack as the edge base candidate case during delta compression\nhere, as though the client had sent us a \"have\" for that commit. I\ndon't think we have tried this in JGit. It would make deltas use\nREF_DELTA, but the delta size has to be smaller than the REF_DELTA\nheader to be used in the stream so its still a smaller overall\ntransfer.\n\n>> Is this series running on github.com/torvalds/linux? Last Saturday I\n>> ran a live demo clone comparing github.com/torvalds/linux to a JGit\n>> bitmap clone and some guy heckled me because GitHub was only a few\n>> seconds slower. :-)\n>\n> It is. Use kernel.org if you want to make fun of someone. :)\n\nHah. OK, so GitHub was only a few seconds slower only because my\ndesktop is better connected to our data centers than to yours. Nicely\ndone, this patch series really works. :)\n"},{"id":"229821","messageId":"CAFFjANQyMfV4M_wynPORfN2S1=eAdBxScgNYzD_dsRmKekp83Q@mail.gmail.com","threadId":"35192","inReplyTo":"20131030081023.GK11317@sigill.intra.peff.net","subject":"Re: [PATCH 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Vicent Marti","fromEmail":"vicent@github.com","sentAt":"2013-10-30T15:47:57Z","receivedAt":"2013-10-30T15:47:57Z","isPatch":true,"sender":{"key":"vicent@github.com","avatar":"https://gravatar.com/avatar/9d57a2b1e3137bf84342ac1dfdf1cde409b86e8fda5397d05f40f17fa5b84a63?d=mp&s=160"},"body":"On Wed, Oct 30, 2013 at 9:10 AM, Jeff King <peff@peff.net> wrote:\n>\n> In fact, I'm not quite sure that even a partial reuse up to an offset is\n> 100% safe. In a newly packed git repo it is, because we always put bases\n> before deltas (and OFS_DELTA objects need this). But if you had a bitmap\n> generated from a fixed thin pack, we would have REF_DELTA objects early\n> on that depend on bases appended to the end of the pack. So I really\n> wonder if we should scrap this partial reuse and either just have full\n> reuse, or go through the regular object_entry construction.\n>\n> Vicent, you've thought about the reuse code a lot more than I have. Any\n> thoughts?\n\nYes, our pack writing and bitmap code takes enough precautions to\narrange the objects in the packfile in a way that can be partially\nreused, so for any given bitmap file written from Git, I'd say we're\nsafe to always reuse the leader of the pack if this is possible.\n\nFor bitmaps generated from JGit, however, we cannot make this\nassumption. I mean, we can right now (from my understanding of the\ncurrent implementation for pack-objects on JGit), but they are free to\nchange this in the future.\n\nObviously I intend to keep the pack reuse on production because the\nCPU savings are noticeable, but we can drop it from the public\npatchset. Ideally, we'd have full pack reuse like JGit, but we cannot\nreasonably do that in GitHub because splitting a pack for the network\nroot would double our disk usage for all the forks.\n\nluv,\nvmg\n"},{"id":"229823","messageId":"CAFFjANQYsULx+Cr68_xrA-jB2WvJqw-KRa2hbHsA7WaLKFowEg@mail.gmail.com","threadId":"35192","inReplyTo":"CAJo=hJs0uUdDfdo9g-FeUmed5Z4+S+spPb+4OL8NipN-GXxuxQ@mail.gmail.com","subject":"Re: [PATCH 11/19] pack-objects: use bitmaps when packing objects","fromName":"Vicent Marti","fromEmail":"vicent@github.com","sentAt":"2013-10-30T16:01:17Z","receivedAt":"2013-10-30T16:01:17Z","isPatch":true,"sender":{"key":"vicent@github.com","avatar":"https://gravatar.com/avatar/9d57a2b1e3137bf84342ac1dfdf1cde409b86e8fda5397d05f40f17fa5b84a63?d=mp&s=160"},"body":"On Wed, Oct 30, 2013 at 11:38 AM, Shawn Pearce <spearce@spearce.org> wrote:\n>> Since (1) is relatively rare, I think we are using this as a proxy for\n>> (2), so that we can do a regular walk rather than looking around for\n>> bitmaps that don't exist. But I may be misremembering the reasoning.\n>> Vicent?\n>\n> Ah. I am not sure if we do this in JGit. I think JGit's approach is to\n> look if the have appears in a pack with bitmaps, this is a simple\n> lookup in the .idx file and does not require expanding any data from\n> the .bitmap file.\n\nNo, you don't do this in JGit right now. This is a small optimization\nwe implemented to prevent *loading* the bitmap file (and hence\nbuilding the reverse index, which can be expensive) unless we're sure\nwe can at least *attempt* a bitmap walk.\n\n> But it wasn't my question. :-)\n>\n> Client sends \"want B ; have E\". What if E appears in the bitmapped\n> pack, but does not itself have a bitmap? Do you walk backwards from B\n> and switch to the bitmap algorithm when you find a commit that has a\n> bitmap and that bitmap contains E?\n\nThis is correct, we use the same heuristics as JGit. Once we have\nloaded a bitmap file we know we can attempt a bitmap walk; if E is on\nthe bitmapped pack, we'll build a temporary bitmap using an extended\nindex (with bits for commits that are not in the packfile) as we walk\nbackwards until E. Once we find a commit with a bitmap, we'll OR that\nwith the temporary bitmap to skip the full walk.\n\n>>> In JGit we write the to_pack list first, then the reuse pack. Our\n>>> rationale was the to_pack list is recent objects that are newer and\n>>> would appear first in a traditional traversal, so they should go at\n>>> the front of the stream. This does mean if they delta compress against\n>>> an object in that reuse_packfile slice they have to use REF_DELTA\n>>> instead of OFS_DELTA.\n>>\n>> That's a good point. In our case I think we do not delta against the\n>> reused packfile objects at all, as we simply send out the whole slice of\n>> packfile without making an entry for each object.\n>\n> JGit also doesn't use the reused packfile as delta bases, because\n> there are no object entries to shove through the delta window. So\n> there is never any risk of a reference to a base later in the file. It\n> also means that \"thin pack\" at the front of the stream is less\n> optimally compressed. At Google we side step that by doing GC at the\n> server very often, to try and keep the number of objects in that pack\n> low.\n>\n> It might make sense to use a commit that covers the majority of the\n> reused pack as the edge base candidate case during delta compression\n> here, as though the client had sent us a \"have\" for that commit. I\n> don't think we have tried this in JGit. It would make deltas use\n> REF_DELTA, but the delta size has to be smaller than the REF_DELTA\n> header to be used in the stream so its still a smaller overall\n> transfer.\n>\n>>> Is this series running on github.com/torvalds/linux? Last Saturday I\n>>> ran a live demo clone comparing github.com/torvalds/linux to a JGit\n>>> bitmap clone and some guy heckled me because GitHub was only a few\n>>> seconds slower. :-)\n>>\n>> It is. Use kernel.org if you want to make fun of someone. :)\n>\n> Hah. OK, so GitHub was only a few seconds slower only because my\n> desktop is better connected to our data centers than to yours. Nicely\n> done, this patch series really works. :)\n\nThanks Shawn, your feedback was very helpful.\n\nRe. cloning speeds: we are actually bottlenecked by our routing layer.\nThe CPUs in our new fileservers can clone `torvalds/linux` to\n/dev/null in 20s, but we're slowing down when routing the actual\ntraffic back to the client. We're in the process of rewriting our Git\nreverse proxys so let's see what the future looks like.\n\nLove,\nvmg\n"},{"id":"229824","messageId":"CAJo=hJvm_sZqobzO0Er-OVp-Xkf4fT_tySQeNWf0mveHH-G-Xg@mail.gmail.com","threadId":"35192","inReplyTo":"CAFFjANQyMfV4M_wynPORfN2S1=eAdBxScgNYzD_dsRmKekp83Q@mail.gmail.com","subject":"Re: [PATCH 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2013-10-30T16:04:06Z","receivedAt":"2013-10-30T16:04:06Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"On Wed, Oct 30, 2013 at 3:47 PM, Vicent Marti <vicent@github.com> wrote:\n> On Wed, Oct 30, 2013 at 9:10 AM, Jeff King <peff@peff.net> wrote:\n>>\n>> In fact, I'm not quite sure that even a partial reuse up to an offset is\n>> 100% safe. In a newly packed git repo it is, because we always put bases\n>> before deltas (and OFS_DELTA objects need this). But if you had a bitmap\n>> generated from a fixed thin pack, we would have REF_DELTA objects early\n>> on that depend on bases appended to the end of the pack. So I really\n>> wonder if we should scrap this partial reuse and either just have full\n>> reuse, or go through the regular object_entry construction.\n>>\n>> Vicent, you've thought about the reuse code a lot more than I have. Any\n>> thoughts?\n>\n> Yes, our pack writing and bitmap code takes enough precautions to\n> arrange the objects in the packfile in a way that can be partially\n> reused, so for any given bitmap file written from Git, I'd say we're\n> safe to always reuse the leader of the pack if this is possible.\n>\n> For bitmaps generated from JGit, however, we cannot make this\n> assumption. I mean, we can right now (from my understanding of the\n> current implementation for pack-objects on JGit), but they are free to\n> change this in the future.\n\nJGit certainly doesn't promise the ordering behavior, so the fact that\nits happening is just luck. The code could change in the future to\ninvalidate this.\n\n> Obviously I intend to keep the pack reuse on production because the\n> CPU savings are noticeable, but we can drop it from the public\n> patchset.\n\nI think you should keep it in, its a significant improvement.\n\n> Ideally, we'd have full pack reuse like JGit, but we cannot\n> reasonably do that in GitHub because splitting a pack for the network\n> root would double our disk usage for all the forks.\n\nI gave a talk the week before about Git bitmaps and why we sometimes\nhave to slice pack files by object.\n\nSome guy in the audience kept yelling that since its Git its all open\nsource and `git clone` is \"just\" a file transfer problem. So maybe for\nhis GitHub repositories and forks its OK to include the entire fork\nnetwork when someone clones?  :-)\n"},{"id":"229826","messageId":"CAFFjANSjFRwRYep_itVBAHuR2jYm=nspRWcUY02zXsEK=V5Reg@mail.gmail.com","threadId":"35192","inReplyTo":"CAJo=hJvOb=oooJ3O0wjxt77caGOMeKwT0VRaV=znMAzm0yNCYw@mail.gmail.com","subject":"Re: [PATCH 09/19] documentation: add documentation for the bitmap format","fromName":"Vicent Marti","fromEmail":"vicent@github.com","sentAt":"2013-10-30T16:11:45Z","receivedAt":"2013-10-30T16:11:45Z","isPatch":true,"sender":{"key":"vicent@github.com","avatar":"https://gravatar.com/avatar/9d57a2b1e3137bf84342ac1dfdf1cde409b86e8fda5397d05f40f17fa5b84a63?d=mp&s=160"},"body":"On Wed, Oct 30, 2013 at 11:23 AM, Shawn Pearce <spearce@spearce.org> wrote:\n> On Wed, Oct 30, 2013 at 7:50 AM, Jeff King <peff@peff.net> wrote:\n>> On Fri, Oct 25, 2013 at 01:47:06PM +0000, Shawn O. Pearce wrote:\n>>\n>>> I think Colby and I talked about having additional optional sections\n>>> in this file, but Colby didn't want to overcomplicate the format early\n>>> on. So v1 is probably not very extensible and we may have to go to v2\n>>> to safely create an extension with the name hash cache used in this\n>>> series.\n>>>\n>>> Given that the JGit v1 bitmap format has been shipping since JGit 3.0\n>>> and in Gerrit Code Review 2.6, its in use in the wild. So we aren't\n>>> going to go back and redefine v1.\n>>\n>> I don't think either course of action affects how JGit in the wild will\n>> react. If we add a new flag to v1, existing JGit barfs. If we move to\n>> v2, existing JGit barfs.  In either case, the simplest fix for JGit is\n>> to ignore the new section.\n>\n> Fair point. Then we can use v1 with the flag for now, JGit will barf and...\n\nShawn, I'm proposing the following patch to JGit (actually Kevin is,\nbecause I don't have the CLA, but whatevs):\n\nhttps://git.eclipse.org/r/#/c/17894/\n\nIt's a very small change (using an \"and\" to check for the flags on the\nbitmap instead of a switch), but I think it's very clean. In the\nspirit of \"Be conservative in what you send, be liberal in what you\naccept\", this patch lets JGit read V1 bitmaps emited from git.git\n*even* if they have the extended Name Cache extension, and has no\neffect on any JGit bitmap that has been generated up to date, or any\nJGit bitmap generated from git.git without name caches.\n\nI don't think it *changes* the semantics of the bitmap V1 format,\nbecause with only one bit value set so far, those semantics weren't\nreally there, and it'll allow newer versions of JGit to never barf.\n\nLove,\nvmg\n"},{"id":"229827","messageId":"CAFFjANRrC8S+Wqnxr7K7HRzLas8BdsWipoToDUyeCY=s9wgdVg@mail.gmail.com","threadId":"35192","inReplyTo":"CAJo=hJvOb=oooJ3O0wjxt77caGOMeKwT0VRaV=znMAzm0yNCYw@mail.gmail.com","subject":"Re: [PATCH 09/19] documentation: add documentation for the bitmap format","fromName":"Vicent Marti","fromEmail":"vicent@github.com","sentAt":"2013-10-30T16:14:47Z","receivedAt":"2013-10-30T16:14:47Z","isPatch":true,"sender":{"key":"vicent@github.com","avatar":"https://gravatar.com/avatar/9d57a2b1e3137bf84342ac1dfdf1cde409b86e8fda5397d05f40f17fa5b84a63?d=mp&s=160"},"body":"On Wed, Oct 30, 2013 at 11:23 AM, Shawn Pearce <spearce@spearce.org> wrote:\n> The name-hash cache is probably important, but it would be nice to\n> have a variable or flag we can use to disable the name-cache\n> generation and thus permit Git to create JGit style v1 indexes, and\n> also use JGit v1 indexes if the name-cache is not available.\n\nYes, we'll reroll the patches with name-hash cache writing disabled by\ndefault. I still think it'd be nice for JGit to be able to ignore the\nflag in the cases where it's there.\n"},{"id":"229831","messageId":"A98A12FA-A198-46ED-9470-E0E8284EBE15@gmail.com","threadId":"35192","inReplyTo":"20131030082516.GN11317@sigill.intra.peff.net","subject":"Re: [PATCH 07/19] compat: add endianness helpers","fromName":"Vicent Martí","fromEmail":"tanoku@gmail.com","sentAt":"2013-10-30T17:06:52Z","receivedAt":"2013-10-30T17:06:52Z","isPatch":true,"sender":{"key":"tanoku@gmail.com","avatar":"https://gravatar.com/avatar/271386991cb4c2b8f1e1ed1d059f3422cc3485de7a598f65043f70be021d095b?d=mp&s=160"},"body":"On Oct 30, 2013, at 9:25 AM, Jeff King <peff@peff.net> wrote:\n\n> On Sat, Oct 26, 2013 at 09:55:36AM +0200, Thomas Rast wrote:\n> \n>>> The POSIX standard doesn't currently define a `nothll`/`htonll`\n>> \n>> typo: ntohll\n> \n> Thanks.\n> \n>>> function pair to perform network-to-host and host-to-network\n>>> swaps of 64-bit data. These 64-bit swaps are necessary for the on-disk\n>>> storage of EWAH bitmaps if they are not in native byte order.\n>> [...]\n>>> +#\t\tinclude <byteswap.h>\n>> \n>> Do we need a hack on top similar to what ntoh_l and hton_l do, for\n>> platforms that do not support unaligned access?\n> \n> Ugh, probably. I didn't even know about those. But we do use them when\n> reading the ewah bitmaps, which I believe can be at random offsets. We\n> should be able to use the same ntoh_l solution.\n> \n> -Peff\n\nYou are right, when reading mmaps we cannot ensure the alignment of the 8 byte sections. For writing the bitmaps to memory, however, we can make that assumption because the writes will happen to either malloc'ed segments, or the stack.\n\nA proposed fix follows:\n\n>From 0eed934805543390195f8aee231f8a1da33dad0b Mon Sep 17 00:00:00 2001\nFrom: Vicent Marti <tanoku@gmail.com>\nDate: Wed, 30 Oct 2013 15:27:20 +0100\nSubject: [PATCH] ewah: support non-aligned reads\n\nWhen reading straight from a mmaped file, some platforms do not support\natomically loading 8 bytes from random positions on memory to perform a\nbyte swap. To work around this, we `memcpy` the whole area that needs\nto be byteswapped to its final destination, and then perform the\nbyteswap on each 8-byte chunk: the destionation chunks *will* be aligned\nbecause the destination area is a malloc'ed chunk of memory.\n\nSigned-off-by: Vicent Marti <tanoku@gmail.com>\n---\n ewah/ewah_io.c | 24 ++++++++++++++++++------\n 1 file changed, 18 insertions(+), 6 deletions(-)\n\ndiff --git a/ewah/ewah_io.c b/ewah/ewah_io.c\nindex fca93f9..41f6d3d 100644\n--- a/ewah/ewah_io.c\n+++ b/ewah/ewah_io.c\n@@ -76,6 +76,10 @@ int ewah_serialize_to(struct ewah_bitmap *self,\n \tbuffer = self->buffer;\n \twords_left = self->buffer_size;\n \n+\t/* buffer is a malloc'ed buffer, which we can assume to\n+\t * be at the very least 8-byte aligned */\n+\tassert((intptr_t)buffer % 8 == 0);\n+\n \twhile (words_left >= words_per_dump) {\n \t\tfor (i = 0; i < words_per_dump; ++i, ++buffer)\n \t\t\tdump[i] = htonll(*buffer);\n@@ -117,7 +121,6 @@ int ewah_serialize(struct ewah_bitmap *self, int fd)\n int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len)\n {\n \tuint32_t *read32 = map;\n-\teword_t *read64;\n \tsize_t i;\n \n \tself->bit_size = ntohl(*read32++);\n@@ -128,10 +131,19 @@ int ewah_read_mmap(struct ewah_bitmap *self, void *map, size_t len)\n \tif (!self->buffer)\n \t\treturn -1;\n \n-\tfor (i = 0, read64 = (void *)read32; i < self->buffer_size; ++i)\n-\t\tself->buffer[i] = ntohll(*read64++);\n+#ifdef NEEDS_ALIGNED_ACCESS\n+\tmemcpy(self->buffer, read32, self->buffer_size * sizeof(eword_t));\n+\tfor (i = 0; i < self->buffer_size; ++i)\n+\t\tself->buffer[i] = ntohll(self->buffer[i]);\n+#else\n+\t{\n+\t\teword_t *read64 = (void *)read32;\n+\t\tfor (i = 0; i < self->buffer_size; ++i)\n+\t\t\tself->buffer[i] = ntohll(read64[i]);\n+\t}\n+#endif\n \n-\tread32 = (void *)read64;\n+\tread32 += self->buffer_size * sizeof(eword_t) / sizeof(uint32_t);\n \tself->rlw = self->buffer + ntohl(*read32++);\n \n \treturn (3 * 4) + (self->buffer_size * 8);\n@@ -175,7 +187,7 @@ int ewah_deserialize(struct ewah_bitmap *self, int fd)\n \t\t\treturn -1;\n \n \t\tfor (i = 0; i < words_per_dump; ++i, ++buffer)\n-\t\t\t*buffer = ntohll(dump[i]);\n+\t\t\t*buffer = ntohll(dump[i]); /* aligned on stack */\n \n \t\twords_left -= words_per_dump;\n \t}\n@@ -185,7 +197,7 @@ int ewah_deserialize(struct ewah_bitmap *self, int fd)\n \t\t\treturn -1;\n \n \t\tfor (i = 0; i < words_left; ++i, ++buffer)\n-\t\t\t*buffer = ntohll(dump[i]);\n+\t\t\t*buffer = ntohll(dump[i]); /* aligned on stack */\n \t}\n \n \t/** 32 bit -- position for the RLW */\n-- \n1.8.3.2\n"},{"id":"229839","messageId":"xmqq61sed5k2.fsf@gitster.dls.corp.google.com","threadId":"35192","inReplyTo":"20131030065922.GC11317@sigill.intra.peff.net","subject":"Re: [PATCH 20/19] count-objects: consider .bitmap without .pack/.idx pair garbage","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-10-30T17:36:29Z","receivedAt":"2013-10-30T17:36:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Sat, Oct 26, 2013 at 05:19:56PM +0700, Nguyen Thai Ngoc Duy wrote:\n>\n>>  Maybe squash this in some place, or leave it separate. I'm fine either way.\n>\n> Thanks, this makes sense.  I've left it as a separate commit.\n>\n> Your subject did not quite make sense to me, as the effect is to _not_\n> report .bitmap files as garbage. I updated the commit message to this:\n>\n>     count-objects: recognize .bitmap in garbage-checking\n>     \n>     Count-objects will report any \"garbage\" files in the packs\n>     directory, including files whose extensions it does not\n>     know (case 1), and files whose matching \".pack\" file is\n>     missing (case 2).  Without having learned about \".bitmap\"\n>     files, the current code reports all such files as garbage\n>     (case 1), even if their pack exists. Instead, they should be\n>     treated as case 2.\n\nThanks; that reads well.\n"},{"id":"229860","messageId":"20131030200754.GB23011@sigill.intra.peff.net","threadId":"35192","inReplyTo":"CACsJy8B3VS=WSNeF35_JTiF6byZiefvEYnadkC8BAmKG5Z7gQQ@mail.gmail.com","subject":"Re: [PATCH v2 11/19] pack-objects: use bitmaps when packing objects","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-30T20:07:54Z","receivedAt":"2013-10-30T20:07:54Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Oct 30, 2013 at 05:28:08PM +0700, Nguyen Thai Ngoc Duy wrote:\n\n> > One other criterion I should have mentioned: we must be using the\n> > internal rev-list. That prevented us in v1.8.4.1 and earlier from using\n> > bitmaps for shallow fetches. But as of v1.8.4.2, we always use\n> > pack-objects' rev-walker. We may need to pass --no-use-bitmap-index for\n> > shallow fetches.\n> \n> I don't think a new option is needed. The code just needs to check if\n> there are any commit grafts. If there are, fall back to the old way.\n> That covers both shallow fetches and some rare grafted repos. I think\n> refs/replace/* does not impact rev walking, so we should be fine if\n> it's used.\n\nI think there are two cases that we need to consider:\n\n  1. We have a full repo and somebody requests a shallow clone for us.\n     We probably do not want to use bitmaps here.  In the series we have\n     been testing, shallow clones turned off bitmaps because we do not\n     use the internal rev_list. But as of cdab485 (upload-pack: delegate\n     rev walking in shallow fetch to pack-objects), that distinction\n     doesn't hold. I think we can check the use of --shallow-file\n     instead of explicitly turning off bitmaps there.\n\n  2. We have a shallow clone that wants to repack. We probably want to\n     turn off bitmap writing here. I don't think that grafts actually\n     matter here, because pack-objects should always be looking at the\n     true graph. It would mean that using \"git rev-list\n     --use-bitmap-index\" does not respect the grafts, and we should\n     probably disable it in that case (and ditto for replacements).\n\n-Peff\n"},{"id":"229862","messageId":"20131030202542.GD23011@sigill.intra.peff.net","threadId":"35192","inReplyTo":"CAFFjANQyMfV4M_wynPORfN2S1=eAdBxScgNYzD_dsRmKekp83Q@mail.gmail.com","subject":"Re: [PATCH 10/19] pack-bitmap: add support for bitmap indexes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-10-30T20:25:43Z","receivedAt":"2013-10-30T20:25:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Oct 30, 2013 at 04:47:57PM +0100, Vicent Martí wrote:\n\n> Yes, our pack writing and bitmap code takes enough precautions to\n> arrange the objects in the packfile in a way that can be partially\n> reused, so for any given bitmap file written from Git, I'd say we're\n> safe to always reuse the leader of the pack if this is possible.\n\nPerhaps it is worth adding a flag to the bitmap index to say \"my bases\nalways come before deltas\".\n\n> Obviously I intend to keep the pack reuse on production because the\n> CPU savings are noticeable, but we can drop it from the public\n> patchset. Ideally, we'd have full pack reuse like JGit, but we cannot\n> reasonably do that in GitHub because splitting a pack for the network\n> root would double our disk usage for all the forks.\n\nAh, right, I was forgetting about the fork-network repositories in my\nanalysis.  This doesn't work out of the box with this series, because we\nwill have all of the commits for all of the forks, followed by all of\nthe trees, and so forth.\n\nWhat the list hasn't seen yet is that we have another patch series that\norganizes the objects based on refs, so you can put all of\n\"refs/remotes/X/*\" in one \"island\", \"refs/remotes/Y/*\" in another\n\"island\", and so forth (in our scheme, each remote represents a single\nfork, but it's configurable).\n\nThis lets us avoid deltas to objects that are not in the same island,\nbecause such deltas would need to be thrown out when cloning a single\nfork. And it lets us tweak the write order so that we can put all of one\nfork's objects at the beginning, making it available for pack-reuse (so\nobviously you would want to pick the \"root\" fork to put at the\nbeginning).\n\nI'm planning to send those patches once the bitmap code is merged. One\nis not strictly dependent on the other, but they touch some of the same\nareas, and rely on some of the same infrastructure, so I think it makes\nsense to do them in series.\n\n-Peff\n"},{"id":"229915","messageId":"CACsJy8DMskq8R5rfz8MFsnB5CRuX=b0Opf31uby7mB22Hg+c2g@mail.gmail.com","threadId":"35192","inReplyTo":"20131030200754.GB23011@sigill.intra.peff.net","subject":"Re: [PATCH v2 11/19] pack-objects: use bitmaps when packing objects","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2013-10-31T12:03:03Z","receivedAt":"2013-10-31T12:03:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Oct 31, 2013 at 3:07 AM, Jeff King <peff@peff.net> wrote:\n> I think there are two cases that we need to consider:\n>\n>   1. We have a full repo and somebody requests a shallow clone for us.\n>      We probably do not want to use bitmaps here.  In the series we have\n>      been testing, shallow clones turned off bitmaps because we do not\n>      use the internal rev_list. But as of cdab485 (upload-pack: delegate\n>      rev walking in shallow fetch to pack-objects), that distinction\n>      doesn't hold. I think we can check the use of --shallow-file\n>      instead of explicitly turning off bitmaps there.\n\nThere's an (non-existing yet) case 1': somebody requests a clone and\nthe source clone is already shallow. is_repository_shallow() could\ncatch both cases.\n\n>   2. We have a shallow clone that wants to repack. We probably want to\n>      turn off bitmap writing here. I don't think that grafts actually\n>      matter here, because pack-objects should always be looking at the\n>      true graph. It would mean that using \"git rev-list\n>      --use-bitmap-index\" does not respect the grafts, and we should\n>      probably disable it in that case (and ditto for replacements).\n\nRight. I forgot that the repo must be complete before it's grafted.\n-- \nDuy\n"}]}