From d1b6ea8ad14f41b87026ab407005e887c4ff6b68 Mon Sep 17 00:00:00 2001 From: David Michael Barr Date: Sat, 1 Dec 2012 14:50:20 +1100 Subject: [PATCH 1/3] delta-apply: add git__delta_read_header --- src/delta-apply.c | 13 +++++++++++++ src/delta-apply.h | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/delta-apply.c b/src/delta-apply.c index 815ca8f16..85e2ef88f 100644 --- a/src/delta-apply.c +++ b/src/delta-apply.c @@ -36,6 +36,19 @@ static int hdr_sz( return 0; } +int git__delta_read_header( + const unsigned char *delta, + size_t delta_len, + size_t *base_sz, + size_t *res_sz) +{ + const unsigned char *delta_end = delta + delta_len; + if ((hdr_sz(base_sz, &delta, delta_end) < 0) || + (hdr_sz(res_sz, &delta, delta_end) < 0)) + return -1; + return 0; +} + int git__delta_apply( git_rawobj *out, const unsigned char *base, diff --git a/src/delta-apply.h b/src/delta-apply.h index 66fa76d43..9aea4ac9f 100644 --- a/src/delta-apply.h +++ b/src/delta-apply.h @@ -30,4 +30,21 @@ extern int git__delta_apply( const unsigned char *delta, size_t delta_len); +/** + * Read the header of a git binary delta. + * + * @param delta the delta to execute copy/insert instructions from. + * @param delta_len total number of bytes in the delta. + * @param base_sz pointer to store the base size field. + * @param res_sz pointer to store the result size field. + * @return + * - 0 on a successful decoding the header. + * - GIT_ERROR if the delta is corrupt. + */ +extern int git__delta_read_header( + const unsigned char *delta, + size_t delta_len, + size_t *base_sz, + size_t *res_sz); + #endif From 44f9f547972efecd326fbf6e92ae1297cc9e1813 Mon Sep 17 00:00:00 2001 From: David Michael Barr Date: Fri, 30 Nov 2012 13:33:30 +1100 Subject: [PATCH 2/3] pack: add git_packfile_resolve_header To paraphrase @peff: You can get both size and type from a packed object reasonably cheaply. If you have: * An object that is not a delta; both type and size are available in the packfile header. * An object that is a delta. The packfile type will be OBJ_*_DELTA, and you have to resolve back to the base to find the real type. That means potentially a lot of packfile index lookups, but each one is relatively cheap. For the size, you inflate the first few bytes of the delta, whose header will tell you the resulting size of applying the delta to the base. For simplicity, we just decompress the whole delta for now. --- src/pack.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/pack.h | 6 ++++++ 2 files changed, 56 insertions(+) diff --git a/src/pack.c b/src/pack.c index 6cb46d37b..d7d39392f 100644 --- a/src/pack.c +++ b/src/pack.c @@ -277,6 +277,56 @@ int git_packfile_unpack_header( return 0; } +int git_packfile_resolve_header( + size_t *size_p, + git_otype *type_p, + struct git_pack_file *p, + git_off_t offset) +{ + git_mwindow *w_curs = NULL; + git_off_t curpos = offset; + size_t size; + git_otype type; + git_off_t base_offset; + int error; + + error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos); + git_mwindow_close(&w_curs); + if (error < 0) + return error; + + if (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) { + size_t base_size; + git_rawobj delta; + base_offset = get_delta_base(p, &w_curs, &curpos, type, offset); + git_mwindow_close(&w_curs); + error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, size, type); + git_mwindow_close(&w_curs); + if (error < 0) + return error; + error = git__delta_read_header(delta.data, delta.len, &base_size, size_p); + git__free(delta.data); + if (error < 0) + return error; + } else + *size_p = size; + + while (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) { + curpos = base_offset; + error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos); + git_mwindow_close(&w_curs); + if (error < 0) + return error; + if (type != GIT_OBJ_OFS_DELTA && type != GIT_OBJ_REF_DELTA) + break; + base_offset = get_delta_base(p, &w_curs, &curpos, type, base_offset); + git_mwindow_close(&w_curs); + } + *type_p = type; + + return error; +} + static int packfile_unpack_delta( git_rawobj *obj, struct git_pack_file *p, diff --git a/src/pack.h b/src/pack.h index 9fb26b6a9..c1277fdfb 100644 --- a/src/pack.h +++ b/src/pack.h @@ -83,6 +83,12 @@ int git_packfile_unpack_header( git_mwindow **w_curs, git_off_t *curpos); +int git_packfile_resolve_header( + size_t *size_p, + git_otype *type_p, + struct git_pack_file *p, + git_off_t offset); + int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, git_off_t *obj_offset); int packfile_unpack_compressed( git_rawobj *obj, From bfb8bcc1df18431e8e7cf2a9dce8acc95cd312e3 Mon Sep 17 00:00:00 2001 From: David Michael Barr Date: Mon, 3 Dec 2012 10:36:32 +1100 Subject: [PATCH 3/3] odb-pack: resurrect pack_backend__read_header --- src/odb_pack.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/odb_pack.c b/src/odb_pack.c index 35bf1580d..fc282dd14 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -384,19 +384,18 @@ cleanup: * ***********************************************************/ -/* -int pack_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid) +static int pack_backend__read_header(size_t *len_p, git_otype *type_p, struct git_odb_backend *backend, const git_oid *oid) { - pack_location location; + struct git_pack_entry e; + int error; - assert(obj && backend && oid); + assert(len_p && type_p && backend && oid); - if (locate_packfile(&location, (struct pack_backend *)backend, oid) < 0) - return GIT_ENOTFOUND; + if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0) + return error; - return read_header_packed(obj, &location); + return git_packfile_resolve_header(len_p, type_p, e.p, e.offset); } -*/ static int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { @@ -579,7 +578,7 @@ int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx) backend->parent.read = &pack_backend__read; backend->parent.read_prefix = &pack_backend__read_prefix; - backend->parent.read_header = NULL; + backend->parent.read_header = &pack_backend__read_header; backend->parent.exists = &pack_backend__exists; backend->parent.foreach = &pack_backend__foreach; backend->parent.free = &pack_backend__free; @@ -616,7 +615,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) backend->parent.read = &pack_backend__read; backend->parent.read_prefix = &pack_backend__read_prefix; - backend->parent.read_header = NULL; + backend->parent.read_header = &pack_backend__read_header; backend->parent.exists = &pack_backend__exists; backend->parent.foreach = &pack_backend__foreach; backend->parent.writepack = &pack_backend__writepack;