Commit aeb5ee5ab50a062aac02ca084b02582430669808

David Turner 2016-05-17T15:40:46

varint: Add varint encoding/decoding This code is ported from git.git Signed-off-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: David Turner <dturner@twopensource.com>

diff --git a/src/varint.c b/src/varint.c
new file mode 100644
index 0000000..2f86860
--- /dev/null
+++ b/src/varint.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "varint.h"
+
+uintmax_t git_decode_varint(const unsigned char *bufp, size_t *varint_len)
+{
+	const unsigned char *buf = bufp;
+	unsigned char c = *buf++;
+	uintmax_t val = c & 127;
+	while (c & 128) {
+		val += 1;
+		if (!val || MSB(val, 7)) {
+			/* This is not a valid varint_len, so it signals
+			   the error */
+			*varint_len = 0;
+			return 0; /* overflow */
+		}
+		c = *buf++;
+		val = (val << 7) + (c & 127);
+	}
+	*varint_len = buf - bufp;
+	return val;
+}
+
+int git_encode_varint(unsigned char *buf, size_t bufsize, uintmax_t value)
+{
+	unsigned char varint[16];
+	unsigned pos = sizeof(varint) - 1;
+	varint[pos] = value & 127;
+	while (value >>= 7)
+		varint[--pos] = 128 | (--value & 127);
+	if (buf) {
+		if (bufsize < pos)
+			return -1;
+		memcpy(buf, varint + pos, sizeof(varint) - pos);
+	}
+	return sizeof(varint) - pos;
+}
diff --git a/src/varint.h b/src/varint.h
new file mode 100644
index 0000000..650ec7d
--- /dev/null
+++ b/src/varint.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_varint_h__
+#define INCLUDE_varint_h__
+
+#include <stdint.h>
+
+extern int git_encode_varint(unsigned char *, size_t, uintmax_t);
+extern uintmax_t git_decode_varint(const unsigned char *, size_t *);
+
+#endif
diff --git a/tests/core/encoding.c b/tests/core/encoding.c
new file mode 100644
index 0000000..7d91720
--- /dev/null
+++ b/tests/core/encoding.c
@@ -0,0 +1,39 @@
+#include "clar_libgit2.h"
+#include "varint.h"
+
+void test_core_encoding__decode(void)
+{
+	const unsigned char *buf = (unsigned char *)"AB";
+	size_t size;
+
+	cl_assert(git_decode_varint(buf, &size) == 65);
+	cl_assert(size == 1);
+
+	buf = (unsigned char *)"\xfe\xdc\xbaXY";
+	cl_assert(git_decode_varint(buf, &size) == 267869656);
+	cl_assert(size == 4);
+
+	buf = (unsigned char *)"\xaa\xaa\xfe\xdc\xbaXY";
+	cl_assert(git_decode_varint(buf, &size) == 1489279344088ULL);
+	cl_assert(size == 6);
+
+	buf = (unsigned char *)"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xfe\xdc\xbaXY";
+	cl_assert(git_decode_varint(buf, &size) == 0);
+	cl_assert(size == 0);
+
+}
+
+void test_core_encoding__encode(void)
+{
+	unsigned char buf[100];
+	cl_assert(git_encode_varint(buf, 100, 65) == 1);
+	cl_assert(buf[0] == 'A');
+
+	cl_assert(git_encode_varint(buf, 100, 267869656) == 4);
+	cl_assert(!memcmp(buf, "\xfe\xdc\xbaX", 4));
+
+	cl_assert(git_encode_varint(buf, 100, 1489279344088ULL) == 6);
+	cl_assert(!memcmp(buf, "\xaa\xaa\xfe\xdc\xbaX", 6));
+
+	cl_assert(git_encode_varint(buf, 1, 1489279344088ULL) == -1);
+}