add functions for bit-reversal

Sometimes we need stuff like reversing every bit in each byte (but not
the byte-order).
diff --git a/configure.ac b/configure.ac
index ef4b0aa..f624f2a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -123,6 +123,7 @@
 	tests/msgfile/Makefile
 	tests/ussd/Makefile
 	tests/smscb/Makefile
+	tests/bits/Makefile
 	utils/Makefile
 	Doxyfile.core
 	Doxyfile.gsm
diff --git a/include/osmocom/core/bits.h b/include/osmocom/core/bits.h
index ab4cf77..b541b9c 100644
--- a/include/osmocom/core/bits.h
+++ b/include/osmocom/core/bits.h
@@ -46,6 +46,33 @@
                        const pbit_t *in, unsigned int in_ofs,
                        unsigned int num_bits, int lsb_mode);
 
+
+/* BIT REVERSAL */
+
+/*! \brief bit-reversal mode for osmo_bit_reversal() */
+enum osmo_br_mode {
+	/*! \brief reverse all bits in a 32bit dword */
+	OSMO_BR_BITS_IN_DWORD	= 31,
+	/*! \brief reverse byte order in a 32bit dword */
+	OSMO_BR_BYTES_IN_DWORD	= 24,
+	/*! \brief reverse bits of each byte in a 32bit dword */
+	OSMO_BR_BITS_IN_BYTE	= 7,
+	/*! \brief swap the two 16bit words in a 32bit dword */
+	OSMO_BR_WORD_SWAP	= 16,
+};
+
+/*! \brief generic bit reversal function */
+uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k);
+
+/* \brief reverse the bits within each byte of a 32bit word */
+uint32_t osmo_revbytebits_32(uint32_t x);
+
+/* \brief reverse the bits within a byte */
+uint32_t osmo_revbytebits_8(uint8_t x);
+
+/* \brief reverse the bits of each byte in a given buffer */
+void osmo_revbytebits_buf(uint8_t *buf, int len);
+
 /*! }@ */
 
 #endif /* _OSMO_BITS_H */
diff --git a/src/bits.c b/src/bits.c
index 3955e3b..9eb2d69 100644
--- a/src/bits.c
+++ b/src/bits.c
@@ -128,4 +128,61 @@
 	return out_ofs + num_bits;
 }
 
+/* generalized bit reversal function, Chapter 7 "Hackers Delight" */
+uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k)
+{
+	if (k &  1) x = (x & 0x55555555) <<  1 | (x & 0xAAAAAAAA) >>  1;
+	if (k &  2) x = (x & 0x33333333) <<  2 | (x & 0xCCCCCCCC) >>  2;
+	if (k &  4) x = (x & 0x0F0F0F0F) <<  4 | (x & 0xF0F0F0F0) >>  4;
+	if (k &  8) x = (x & 0x00FF00FF) <<  8 | (x & 0xFF00FF00) >>  8;
+	if (k & 16) x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;
+
+	return x;
+}
+
+/* generalized bit reversal function, Chapter 7 "Hackers Delight" */
+uint32_t osmo_revbytebits_32(uint32_t x)
+{
+	x = (x & 0x55555555) <<  1 | (x & 0xAAAAAAAA) >>  1;
+	x = (x & 0x33333333) <<  2 | (x & 0xCCCCCCCC) >>  2;
+	x = (x & 0x0F0F0F0F) <<  4 | (x & 0xF0F0F0F0) >>  4;
+
+	return x;
+}
+
+uint32_t osmo_revbytebits_8(uint8_t x)
+{
+	x = (x & 0x55) <<  1 | (x & 0xAA) >>  1;
+	x = (x & 0x33) <<  2 | (x & 0xCC) >>  2;
+	x = (x & 0x0F) <<  4 | (x & 0xF0) >>  4;
+
+	return x;
+}
+
+void osmo_revbytebits_buf(uint8_t *buf, int len)
+{
+	unsigned int i;
+	unsigned int unaligned_cnt;
+	int len_remain = len;
+
+	unaligned_cnt = ((unsigned long)buf & 3);
+	for (i = 0; i < unaligned_cnt; i++) {
+		buf[i] = osmo_revbytebits_8(buf[i]);
+		len_remain--;
+		if (len_remain <= 0)
+			return;
+	}
+
+	for (i = unaligned_cnt; i < len; i += 4) {
+		uint32_t *cur = (uint32_t *) (buf + i);
+		*cur = osmo_revbytebits_32(*cur);
+		len_remain -= 4;
+	}
+
+	for (i = len - len_remain; i < len; i++) {
+		buf[i] = osmo_revbytebits_8(buf[i]);
+		len_remain--;
+	}
+}
+
 /*! }@ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 2b4ac6e..6c3cb33 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,5 +1,5 @@
 if ENABLE_TESTS
-SUBDIRS = timer sms ussd smscb
+SUBDIRS = timer sms ussd smscb bits
 if ENABLE_MSGFILE
 SUBDIRS += msgfile
 endif
diff --git a/tests/bits/Makefile.am b/tests/bits/Makefile.am
new file mode 100644
index 0000000..dd03e83
--- /dev/null
+++ b/tests/bits/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = bitrev_test
+
+bitrev_test_SOURCES = bitrev_test.c
+bitrev_test_LDADD = $(top_builddir)/src/libosmocore.la
+
diff --git a/tests/bits/bitrev_test.c b/tests/bits/bitrev_test.c
new file mode 100644
index 0000000..5eca990
--- /dev/null
+++ b/tests/bits/bitrev_test.c
@@ -0,0 +1,36 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bits.h>
+
+static const uint8_t input[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+static const uint8_t exp_out[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+
+int main(int argc, char **argv)
+{
+	uint8_t out[ARRAY_SIZE(input)];
+	unsigned int offs;
+
+	for (offs = 0; offs < sizeof(out); offs++) {
+		uint8_t *start = out + offs;
+		uint8_t len = sizeof(out) - offs;
+
+		memcpy(out, input, sizeof(out));
+
+		printf("INORDER:  %s\n", osmo_hexdump(start, len));
+		osmo_revbytebits_buf(start, len);
+		printf("REVERSED: %s\n", osmo_hexdump(start, len));
+		if (memcmp(start, exp_out + offs, len)) {
+			printf("EXPECTED: %s\n", osmo_hexdump(exp_out+offs, len));
+			fprintf(stderr, "REVERSED != EXPECTED!\n");
+			exit(1);
+		}
+		printf("\n");
+	}
+
+	return 0;
+}