diff options
Diffstat (limited to 'sha1_file.c')
| -rw-r--r-- | sha1_file.c | 87 |
1 files changed, 84 insertions, 3 deletions
diff --git a/sha1_file.c b/sha1_file.c index 110d696213..b0b21776e7 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1352,6 +1352,87 @@ static void *unpack_compressed_entry(struct packed_git *p, return buffer; } +#define MAX_DELTA_CACHE (256) + +static size_t delta_base_cached; +static struct delta_base_cache_entry { + struct packed_git *p; + off_t base_offset; + unsigned long size; + void *data; + enum object_type type; +} delta_base_cache[MAX_DELTA_CACHE]; + +static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset) +{ + unsigned long hash; + + hash = (unsigned long)p + (unsigned long)base_offset; + hash += (hash >> 8) + (hash >> 16); + return hash & 0xff; +} + +static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset, + unsigned long *base_size, enum object_type *type, int keep_cache) +{ + void *ret; + unsigned long hash = pack_entry_hash(p, base_offset); + struct delta_base_cache_entry *ent = delta_base_cache + hash; + + ret = ent->data; + if (ret && ent->p == p && ent->base_offset == base_offset) + goto found_cache_entry; + return unpack_entry(p, base_offset, type, base_size); + +found_cache_entry: + if (!keep_cache) { + ent->data = NULL; + delta_base_cached -= ent->size; + } + else { + ret = xmalloc(ent->size + 1); + memcpy(ret, ent->data, ent->size); + ((char *)ret)[ent->size] = 0; + } + *type = ent->type; + *base_size = ent->size; + return ret; +} + +static inline void release_delta_base_cache(struct delta_base_cache_entry *ent) +{ + if (ent->data) { + free(ent->data); + ent->data = NULL; + delta_base_cached -= ent->size; + } +} + +static void add_delta_base_cache(struct packed_git *p, off_t base_offset, + void *base, unsigned long base_size, enum object_type type) +{ + unsigned long i, hash = pack_entry_hash(p, base_offset); + struct delta_base_cache_entry *ent = delta_base_cache + hash; + + release_delta_base_cache(ent); + delta_base_cached += base_size; + for (i = 0; delta_base_cached > delta_base_cache_limit + && i < ARRAY_SIZE(delta_base_cache); i++) { + struct delta_base_cache_entry *f = delta_base_cache + i; + if (f->type == OBJ_BLOB) + release_delta_base_cache(f); + } + for (i = 0; delta_base_cached > delta_base_cache_limit + && i < ARRAY_SIZE(delta_base_cache); i++) + release_delta_base_cache(delta_base_cache + i); + + ent->p = p; + ent->base_offset = base_offset; + ent->type = type; + ent->data = base; + ent->size = base_size; +} + static void *unpack_delta_entry(struct packed_git *p, struct pack_window **w_curs, off_t curpos, @@ -1365,7 +1446,7 @@ static void *unpack_delta_entry(struct packed_git *p, off_t base_offset; base_offset = get_delta_base(p, w_curs, &curpos, *type, obj_offset); - base = unpack_entry(p, base_offset, type, &base_size); + base = cache_or_unpack_entry(p, base_offset, &base_size, type, 0); if (!base) die("failed to read delta base object" " at %"PRIuMAX" from %s", @@ -1378,7 +1459,7 @@ static void *unpack_delta_entry(struct packed_git *p, if (!result) die("failed to apply delta"); free(delta_data); - free(base); + add_delta_base_cache(p, base_offset, base, base_size, *type); return result; } @@ -1562,7 +1643,7 @@ static void *read_packed_sha1(const unsigned char *sha1, if (!find_pack_entry(sha1, &e, NULL)) return NULL; else - return unpack_entry(e.p, e.offset, type, size); + return cache_or_unpack_entry(e.p, e.offset, size, type, 1); } /* |
