From 6c04269c8f558c109b0cd4524feb9d95bbbb3f6b Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Fri, 4 Mar 2016 00:50:35 -0500 Subject: [PATCH] git_odb_exists_many_prefixes: query odb for multiple short ids Query the object database for multiple objects at a time, given their object ID (which may be abbreviated) and optional type. --- include/git2/odb.h | 32 +++++++++- src/odb.c | 80 +++++++++++++++++++++---- src/oid.h | 9 +++ tests/odb/mixed.c | 141 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 249 insertions(+), 13 deletions(-) diff --git a/include/git2/odb.h b/include/git2/odb.h index 4f1e18bc1..2b542b523 100644 --- a/include/git2/odb.h +++ b/include/git2/odb.h @@ -10,6 +10,7 @@ #include "common.h" #include "types.h" #include "oid.h" +#include "oidarray.h" /** * @file git2/odb.h @@ -159,7 +160,8 @@ GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_otype *type_out, git_od GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id); /** - * Determine if objects can be found in the object database from a short OID. + * Determine if an object can be found in the object database by an + * abbreviated object ID. * * @param out The full OID of the found object if just one is found. * @param db The database to be searched for the given object. @@ -171,6 +173,34 @@ GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id); GIT_EXTERN(int) git_odb_exists_prefix( git_oid *out, git_odb *db, const git_oid *short_id, size_t len); +/** + * Determine if one or more objects can be found in the object database + * by their abbreviated object IDs. Callers may further restrict the + * lookup based on type. This function will write the complete object + * ID to the `id`s array, and the updated length to the `id_lengths` + * array. (If an object is found, it will have its length updated to + * `GIT_OID_HEXSZ`; if an object is not found, will be be `0`.) + * + * Note that since this function operates on multiple objects, the + * underlying database will not be asked to be reloaded if an object is + * not found (which is unlike other object database operations.) + * + * @param db The database to be searched for the given objects. + * @param ids An array of object IDs to search for + * @param id_lengths The corresponding length of each entry in the `ids` + * array + * @param types The corresponding type of each entry in the `ids` array + * (or null to lookup an object of any type) + * @param cnt The length of the `ids`, `id_lengths` and `types` arrays + * @return 0 on success or an error code on failure + */ +GIT_EXTERN(int) git_odb_exists_many_prefixes( + git_odb *db, + git_oid *ids, + size_t *id_lengths, + git_otype *types, + size_t cnt); + /** * Refresh the object database to load newly added files. * diff --git a/src/odb.c b/src/odb.c index cb0f70623..e61958549 100644 --- a/src/odb.c +++ b/src/odb.c @@ -18,6 +18,7 @@ #include "git2/odb_backend.h" #include "git2/oid.h" +#include "git2/oidarray.h" #define GIT_ALTERNATES_FILE "info/alternates" @@ -651,7 +652,7 @@ int git_odb_exists(git_odb *db, const git_oid *id) if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { git_odb_object_free(object); - return (int)true; + return 1; } if (odb_exists_1(db, id, false)) @@ -716,10 +717,8 @@ int git_odb_exists_prefix( if (len < GIT_OID_MINPREFIXLEN) return git_odb__error_ambiguous("prefix length too short"); - if (len > GIT_OID_HEXSZ) - len = GIT_OID_HEXSZ; - if (len == GIT_OID_HEXSZ) { + if (len >= GIT_OID_HEXSZ) { if (git_odb_exists(db, short_id)) { if (out) git_oid_cpy(out, short_id); @@ -730,10 +729,7 @@ int git_odb_exists_prefix( } } - /* just copy valid part of short_id */ - memcpy(&key.id, short_id->id, (len + 1) / 2); - if (len & 1) - key.id[len / 2] &= 0xF0; + git_oid__cpy_prefix(&key, short_id, len); error = odb_exists_prefix_1(out, db, &key, len, false); @@ -746,6 +742,69 @@ int git_odb_exists_prefix( return error; } +int git_odb_exists_many_prefixes( + git_odb *db, + git_oid *ids, + size_t *id_lengths, + git_otype *types, + size_t cnt) +{ + size_t len, i; + int error; + + assert(db && ids && id_lengths); + + for (i = 0; i < cnt; i++) { + git_oid *actual_id = NULL, tmp; + git_otype actual_type = 0; + + /* if we were given a full object ID, simply look it up */ + if (id_lengths[i] >= GIT_OID_HEXSZ) { + error = git_odb_read_header(&len, &actual_type, db, &ids[i]); + } + + /* otherwise, resolve the short id to full, then (optionally) + * read the header. + */ + else if (id_lengths[i] >= GIT_OID_MINPREFIXLEN) { + error = odb_exists_prefix_1(&tmp, + db, &ids[i], id_lengths[i], false); + + if (!error) { + actual_id = &tmp; + + if (types && types[i] != GIT_OBJ_ANY) + error = git_odb_read_header(&len, &actual_type, db, &tmp); + else + actual_type = GIT_OBJ_ANY; + } + } + + if (error < 0 && error != GIT_ENOTFOUND && error != GIT_EAMBIGUOUS) + break; + + error = 0; + + if (types && types[i] != GIT_OBJ_ANY && types[i] != actual_type) + actual_type = 0; + + if (!actual_type) { + id_lengths[i] = 0; + memset(&ids[i], 0, sizeof(git_oid)); + } else { + id_lengths[i] = GIT_OID_HEXSZ; + + if (actual_id) + git_oid_cpy(&ids[i], actual_id); + } + } + + if (!error) + giterr_clear(); + + return error; +} + int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id) { int error; @@ -957,10 +1016,7 @@ int git_odb_read_prefix( return 0; } - /* just copy valid part of short_id */ - memcpy(&key.id, short_id->id, (len + 1) / 2); - if (len & 1) - key.id[len / 2] &= 0xF0; + git_oid__cpy_prefix(&key, short_id, len); error = read_prefix_1(out, db, &key, len, false); diff --git a/src/oid.h b/src/oid.h index aa1f0bfdc..922a2a347 100644 --- a/src/oid.h +++ b/src/oid.h @@ -44,4 +44,13 @@ GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b) return git_oid__hashcmp(a->id, b->id); } +GIT_INLINE(void) git_oid__cpy_prefix( + git_oid *out, const git_oid *id, size_t len) +{ + memcpy(&out->id, id->id, (len + 1) / 2); + + if (len & 1) + out->id[len / 2] &= 0xF0; +} + #endif diff --git a/tests/odb/mixed.c b/tests/odb/mixed.c index 2dad4b64e..fe22f85bc 100644 --- a/tests/odb/mixed.c +++ b/tests/odb/mixed.c @@ -108,3 +108,144 @@ void test_odb_mixed__dup_oid_prefix_0(void) { cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); git_odb_object_free(obj); } + +struct odb_test_data { + char *lookup_id; + char *expected_id; + git_otype expected_type; +}; + +struct odb_test_data prefix_data[] = { + /* some prefixes and their expected values */ + { "dea509d0", NULL, GIT_OBJ_ANY }, + { "00000000", NULL, GIT_OBJ_ANY }, + { "dea509d0", NULL, GIT_OBJ_ANY }, + { "dea509d09", "dea509d097ce692e167dfc6a48a7a280cc5e877e", GIT_OBJ_BLOB }, + { "dea509d0b", "dea509d0b3cb8ee0650f6ca210bc83f4678851ba", GIT_OBJ_BLOB }, + { "ce0136250", "ce013625030ba8dba906f756967f9e9ca394464a", GIT_OBJ_BLOB }, + { "0ddeaded", NULL, GIT_OBJ_ANY }, + { "4d5979b", "4d5979b468252190cb572ae758aca36928e8a91e", GIT_OBJ_TREE }, + { "0ddeaded", NULL, GIT_OBJ_ANY }, + { "0ddeadede", "0ddeadede9e6d6ccddce0ee1e5749eed0485e5ea", GIT_OBJ_BLOB }, + { "0ddeaded9", "0ddeaded9502971eefe1e41e34d0e536853ae20f", GIT_OBJ_BLOB }, + { "f00b4e", NULL, GIT_OBJ_ANY }, + + /* some full-length object ids */ + { "0000000000000000000000000000000000000000", NULL, GIT_OBJ_ANY }, + { + "dea509d097ce692e167dfc6a48a7a280cc5e877e", + "dea509d097ce692e167dfc6a48a7a280cc5e877e", + GIT_OBJ_BLOB + }, + { "f00f00f00f00f00f00f00f00f00f00f00f00f00f", NULL, GIT_OBJ_ANY }, + { + "4d5979b468252190cb572ae758aca36928e8a91e", + "4d5979b468252190cb572ae758aca36928e8a91e", + GIT_OBJ_TREE + }, +}; + +static void setup_prefix_query( + git_oid **out_ids, + size_t **out_lengths, + git_otype **out_types, + size_t *out_num) +{ + git_oid *ids; + git_otype *types; + size_t num, *lengths, i; + + num = ARRAY_SIZE(prefix_data); + + cl_assert((ids = git__calloc(num, sizeof(git_oid)))); + cl_assert((lengths = git__calloc(num, sizeof(size_t)))); + cl_assert((types = git__calloc(num, sizeof(git_otype)))); + + for (i = 0; i < num; i++) { + lengths[i] = strlen(prefix_data[i].lookup_id); + git_oid_fromstrn(&ids[i], prefix_data[i].lookup_id, lengths[i]); + types[i] = prefix_data[i].expected_type; + } + + *out_ids = ids; + *out_lengths = lengths; + *out_types = types; + *out_num = num; +} + +static void assert_found_objects(git_oid *ids, size_t *lengths) +{ + size_t num, i; + + num = ARRAY_SIZE(prefix_data); + + for (i = 0; i < num; i++) { + git_oid expected_id = {{0}}; + size_t expected_len = 0; + + if (prefix_data[i].expected_id) { + git_oid_fromstr(&expected_id, prefix_data[i].expected_id); + expected_len = GIT_OID_HEXSZ; + } + + cl_assert_equal_i(expected_len, lengths[i]); + cl_assert_equal_oid(&expected_id, &ids[i]); + } +} + +static void assert_notfound_objects(git_oid *ids, size_t *lengths) +{ + git_oid expected_id = {{0}}; + size_t num, i; + + num = ARRAY_SIZE(prefix_data); + + for (i = 0; i < num; i++) { + cl_assert_equal_i(0, lengths[i]); + cl_assert_equal_oid(&expected_id, &ids[i]); + } +} + +void test_odb_mixed__prefix_many(void) +{ + git_oid *ids; + size_t i, num, *lengths; + git_otype *types; + + /* test looking for the actual (correct) types */ + + setup_prefix_query(&ids, &lengths, &types, &num); + cl_git_pass(git_odb_exists_many_prefixes(_odb, ids, lengths, types, num)); + assert_found_objects(ids, lengths); + git__free(ids); git__free(lengths); git__free(types); + + /* test looking for no specified types (types array == NULL) */ + + setup_prefix_query(&ids, &lengths, &types, &num); + cl_git_pass(git_odb_exists_many_prefixes(_odb, ids, lengths, NULL, num)); + assert_found_objects(ids, lengths); + git__free(ids); git__free(lengths); git__free(types); + + /* test looking for an explicit GIT_OBJ_ANY */ + + setup_prefix_query(&ids, &lengths, &types, &num); + + for (i = 0; i < num; i++) + types[i] = GIT_OBJ_ANY; + + cl_git_pass(git_odb_exists_many_prefixes(_odb, ids, lengths, types, num)); + assert_found_objects(ids, lengths); + git__free(ids); git__free(lengths); git__free(types); + + /* test looking for the completely wrong type */ + + setup_prefix_query(&ids, &lengths, &types, &num); + + for (i = 0; i < num; i++) + types[i] = (types[i] == GIT_OBJ_BLOB) ? GIT_OBJ_TREE : GIT_OBJ_BLOB; + + cl_git_pass(git_odb_exists_many_prefixes(_odb, ids, lengths, types, num)); + assert_notfound_objects(ids, lengths); + git__free(ids); git__free(lengths); git__free(types); +} +