X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=sha1_file.c;h=940bab561254842fd50c59cf9284a8c878cb3fd8;hb=a8c40471ab0851bf9a58f7dc76f121258e0690e2;hp=a7e3a2a9f963ce1bee1f54f05eaa8852b2bd2347;hpb=e5e01619bcb753a3c45fb51d498371c9ff0677da;p=git.git diff --git a/sha1_file.c b/sha1_file.c index a7e3a2a9f..940bab561 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1030,14 +1030,27 @@ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size n = size; memcpy(buf, (char *) buffer + bytes, n); bytes = n; - if (bytes < size) { + if (bytes <= size) { + /* + * The above condition must be (bytes <= size), not + * (bytes < size). In other words, even though we + * expect no more output and set avail_out to zer0, + * the input zlib stream may have bytes that express + * "this concludes the stream", and we *do* want to + * eat that input. + * + * Otherwise we would not be able to test that we + * consumed all the input to reach the expected size; + * we also want to check that zlib tells us that all + * went well with status == Z_STREAM_END at the end. + */ stream->next_out = buf + bytes; stream->avail_out = size - bytes; while (status == Z_OK) status = inflate(stream, Z_FINISH); } buf[size] = 0; - if ((status == Z_OK || status == Z_STREAM_END) && !stream->avail_in) { + if (status == Z_STREAM_END && !stream->avail_in) { inflateEnd(stream); return buf; } @@ -1354,11 +1367,19 @@ static void *unpack_compressed_entry(struct packed_git *p, #define MAX_DELTA_CACHE (256) +static size_t delta_base_cached; + +static struct delta_base_cache_lru_list { + struct delta_base_cache_lru_list *prev; + struct delta_base_cache_lru_list *next; +} delta_base_cache_lru = { &delta_base_cache_lru, &delta_base_cache_lru }; + static struct delta_base_cache_entry { + struct delta_base_cache_lru_list lru; + void *data; struct packed_git *p; off_t base_offset; unsigned long size; - void *data; enum object_type type; } delta_base_cache[MAX_DELTA_CACHE]; @@ -1368,11 +1389,11 @@ static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset) hash = (unsigned long)p + (unsigned long)base_offset; hash += (hash >> 8) + (hash >> 16); - return hash & 0xff; + return hash % MAX_DELTA_CACHE; } static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset, - unsigned long *base_size, enum object_type *type) + unsigned long *base_size, enum object_type *type, int keep_cache) { void *ret; unsigned long hash = pack_entry_hash(p, base_offset); @@ -1384,25 +1405,68 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset, return unpack_entry(p, base_offset, type, base_size); found_cache_entry: - ent->data = NULL; + if (!keep_cache) { + ent->data = NULL; + ent->lru.next->prev = ent->lru.prev; + ent->lru.prev->next = ent->lru.next; + 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; + ent->lru.next->prev = ent->lru.prev; + ent->lru.prev->next = ent->lru.next; + 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 hash = pack_entry_hash(p, base_offset); struct delta_base_cache_entry *ent = delta_base_cache + hash; + struct delta_base_cache_lru_list *lru; + + release_delta_base_cache(ent); + delta_base_cached += base_size; + + for (lru = delta_base_cache_lru.next; + delta_base_cached > delta_base_cache_limit + && lru != &delta_base_cache_lru; + lru = lru->next) { + struct delta_base_cache_entry *f = (void *)lru; + if (f->type == OBJ_BLOB) + release_delta_base_cache(f); + } + for (lru = delta_base_cache_lru.next; + delta_base_cached > delta_base_cache_limit + && lru != &delta_base_cache_lru; + lru = lru->next) { + struct delta_base_cache_entry *f = (void *)lru; + release_delta_base_cache(f); + } - if (ent->data) - free(ent->data); ent->p = p; ent->base_offset = base_offset; ent->type = type; ent->data = base; ent->size = base_size; + ent->lru.next = &delta_base_cache_lru; + ent->lru.prev = delta_base_cache_lru.prev; + delta_base_cache_lru.prev->next = &ent->lru; + delta_base_cache_lru.prev = &ent->lru; } static void *unpack_delta_entry(struct packed_git *p, @@ -1418,7 +1482,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 = cache_or_unpack_entry(p, base_offset, &base_size, type); + 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", @@ -1615,7 +1679,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); } /* @@ -1744,7 +1808,7 @@ void *read_object_with_reference(const unsigned char *sha1, } } -static void write_sha1_file_prepare(void *buf, unsigned long len, +static void write_sha1_file_prepare(const void *buf, unsigned long len, const char *type, unsigned char *sha1, char *hdr, int *hdrlen) { @@ -1872,7 +1936,7 @@ static void setup_object_header(z_stream *stream, const char *type, unsigned lon stream->avail_out -= hdrlen; } -int hash_sha1_file(void *buf, unsigned long len, const char *type, +int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1) { char hdr[32]; @@ -1883,7 +1947,7 @@ int hash_sha1_file(void *buf, unsigned long len, const char *type, int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1) { - int size; + int size, ret; unsigned char *compressed; z_stream stream; unsigned char sha1[20]; @@ -1943,9 +2007,14 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha /* Then the data itself.. */ stream.next_in = buf; stream.avail_in = len; - while (deflate(&stream, Z_FINISH) == Z_OK) - /* nothing */; - deflateEnd(&stream); + ret = deflate(&stream, Z_FINISH); + if (ret != Z_STREAM_END) + die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret); + + ret = deflateEnd(&stream); + if (ret != Z_OK) + die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret); + size = stream.total_out; if (write_buffer(fd, compressed, size) < 0)