Commit 660d59caa9cd6260fbc980e7da15f806d6d53083

Russell Belfer 2013-05-17T16:40:00

Add git_oid_nfmt - a flexible OID formatter I frequently want to the the first N digits of an OID formatted as a string and I'd like it to be efficient. This function makes that easy and I could rewrite the OID formatters in terms of it.

diff --git a/include/git2/oid.h b/include/git2/oid.h
index b20bb22..018cead 100644
--- a/include/git2/oid.h
+++ b/include/git2/oid.h
@@ -90,6 +90,17 @@ GIT_EXTERN(void) git_oid_fromraw(git_oid *out, const unsigned char *raw);
 GIT_EXTERN(void) git_oid_fmt(char *out, const git_oid *id);
 
 /**
+ * Format a git_oid into a partial hex string.
+ *
+ * @param out output hex string; you say how many bytes to write.
+ *		If the number of bytes is > GIT_OID_HEXSZ, extra bytes
+ *		will be zeroed; if not, a '\0' terminator is NOT added.
+ * @param n number of characters to write into out string
+ * @param oid oid structure to format.
+ */
+GIT_EXTERN(void) git_oid_nfmt(char *out, size_t n, const git_oid *id);
+
+/**
  * Format a git_oid into a loose-object path string.
  *
  * The resulting string is "aa/...", where "aa" is the first two
@@ -117,10 +128,12 @@ GIT_EXTERN(char *) git_oid_allocfmt(const git_oid *id);
  * Format a git_oid into a buffer as a hex format c-string.
  *
  * If the buffer is smaller than GIT_OID_HEXSZ+1, then the resulting
- * oid c-string will be truncated to n-1 characters. If there are
- * any input parameter errors (out == NULL, n == 0, oid == NULL),
- * then a pointer to an empty string is returned, so that the return
- * value can always be printed.
+ * oid c-string will be truncated to n-1 characters (but will still be
+ * NUL-byte terminated).
+ *
+ * If there are any input parameter errors (out == NULL, n == 0, oid ==
+ * NULL), then a pointer to an empty string is returned, so that the
+ * return value can always be printed.
  *
  * @param out the buffer into which the oid string is output.
  * @param n the size of the out buffer.
diff --git a/src/oid.c b/src/oid.c
index e74640c..a64bd3b 100644
--- a/src/oid.c
+++ b/src/oid.c
@@ -68,12 +68,31 @@ GIT_INLINE(char) *fmt_one(char *str, unsigned int val)
 	return str;
 }
 
-void git_oid_fmt(char *str, const git_oid *oid)
+void git_oid_nfmt(char *str, size_t n, const git_oid *oid)
 {
-	size_t i;
+	size_t i, max_i;
+
+	if (!oid) {
+		memset(str, 0, n);
+		return;
+	}
+	if (n > GIT_OID_HEXSZ) {
+		memset(&str[GIT_OID_HEXSZ], 0, n - GIT_OID_HEXSZ);
+		n = GIT_OID_HEXSZ;
+	}
+
+	max_i = n / 2;
 
-	for (i = 0; i < sizeof(oid->id); i++)
+	for (i = 0; i < max_i; i++)
 		str = fmt_one(str, oid->id[i]);
+
+	if (n & 1)
+		*str++ = to_hex[oid->id[i] >> 4];
+}
+
+void git_oid_fmt(char *str, const git_oid *oid)
+{
+	git_oid_nfmt(str, GIT_OID_HEXSZ, oid);
 }
 
 void git_oid_pathfmt(char *str, const git_oid *oid)
@@ -91,31 +110,20 @@ char *git_oid_allocfmt(const git_oid *oid)
 	char *str = git__malloc(GIT_OID_HEXSZ + 1);
 	if (!str)
 		return NULL;
-	git_oid_fmt(str, oid);
-	str[GIT_OID_HEXSZ] = '\0';
+	git_oid_nfmt(str, GIT_OID_HEXSZ + 1, oid);
 	return str;
 }
 
 char *git_oid_tostr(char *out, size_t n, const git_oid *oid)
 {
-	char str[GIT_OID_HEXSZ];
-
 	if (!out || n == 0)
 		return "";
 
-	n--; /* allow room for terminating NUL */
-
-	if (oid == NULL)
-		n = 0;
-
-	if (n > 0) {
-		git_oid_fmt(str, oid);
-		if (n > GIT_OID_HEXSZ)
-			n = GIT_OID_HEXSZ;
-		memcpy(out, str, n);
-	}
+	if (n > GIT_OID_HEXSZ + 1)
+		n = GIT_OID_HEXSZ + 1;
 
-	out[n] = '\0';
+	git_oid_nfmt(out, n - 1, oid); /* allow room for terminating NUL */
+	out[n - 1] = '\0';
 
 	return out;
 }
diff --git a/tests-clar/object/raw/convert.c b/tests-clar/object/raw/convert.c
index 74442c1..86f0d74 100644
--- a/tests-clar/object/raw/convert.c
+++ b/tests-clar/object/raw/convert.c
@@ -73,3 +73,41 @@ void test_object_raw_convert__succeed_on_oid_to_string_conversion_big(void)
 	cl_assert(str && str == big && *(str+GIT_OID_HEXSZ+2) == 'Y');
 	cl_assert(str && str == big && *(str+GIT_OID_HEXSZ+3) == 'Z');
 }
+
+static void check_partial_oid(
+	char *buffer, size_t count, const git_oid *oid, const char *expected)
+{
+	git_oid_nfmt(buffer, count, oid);
+	buffer[count] = '\0';
+	cl_assert_equal_s(expected, buffer);
+}
+
+void test_object_raw_convert__convert_oid_partially(void)
+{
+	const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+	git_oid in;
+	char big[GIT_OID_HEXSZ + 1 + 3]; /* note + 4 => big buffer */
+	char *str;
+
+	cl_git_pass(git_oid_fromstr(&in, exp));
+
+	git_oid_nfmt(big, sizeof(big), &in);
+	cl_assert_equal_s(exp, big);
+
+	git_oid_nfmt(big, GIT_OID_HEXSZ + 1, &in);
+	cl_assert_equal_s(exp, big);
+
+	check_partial_oid(big, 1, &in, "1");
+	check_partial_oid(big, 2, &in, "16");
+	check_partial_oid(big, 3, &in, "16a");
+	check_partial_oid(big, 4, &in, "16a0");
+	check_partial_oid(big, 5, &in, "16a01");
+
+	check_partial_oid(big, GIT_OID_HEXSZ, &in, exp);
+	check_partial_oid(
+		big, GIT_OID_HEXSZ - 1, &in, "16a0123456789abcdef4b775213c23a8bd74f5e");
+	check_partial_oid(
+		big, GIT_OID_HEXSZ - 2, &in, "16a0123456789abcdef4b775213c23a8bd74f5");
+	check_partial_oid(
+		big, GIT_OID_HEXSZ - 3, &in, "16a0123456789abcdef4b775213c23a8bd74f");
+}