]> git.ipfire.org Git - thirdparty/git.git/blobdiff - pack-revindex.c
The tenth batch
[thirdparty/git.git] / pack-revindex.c
index a508d5f0a4df27c53b2271aaff4dceb242cecd45..83fe4de77382a1089b62972e3fa5d7771c1a0790 100644 (file)
@@ -2,6 +2,7 @@
 #include "pack-revindex.h"
 #include "object-store.h"
 #include "packfile.h"
+#include "config.h"
 
 struct revindex_entry {
        off_t offset;
@@ -164,34 +165,151 @@ static void create_pack_revindex(struct packed_git *p)
        sort_revindex(p->revindex, num_ent, p->pack_size);
 }
 
-int load_pack_revindex(struct packed_git *p)
+static int create_pack_revindex_in_memory(struct packed_git *p)
 {
-       if (!p->revindex) {
-               if (open_pack_index(p))
-                       return -1;
-               create_pack_revindex(p);
-       }
+       if (git_env_bool(GIT_TEST_REV_INDEX_DIE_IN_MEMORY, 0))
+               die("dying as requested by '%s'",
+                   GIT_TEST_REV_INDEX_DIE_IN_MEMORY);
+       if (open_pack_index(p))
+               return -1;
+       create_pack_revindex(p);
        return 0;
 }
 
+static char *pack_revindex_filename(struct packed_git *p)
+{
+       size_t len;
+       if (!strip_suffix(p->pack_name, ".pack", &len))
+               BUG("pack_name does not end in .pack");
+       return xstrfmt("%.*s.rev", (int)len, p->pack_name);
+}
+
+#define RIDX_HEADER_SIZE (12)
+#define RIDX_MIN_SIZE (RIDX_HEADER_SIZE + (2 * the_hash_algo->rawsz))
+
+struct revindex_header {
+       uint32_t signature;
+       uint32_t version;
+       uint32_t hash_id;
+};
+
+static int load_revindex_from_disk(char *revindex_name,
+                                  uint32_t num_objects,
+                                  const uint32_t **data_p, size_t *len_p)
+{
+       int fd, ret = 0;
+       struct stat st;
+       void *data = NULL;
+       size_t revindex_size;
+       struct revindex_header *hdr;
+
+       fd = git_open(revindex_name);
+
+       if (fd < 0) {
+               ret = -1;
+               goto cleanup;
+       }
+       if (fstat(fd, &st)) {
+               ret = error_errno(_("failed to read %s"), revindex_name);
+               goto cleanup;
+       }
+
+       revindex_size = xsize_t(st.st_size);
+
+       if (revindex_size < RIDX_MIN_SIZE) {
+               ret = error(_("reverse-index file %s is too small"), revindex_name);
+               goto cleanup;
+       }
+
+       if (revindex_size - RIDX_MIN_SIZE != st_mult(sizeof(uint32_t), num_objects)) {
+               ret = error(_("reverse-index file %s is corrupt"), revindex_name);
+               goto cleanup;
+       }
+
+       data = xmmap(NULL, revindex_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       hdr = data;
+
+       if (ntohl(hdr->signature) != RIDX_SIGNATURE) {
+               ret = error(_("reverse-index file %s has unknown signature"), revindex_name);
+               goto cleanup;
+       }
+       if (ntohl(hdr->version) != 1) {
+               ret = error(_("reverse-index file %s has unsupported version %"PRIu32),
+                           revindex_name, ntohl(hdr->version));
+               goto cleanup;
+       }
+       if (!(ntohl(hdr->hash_id) == 1 || ntohl(hdr->hash_id) == 2)) {
+               ret = error(_("reverse-index file %s has unsupported hash id %"PRIu32),
+                           revindex_name, ntohl(hdr->hash_id));
+               goto cleanup;
+       }
+
+cleanup:
+       if (ret) {
+               if (data)
+                       munmap(data, revindex_size);
+       } else {
+               *len_p = revindex_size;
+               *data_p = (const uint32_t *)data;
+       }
+
+       close(fd);
+       return ret;
+}
+
+static int load_pack_revindex_from_disk(struct packed_git *p)
+{
+       char *revindex_name;
+       int ret;
+       if (open_pack_index(p))
+               return -1;
+
+       revindex_name = pack_revindex_filename(p);
+
+       ret = load_revindex_from_disk(revindex_name,
+                                     p->num_objects,
+                                     &p->revindex_map,
+                                     &p->revindex_size);
+       if (ret)
+               goto cleanup;
+
+       p->revindex_data = (const uint32_t *)((const char *)p->revindex_map + RIDX_HEADER_SIZE);
+
+cleanup:
+       free(revindex_name);
+       return ret;
+}
+
+int load_pack_revindex(struct packed_git *p)
+{
+       if (p->revindex || p->revindex_data)
+               return 0;
+
+       if (!load_pack_revindex_from_disk(p))
+               return 0;
+       else if (!create_pack_revindex_in_memory(p))
+               return 0;
+       return -1;
+}
+
 int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos)
 {
        unsigned lo, hi;
-       const struct revindex_entry *revindex;
 
        if (load_pack_revindex(p) < 0)
                return -1;
 
        lo = 0;
        hi = p->num_objects + 1;
-       revindex = p->revindex;
 
        do {
                const unsigned mi = lo + (hi - lo) / 2;
-               if (revindex[mi].offset == ofs) {
+               off_t got = pack_pos_to_offset(p, mi);
+
+               if (got == ofs) {
                        *pos = mi;
                        return 0;
-               } else if (ofs < revindex[mi].offset)
+               } else if (ofs < got)
                        hi = mi;
                else
                        lo = mi + 1;
@@ -203,18 +321,28 @@ int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos)
 
 uint32_t pack_pos_to_index(struct packed_git *p, uint32_t pos)
 {
-       if (!p->revindex)
+       if (!(p->revindex || p->revindex_data))
                BUG("pack_pos_to_index: reverse index not yet loaded");
        if (p->num_objects <= pos)
                BUG("pack_pos_to_index: out-of-bounds object at %"PRIu32, pos);
-       return p->revindex[pos].nr;
+
+       if (p->revindex)
+               return p->revindex[pos].nr;
+       else
+               return get_be32(p->revindex_data + pos);
 }
 
 off_t pack_pos_to_offset(struct packed_git *p, uint32_t pos)
 {
-       if (!p->revindex)
+       if (!(p->revindex || p->revindex_data))
                BUG("pack_pos_to_index: reverse index not yet loaded");
        if (p->num_objects < pos)
                BUG("pack_pos_to_offset: out-of-bounds object at %"PRIu32, pos);
-       return p->revindex[pos].offset;
+
+       if (p->revindex)
+               return p->revindex[pos].offset;
+       else if (pos == p->num_objects)
+               return p->pack_size - the_hash_algo->rawsz;
+       else
+               return nth_packed_object_offset(p, pack_pos_to_index(p, pos));
 }