diff --git a/src/delta.c b/src/delta.c index 8a4c2a10458..dc45697b6e3 100644 --- a/src/delta.c +++ b/src/delta.c @@ -114,7 +114,7 @@ struct index_entry { struct git_delta_index { unsigned long memsize; const void *src_buf; - unsigned long src_size; + size_t src_size; unsigned int hash_mask; struct index_entry *hash[GIT_FLEX_ARRAY]; }; @@ -142,8 +142,8 @@ static int lookup_index_alloc( return 0; } -struct git_delta_index * -git_delta_create_index(const void *buf, unsigned long bufsize) +int git_delta_index_init( + git_delta_index **out, const void *buf, size_t bufsize) { unsigned int i, hsize, hmask, entries, prev_val, *hash_count; const unsigned char *data, *buffer = buf; @@ -152,8 +152,10 @@ git_delta_create_index(const void *buf, unsigned long bufsize) void *mem; unsigned long memsize; + *out = NULL; + if (!buf || !bufsize) - return NULL; + return 0; /* Determine index hash size. Note that indexing skips the first byte to allow for optimizing the rabin polynomial @@ -172,7 +174,7 @@ git_delta_create_index(const void *buf, unsigned long bufsize) hmask = hsize - 1; if (lookup_index_alloc(&mem, &memsize, entries, hsize) < 0) - return NULL; + return -1; index = mem; mem = index->hash; @@ -190,7 +192,7 @@ git_delta_create_index(const void *buf, unsigned long bufsize) hash_count = git__calloc(hsize, sizeof(*hash_count)); if (!hash_count) { git__free(index); - return NULL; + return -1; } /* then populate the index */ @@ -243,20 +245,20 @@ git_delta_create_index(const void *buf, unsigned long bufsize) } git__free(hash_count); - return index; + *out = index; + return 0; } -void git_delta_free_index(struct git_delta_index *index) +void git_delta_index_free(git_delta_index *index) { git__free(index); } -unsigned long git_delta_sizeof_index(struct git_delta_index *index) +size_t git_delta_index_size(git_delta_index *index) { - if (index) - return index->memsize; - else - return 0; + assert(index); + + return index->memsize; } /* @@ -265,55 +267,57 @@ unsigned long git_delta_sizeof_index(struct git_delta_index *index) */ #define MAX_OP_SIZE (5 + 5 + 1 + RABIN_WINDOW + 7) -void * -git_delta_create( +int git_delta_create_from_index( + void **out, + size_t *out_len, const struct git_delta_index *index, const void *trg_buf, - unsigned long trg_size, - unsigned long *delta_size, - unsigned long max_size) + size_t trg_size, + size_t max_size) { - unsigned int i, outpos, outsize, moff, msize, val; + unsigned int i, bufpos, bufsize, moff, msize, val; int inscnt; const unsigned char *ref_data, *ref_top, *data, *top; - unsigned char *out; + unsigned char *buf; + + *out = NULL; + *out_len = 0; if (!trg_buf || !trg_size) - return NULL; + return 0; - outpos = 0; - outsize = 8192; - if (max_size && outsize >= max_size) - outsize = (unsigned int)(max_size + MAX_OP_SIZE + 1); - out = git__malloc(outsize); - if (!out) - return NULL; + bufpos = 0; + bufsize = 8192; + if (max_size && bufsize >= max_size) + bufsize = (unsigned int)(max_size + MAX_OP_SIZE + 1); + buf = git__malloc(bufsize); + GITERR_CHECK_ALLOC(buf); /* store reference buffer size */ i = index->src_size; while (i >= 0x80) { - out[outpos++] = i | 0x80; + buf[bufpos++] = i | 0x80; i >>= 7; } - out[outpos++] = i; + buf[bufpos++] = i; /* store target buffer size */ i = trg_size; while (i >= 0x80) { - out[outpos++] = i | 0x80; + buf[bufpos++] = i | 0x80; i >>= 7; } - out[outpos++] = i; + buf[bufpos++] = i; ref_data = index->src_buf; ref_top = ref_data + index->src_size; data = trg_buf; top = (const unsigned char *) trg_buf + trg_size; - outpos++; + bufpos++; val = 0; for (i = 0; i < RABIN_WINDOW && data < top; i++, data++) { - out[outpos++] = *data; + buf[bufpos++] = *data; val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT]; } inscnt = i; @@ -350,11 +354,11 @@ git_delta_create( if (msize < 4) { if (!inscnt) - outpos++; - out[outpos++] = *data++; + bufpos++; + buf[bufpos++] = *data++; inscnt++; if (inscnt == 0x7f) { - out[outpos - inscnt - 1] = inscnt; + buf[bufpos - inscnt - 1] = inscnt; inscnt = 0; } msize = 0; @@ -368,14 +372,14 @@ git_delta_create( msize++; moff--; data--; - outpos--; + bufpos--; if (--inscnt) continue; - outpos--; /* remove count slot */ + bufpos--; /* remove count slot */ inscnt--; /* make it -1 */ break; } - out[outpos - inscnt - 1] = inscnt; + buf[bufpos - inscnt - 1] = inscnt; inscnt = 0; } @@ -383,22 +387,22 @@ git_delta_create( left = (msize < 0x10000) ? 0 : (msize - 0x10000); msize -= left; - op = out + outpos++; + op = buf + bufpos++; i = 0x80; if (moff & 0x000000ff) - out[outpos++] = moff >> 0, i |= 0x01; + buf[bufpos++] = moff >> 0, i |= 0x01; if (moff & 0x0000ff00) - out[outpos++] = moff >> 8, i |= 0x02; + buf[bufpos++] = moff >> 8, i |= 0x02; if (moff & 0x00ff0000) - out[outpos++] = moff >> 16, i |= 0x04; + buf[bufpos++] = moff >> 16, i |= 0x04; if (moff & 0xff000000) - out[outpos++] = moff >> 24, i |= 0x08; + buf[bufpos++] = moff >> 24, i |= 0x08; if (msize & 0x00ff) - out[outpos++] = msize >> 0, i |= 0x10; + buf[bufpos++] = msize >> 0, i |= 0x10; if (msize & 0xff00) - out[outpos++] = msize >> 8, i |= 0x20; + buf[bufpos++] = msize >> 8, i |= 0x20; *op = i; @@ -415,31 +419,33 @@ git_delta_create( } } - if (outpos >= outsize - MAX_OP_SIZE) { - void *tmp = out; - outsize = outsize * 3 / 2; - if (max_size && outsize >= max_size) - outsize = max_size + MAX_OP_SIZE + 1; - if (max_size && outpos > max_size) + if (bufpos >= bufsize - MAX_OP_SIZE) { + void *tmp = buf; + bufsize = bufsize * 3 / 2; + if (max_size && bufsize >= max_size) + bufsize = max_size + MAX_OP_SIZE + 1; + if (max_size && bufpos > max_size) break; - out = git__realloc(out, outsize); - if (!out) { + buf = git__realloc(buf, bufsize); + if (!buf) { git__free(tmp); - return NULL; + return -1; } } } if (inscnt) - out[outpos - inscnt - 1] = inscnt; + buf[bufpos - inscnt - 1] = inscnt; - if (max_size && outpos > max_size) { - git__free(out); - return NULL; + if (max_size && bufpos > max_size) { + giterr_set(GITERR_NOMEMORY, "delta would be larger than maximum size"); + git__free(buf); + return GIT_EBUFS; } - *delta_size = outpos; - return out; + *out_len = bufpos; + *out = buf; + return 0; } /* @@ -459,8 +465,11 @@ static int hdr_sz( unsigned int c, shift = 0; do { - if (d == end) + if (d == end) { + giterr_set(GITERR_INVALID, "truncated delta"); return -1; + } + c = *d++; r |= (c & 0x7f) << shift; shift += 7; diff --git a/src/delta.h b/src/delta.h index d9d1d0fa8a2..cc937292236 100644 --- a/src/delta.h +++ b/src/delta.h @@ -8,11 +8,10 @@ #include "common.h" #include "pack.h" -/* opaque object for delta index */ -struct git_delta_index; +typedef struct git_delta_index git_delta_index; /* - * create_delta_index: compute index data from given buffer + * git_delta_index_init: compute index data from given buffer * * This returns a pointer to a struct delta_index that should be passed to * subsequent create_delta() calls, or to free_delta_index(). A NULL pointer @@ -20,22 +19,18 @@ struct git_delta_index; * before free_delta_index() is called. The returned pointer must be freed * using free_delta_index(). */ -extern struct git_delta_index *git_delta_create_index( - const void *buf, unsigned long bufsize); +extern int git_delta_index_init( + git_delta_index **out, const void *buf, size_t bufsize); /* - * free_delta_index: free the index created by create_delta_index() - * - * Given pointer must be what create_delta_index() returned, or NULL. + * Free the index created by git_delta_index_init() */ -extern void git_delta_free_index(struct git_delta_index *index); +extern void git_delta_index_free(git_delta_index *index); /* - * sizeof_delta_index: returns memory usage of delta index - * - * Given pointer must be what create_delta_index() returned, or NULL. + * Returns memory usage of delta index. */ -extern unsigned long git_delta_sizeof_index(struct git_delta_index *index); +extern size_t git_delta_index_size(git_delta_index *index); /* * create_delta: create a delta from given index for the given buffer @@ -47,71 +42,50 @@ extern unsigned long git_delta_sizeof_index(struct git_delta_index *index); * returned and *delta_size is updated with its size. The returned buffer * must be freed by the caller. */ -extern void *git_delta_create( +extern int git_delta_create_from_index( + void **out, + size_t *out_size, const struct git_delta_index *index, const void *buf, - unsigned long bufsize, - unsigned long *delta_size, - unsigned long max_delta_size); + size_t bufsize, + size_t max_delta_size); /* * diff_delta: create a delta from source buffer to target buffer * * If max_delta_size is non-zero and the resulting delta is to be larger - * than max_delta_size then NULL is returned. On success, a non-NULL + * than max_delta_size then GIT_EBUFS is returned. On success, a non-NULL * pointer to the buffer with the delta data is returned and *delta_size is * updated with its size. The returned buffer must be freed by the caller. */ -GIT_INLINE(void *) git_delta( - const void *src_buf, unsigned long src_bufsize, - const void *trg_buf, unsigned long trg_bufsize, - unsigned long *delta_size, - unsigned long max_delta_size) +GIT_INLINE(int) git_delta( + void **out, size_t *out_len, + const void *src_buf, size_t src_bufsize, + const void *trg_buf, size_t trg_bufsize, + size_t max_delta_size) { - struct git_delta_index *index = git_delta_create_index(src_buf, src_bufsize); + git_delta_index *index; + int error = 0; + + *out = NULL; + *out_len = 0; + + if ((error = git_delta_index_init(&index, src_buf, src_bufsize)) < 0) + return error; + if (index) { - void *delta = git_delta_create( - index, trg_buf, trg_bufsize, delta_size, max_delta_size); - git_delta_free_index(index); - return delta; + error = git_delta_create_from_index(out, out_len, + index, trg_buf, trg_bufsize, max_delta_size); + + git_delta_index_free(index); } - return NULL; -} -/* - * patch_delta: recreate target buffer given source buffer and delta data - * - * On success, a non-NULL pointer to the target buffer is returned and - * *trg_bufsize is updated with its size. On failure a NULL pointer is - * returned. The returned buffer must be freed by the caller. - */ -extern void *git_delta_patch( - const void *src_buf, unsigned long src_size, - const void *delta_buf, unsigned long delta_size, - unsigned long *dst_size); + return error; +} /* the smallest possible delta size is 4 bytes */ #define GIT_DELTA_SIZE_MIN 4 -/* - * This must be called twice on the delta data buffer, first to get the - * expected source buffer size, and again to get the target buffer size. - */ -GIT_INLINE(unsigned long) git_delta_get_hdr_size( - const unsigned char **datap, const unsigned char *top) -{ - const unsigned char *data = *datap; - unsigned long cmd, size = 0; - int i = 0; - do { - cmd = *data++; - size |= (cmd & 0x7f) << i; - i += 7; - } while (cmd & 0x80 && data < top); - *datap = data; - return size; -} - /** * Apply a git binary delta to recover the original content. * The caller is responsible for freeing the returned buffer. diff --git a/src/diff_patch.c b/src/diff_patch.c index 50faa3b3f7a..20a84388f29 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -270,20 +270,24 @@ static int create_binary( } if (a_datalen && b_datalen) { - void *delta_data = git_delta( - a_data, (unsigned long)a_datalen, - b_data, (unsigned long)b_datalen, - &delta_data_len, (unsigned long)deflate.size); + void *delta_data; - if (delta_data) { + error = git_delta(&delta_data, &delta_data_len, + a_data, a_datalen, + b_data, b_datalen, + deflate.size); + + if (error == 0) { error = git_zstream_deflatebuf( &delta, delta_data, (size_t)delta_data_len); git__free(delta_data); - - if (error < 0) - goto done; + } else if (error == GIT_EBUFS) { + error = 0; } + + if (error < 0) + goto done; } if (delta.size && delta.size < deflate.size) { diff --git a/src/pack-objects.c b/src/pack-objects.c index 11e13f7d45d..6f86deb07b1 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -274,6 +274,7 @@ static int get_delta(void **out, git_odb *odb, git_pobject *po) git_odb_object *src = NULL, *trg = NULL; unsigned long delta_size; void *delta_buf; + int error; *out = NULL; @@ -281,12 +282,15 @@ static int get_delta(void **out, git_odb *odb, git_pobject *po) git_odb_read(&trg, odb, &po->id) < 0) goto on_error; - delta_buf = git_delta( - git_odb_object_data(src), (unsigned long)git_odb_object_size(src), - git_odb_object_data(trg), (unsigned long)git_odb_object_size(trg), - &delta_size, 0); + error = git_delta(&delta_buf, &delta_size, + git_odb_object_data(src), git_odb_object_size(src), + git_odb_object_data(trg), git_odb_object_size(trg), + 0); + + if (error < 0 && error != GIT_EBUFS) + goto on_error; - if (!delta_buf || delta_size != po->delta_size) { + if (error == GIT_EBUFS || delta_size != po->delta_size) { giterr_set(GITERR_INVALID, "Delta size changed"); goto on_error; } @@ -815,16 +819,14 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg, *mem_usage += sz; } if (!src->index) { - src->index = git_delta_create_index(src->data, src_size); - if (!src->index) + if (git_delta_index_init(&src->index, src->data, src_size) < 0) return 0; /* suboptimal pack - out of memory */ - *mem_usage += git_delta_sizeof_index(src->index); + *mem_usage += git_delta_index_size(src->index); } - delta_buf = git_delta_create(src->index, trg->data, trg_size, - &delta_size, max_size); - if (!delta_buf) + if (git_delta_create_from_index(&delta_buf, &delta_size, src->index, trg->data, trg_size, + max_size) < 0) return 0; if (trg_object->delta) { @@ -885,9 +887,14 @@ static unsigned int check_delta_limit(git_pobject *me, unsigned int n) static unsigned long free_unpacked(struct unpacked *n) { - unsigned long freed_mem = git_delta_sizeof_index(n->index); - git_delta_free_index(n->index); + unsigned long freed_mem = 0; + + if (n->index) { + freed_mem += git_delta_index_size(n->index); + git_delta_index_free(n->index); + } n->index = NULL; + if (n->data) { freed_mem += (unsigned long)n->object->size; git__free(n->data);