bitvec: Add get/set byte sequences

The new functions bitvec_get_bytes and bitvec_set_bytes copy
byte sequences from bitvecs to uint8_t arrays and vice versa.
While the bytes in the bitvecs do not need to be aligned, the uint8_t
arrays always are. In case the bytes in the bitvec are aligned, the
implementation uses memcpy.

Note that the implementation like the other existing functions assume
MSB first encoding.

[hfreyther: Squash the comment fix into this commit as well]

Sponsored-by: On-Waves ehf
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9d14350..2411afa 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -11,7 +11,8 @@
 		 logging/logging_test fr/fr_test			\
 		 loggingrb/loggingrb_test strrb/strrb_test              \
 		 vty/vty_test comp128/comp128_test utils/utils_test	\
-		 smscb/gsm0341_test stats/stats_test
+		 smscb/gsm0341_test stats/stats_test			\
+		 bitvec/bitvec_test
 
 if ENABLE_MSGFILE
 check_PROGRAMS += msgfile/msgfile_test
@@ -38,6 +39,9 @@
 bits_bitrev_test_SOURCES = bits/bitrev_test.c
 bits_bitrev_test_LDADD = $(top_builddir)/src/libosmocore.la
 
+bitvec_bitvec_test_SOURCES = bitvec/bitvec_test.c
+bitvec_bitvec_test_LDADD = $(top_builddir)/src/libosmocore.la
+
 conv_conv_test_SOURCES = conv/conv_test.c
 conv_conv_test_LDADD = $(top_builddir)/src/libosmocore.la
 
@@ -128,7 +132,8 @@
              fr/fr_test.ok loggingrb/logging_test.ok			\
              loggingrb/logging_test.err	strrb/strrb_test.ok		\
 	     vty/vty_test.ok comp128/comp128_test.ok			\
-	     utils/utils_test.ok stats/stats_test.ok
+	     utils/utils_test.ok stats/stats_test.ok			\
+	     bitvec/bitvec_test.ok
 
 DISTCLEANFILES = atconfig
 
diff --git a/tests/bitvec/bitvec_test.c b/tests/bitvec/bitvec_test.c
new file mode 100644
index 0000000..624e334
--- /dev/null
+++ b/tests/bitvec/bitvec_test.c
@@ -0,0 +1,62 @@
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bitvec.h>
+
+static void test_byte_ops()
+{
+	struct bitvec bv;
+	const uint8_t *in = (const uint8_t *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	uint8_t out[26 + 2];
+	uint8_t data[64];
+	int i;
+	int rc;
+	int in_size = strlen((const char *)in);
+
+	printf("=== start %s ===\n", __func__);
+
+	bv.data = data;
+	bv.data_len = sizeof(data);
+
+	for (i = 0; i < 32; i++) {
+		/* Write to bitvec */
+		memset(data, 0x00, sizeof(data));
+		bv.cur_bit = i;
+		rc = bitvec_set_uint(&bv, 0x7e, 8);
+		OSMO_ASSERT(rc >= 0);
+		rc = bitvec_set_bytes(&bv, in, in_size);
+		OSMO_ASSERT(rc >= 0);
+		rc = bitvec_set_uint(&bv, 0x7e, 8);
+		OSMO_ASSERT(rc >= 0);
+
+		fprintf(stderr, "bitvec: %s\n", osmo_hexdump(bv.data, bv.data_len));
+
+		/* Read from bitvec */
+		memset(out, 0xff, sizeof(out));
+		bv.cur_bit = i;
+		rc = bitvec_get_uint(&bv, 8);
+		OSMO_ASSERT(rc == 0x7e);
+		rc = bitvec_get_bytes(&bv, out + 1, in_size);
+		OSMO_ASSERT(rc >= 0);
+		rc = bitvec_get_uint(&bv, 8);
+		OSMO_ASSERT(rc == 0x7e);
+
+		fprintf(stderr, "out: %s\n", osmo_hexdump(out, sizeof(out)));
+
+		OSMO_ASSERT(out[0] == 0xff);
+		OSMO_ASSERT(out[in_size+1] == 0xff);
+		OSMO_ASSERT(memcmp(in, out + 1, in_size) == 0);
+	}
+
+	printf("=== end %s ===\n", __func__);
+}
+
+int main(int argc, char **argv)
+{
+	test_byte_ops();
+	return 0;
+}
diff --git a/tests/bitvec/bitvec_test.ok b/tests/bitvec/bitvec_test.ok
new file mode 100644
index 0000000..1f329af
--- /dev/null
+++ b/tests/bitvec/bitvec_test.ok
@@ -0,0 +1,2 @@
+=== start test_byte_ops ===
+=== end test_byte_ops ===
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 85c3e8b..55e79f1 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -21,6 +21,12 @@
 AT_CHECK([$abs_top_builddir/tests/bits/bitrev_test], [0], [expout])
 AT_CLEANUP
 
+AT_SETUP([bitvec])
+AT_KEYWORDS([bitvec])
+cat $abs_srcdir/bitvec/bitvec_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/bitvec/bitvec_test], [0], [expout], [ignore])
+AT_CLEANUP
+
 AT_SETUP([conv])
 AT_KEYWORDS([conv])
 cat $abs_srcdir/conv/conv_test.ok > expout