Commit 6fc5a58197bf04e1b5c6ca1bdb5765e90d3eb106

Russell Belfer 2013-07-08T22:42:02

Basic bit vector This is a simple bit vector object that is not resizable after the initial allocation but can be of arbitrary size. It will keep the bti vector entirely on the stack for vectors 64 bits or less, and will allocate the vector on the heap for larger sizes. The API is uniform regardless of storage location. This is very basic right now and all the APIs are inline functions, but it is useful for storing an array of boolean values.

diff --git a/src/bitvec.h b/src/bitvec.h
new file mode 100644
index 0000000..a033f53
--- /dev/null
+++ b/src/bitvec.h
@@ -0,0 +1,92 @@
+/*
+ * 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_bitvec_h__
+#define INCLUDE_bitvec_h__
+
+#include "util.h"
+
+/*
+ * This is a silly little fixed length bit vector type that will store
+ * vectors of 64 bits or less directly in the structure and allocate
+ * memory for vectors longer than 64 bits.  You can use the two versions
+ * transparently through the API and avoid heap allocation completely when
+ * using a short bit vector as a result.
+ */
+typedef struct {
+	size_t length;
+	union {
+		uint8_t *ptr;
+		uint64_t bits;
+	} u;
+} git_bitvec;
+
+GIT_INLINE(int) git_bitvec_init(git_bitvec *bv, size_t capacity)
+{
+	if (capacity < 64) {
+		bv->length = 0;
+		bv->u.bits = 0;
+		return 0;
+	}
+
+	bv->length = (capacity + 7) / 8;
+	bv->u.ptr  = git__calloc(bv->length, 1);
+	return bv->u.ptr ? 0 : -1;
+}
+
+#define GIT_BITVEC_MASK_INLINE(BIT) (((uint64_t)1) << BIT)
+
+#define GIT_BITVEC_MASK_BYTE(BIT)  (((uint8_t)1) << ((BIT) & 0x07))
+#define GIT_BITVEC_INDEX_BYTE(BIT) ((BIT) >> 3)
+
+GIT_INLINE(void) git_bitvec_set(git_bitvec *bv, size_t bit, bool on)
+{
+	if (!bv->length) {
+		assert(bit < 64);
+
+		if (on)
+			bv->u.bits |= GIT_BITVEC_MASK_INLINE(bit);
+		else
+			bv->u.bits &= ~GIT_BITVEC_MASK_INLINE(bit);
+	} else {
+		assert(bit < bv->length * 8);
+
+		if (on)
+			bv->u.ptr[GIT_BITVEC_INDEX_BYTE(bit)] |= GIT_BITVEC_MASK_BYTE(bit);
+		else
+			bv->u.ptr[GIT_BITVEC_INDEX_BYTE(bit)] &= ~GIT_BITVEC_MASK_BYTE(bit);
+	}
+}
+
+GIT_INLINE(bool) git_bitvec_get(git_bitvec *bv, size_t bit)
+{
+	if (!bv->length) {
+		assert(bit < 64);
+		return (bv->u.bits & GIT_BITVEC_MASK_INLINE(bit)) != 0;
+	} else {
+		assert(bit < bv->length * 8);
+		return (bv->u.ptr[GIT_BITVEC_INDEX_BYTE(bit)] &
+				GIT_BITVEC_MASK_BYTE(bit)) != 0;
+	}
+}
+
+GIT_INLINE(void) git_bitvec_clear(git_bitvec *bv)
+{
+	if (!bv->length)
+		bv->u.bits = 0;
+	else
+		memset(bv->u.ptr, 0, bv->length);
+}
+
+GIT_INLINE(void) git_bitvec_free(git_bitvec *bv)
+{
+	if (bv->length) {
+		git__free(bv->u.ptr);
+		memset(bv, 0, sizeof(*bv));
+	}
+}
+
+#endif
diff --git a/tests-clar/core/bitvec.c b/tests-clar/core/bitvec.c
new file mode 100644
index 0000000..48d7b99
--- /dev/null
+++ b/tests-clar/core/bitvec.c
@@ -0,0 +1,64 @@
+#include "clar_libgit2.h"
+#include "bitvec.h"
+
+#if 0
+static void print_bitvec(git_bitvec *bv)
+{
+	int b;
+
+	if (!bv->length) {
+		for (b = 63; b >= 0; --b)
+			fprintf(stderr, "%d", (bv->u.bits & (1ul << b)) ? 1 : 0);
+	} else {
+		for (b = bv->length * 8; b >= 0; --b)
+			fprintf(stderr, "%d", (bv->u.ptr[b >> 3] & (b & 0x0ff)) ? 1 : 0);
+	}
+	fprintf(stderr, "\n");
+}
+#endif
+
+static void set_some_bits(git_bitvec *bv, size_t length)
+{
+	size_t i;
+
+	for (i = 0; i < length; ++i) {
+		if (i % 3 == 0 || i % 7 == 0)
+			git_bitvec_set(bv, i, true);
+	}
+}
+
+static void check_some_bits(git_bitvec *bv, size_t length)
+{
+	size_t i;
+
+	for (i = 0; i < length; ++i)
+		cl_assert_equal_b(i % 3 == 0 || i % 7 == 0, git_bitvec_get(bv, i));
+}
+
+void test_core_bitvec__0(void)
+{
+	git_bitvec bv;
+
+	cl_git_pass(git_bitvec_init(&bv, 32));
+	set_some_bits(&bv, 16);
+	check_some_bits(&bv, 16);
+	git_bitvec_clear(&bv);
+	set_some_bits(&bv, 32);
+	check_some_bits(&bv, 32);
+	git_bitvec_clear(&bv);
+	set_some_bits(&bv, 64);
+	check_some_bits(&bv, 64);
+	git_bitvec_free(&bv);
+
+	cl_git_pass(git_bitvec_init(&bv, 128));
+	set_some_bits(&bv, 32);
+	check_some_bits(&bv, 32);
+	set_some_bits(&bv, 128);
+	check_some_bits(&bv, 128);
+	git_bitvec_free(&bv);
+
+	cl_git_pass(git_bitvec_init(&bv, 4000));
+	set_some_bits(&bv, 4000);
+	check_some_bits(&bv, 4000);
+	git_bitvec_free(&bv);
+}