Commit af795e498d411142ddb073e8ca2c5447c3295a4c

Shawn O. Pearce 2008-12-02T09:56:23

Add routines to convert git_oid to hex strings [sp: Credit for some of this implementation goes to Pieter, I started off a patch he proposed for libgit2 but reworked enough of it that I don't want to blame him for any bugs.] Suggested-by: Pieter de Bie <pdebie@ai.rug.nl> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>

diff --git a/src/git/oid.h b/src/git/oid.h
index 9e90ed0..6c1a2d8 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;
 
 /**
@@ -41,6 +47,40 @@ GIT_INLINE(void) git_oid_mkraw(git_oid *out, const unsigned char *raw)
 }
 
 /**
+ * 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.
+ * <p>
+ * 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.
  * @param src oid structure to copy from.
diff --git a/src/oid.c b/src/oid.c
index e235f89..5f34085 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 6249655..e682310 100644
--- a/tests/t0000-oid.c
+++ b/tests/t0000-oid.c
@@ -1,6 +1,14 @@
 #include "test_lib.h"
 #include <git/oid.h>
 
+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
+