diff --git a/src/git/oid.h b/src/git/oid.h index 9e90ed0d1..6c1a2d8c3 100644 --- a/src/git/oid.h +++ b/src/git/oid.h @@ -13,11 +13,17 @@ */ GIT_BEGIN_DECL +/** Size (in bytes) of a raw/binary oid */ +#define GIT_OID_RAWSZ 20 + +/** Size (in bytes) of a hex formatted oid */ +#define GIT_OID_HEXSZ (GIT_OID_RAWSZ * 2) + /** Unique identity of any object (commit, tree, blob, tag). */ typedef struct { /** raw binary formatted id */ - unsigned char id[20]; + unsigned char id[GIT_OID_RAWSZ]; } git_oid; /** @@ -40,6 +46,40 @@ GIT_INLINE(void) git_oid_mkraw(git_oid *out, const unsigned char *raw) memcpy(out->id, raw, sizeof(out->id)); } +/** + * Format a git_oid into a hex string. + * @param str output hex string; must be pointing at the start of + * the hex sequence and have at least the number of bytes + * needed for an oid encoded in hex (40 bytes). Only the + * oid digits are written; a '\0' terminator must be added + * by the caller if it is required. + * @param oid oid structure to format. + */ +GIT_EXTERN(void) git_oid_fmt(char *str, const git_oid *oid); + +/** + * Format a git_oid into a loose-object path string. + *

+ * The resulting string is "aa/...", where "aa" is the first two + * hex digitis of the oid and "..." is the remaining 38 digits. + * + * @param str output hex string; must be pointing at the start of + * the hex sequence and have at least the number of bytes + * needed for an oid encoded in hex (41 bytes). Only the + * oid digits are written; a '\0' terminator must be added + * by the caller if it is required. + * @param oid oid structure to format. + */ +GIT_EXTERN(void) git_oid_pathfmt(char *str, const git_oid *oid); + +/** + * Format a gid_oid into a newly allocated c-string. + * @param oid theoid structure to format + * @return the c-string; NULL if memory is exhausted. Caller must + * deallocate the string with free(). + */ +GIT_EXTERN(char *) git_oid_allocfmt(const git_oid *oid); + /** * Copy an oid from one structure to another. * @param out oid structure the result is written into. diff --git a/src/oid.c b/src/oid.c index e235f89f5..5f3408511 100644 --- a/src/oid.c +++ b/src/oid.c @@ -44,6 +44,7 @@ static signed char from_hex[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* e0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* f0 */ }; +static char to_hex[] = "0123456789abcdef"; int git_oid_mkstr(git_oid *out, const char *str) { @@ -57,3 +58,38 @@ int git_oid_mkstr(git_oid *out, const char *str) } return GIT_SUCCESS; } + +static inline char *fmt_one(char *str, unsigned int val) +{ + *str++ = to_hex[val >> 4]; + *str++ = to_hex[val & 0xf]; + return str; +} + +void git_oid_fmt(char *str, const git_oid *oid) +{ + int i; + + for (i = 0; i < sizeof(oid->id); i++) + str = fmt_one(str, oid->id[i]); +} + +void git_oid_pathfmt(char *str, const git_oid *oid) +{ + int i; + + str = fmt_one(str, oid->id[0]); + *str++ = '/'; + for (i = 1; i < sizeof(oid->id); i++) + str = fmt_one(str, oid->id[i]); +} + +char *git_oid_allocfmt(const git_oid *oid) +{ + char *str = malloc(GIT_OID_HEXSZ + 1); + if (!str) + return NULL; + git_oid_fmt(str, oid); + str[GIT_OID_HEXSZ] = '\0'; + return str; +} diff --git a/tests/t0000-oid.c b/tests/t0000-oid.c index 624965525..e68231085 100644 --- a/tests/t0000-oid.c +++ b/tests/t0000-oid.c @@ -1,6 +1,14 @@ #include "test_lib.h" #include +BEGIN_TEST(oid_szs) + git_oid out; + must_be_true(20 == GIT_OID_RAWSZ); + must_be_true(40 == GIT_OID_HEXSZ); + must_be_true(sizeof(out) == GIT_OID_RAWSZ); + must_be_true(sizeof(out.id) == GIT_OID_RAWSZ); +END_TEST + BEGIN_TEST(empty_string) git_oid out; must_fail(git_oid_mkstr(&out, "")); @@ -150,3 +158,51 @@ BEGIN_TEST(cmp_oid_gt) git_oid_mkraw(&b, b_in); must_be_true(git_oid_cmp(&a, &b) > 0); END_TEST + +BEGIN_TEST(cmp_oid_fmt) + const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0"; + git_oid in; + char out[GIT_OID_HEXSZ + 1]; + + must_pass(git_oid_mkstr(&in, exp)); + + /* Format doesn't touch the last byte */ + out[GIT_OID_HEXSZ] = 'Z'; + git_oid_fmt(out, &in); + must_be_true(out[GIT_OID_HEXSZ] == 'Z'); + + /* Format produced the right result */ + out[GIT_OID_HEXSZ] = '\0'; + must_pass(strcmp(exp, out)); +END_TEST + +BEGIN_TEST(cmp_oid_allocfmt) + const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0"; + git_oid in; + char *out; + + must_pass(git_oid_mkstr(&in, exp)); + + out = git_oid_allocfmt(&in); + must_be_true(out); + must_pass(strcmp(exp, out)); +END_TEST + +BEGIN_TEST(cmp_oid_pathfmt) + const char *exp1 = "16a0123456789abcdef4b775213c23a8bd74f5e0"; + const char *exp2 = "16/a0123456789abcdef4b775213c23a8bd74f5e0"; + git_oid in; + char out[GIT_OID_HEXSZ + 2]; + + must_pass(git_oid_mkstr(&in, exp1)); + + /* Format doesn't touch the last byte */ + out[GIT_OID_HEXSZ + 1] = 'Z'; + git_oid_pathfmt(out, &in); + must_be_true(out[GIT_OID_HEXSZ + 1] == 'Z'); + + /* Format produced the right result */ + out[GIT_OID_HEXSZ + 1] = '\0'; + must_pass(strcmp(exp2, out)); +END_TEST +