From 1c4bc7aca79ba660a26f43e66d6eb96c490061ca Mon Sep 17 00:00:00 2001
From: Roman Tsisyk <roman@tsisyk.com>
Date: Tue, 15 Jan 2013 16:29:41 +0400
Subject: [PATCH] libbit - Bit manipulation library (initial import)

---
 CMakeLists.txt             |  19 +++
 include/config.h.cmake     |  12 ++
 include/lib/bit/bit.h      | 295 +++++++++++++++++++++++++++++++++++++
 src/CMakeLists.txt         |   2 +
 src/lib/CMakeLists.txt     |   7 +
 src/lib/bit/CMakeLists.txt |   3 +
 src/lib/bit/bit.c          | 144 ++++++++++++++++++
 7 files changed, 482 insertions(+)
 create mode 100644 include/lib/bit/bit.h
 create mode 100644 src/lib/CMakeLists.txt
 create mode 100644 src/lib/bit/CMakeLists.txt
 create mode 100644 src/lib/bit/bit.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 55774f891b..0d2da0809a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,6 +9,7 @@ include(CheckLibraryExists)
 include(CheckIncludeFile)
 include(CheckCCompilerFlag)
 include(CheckSymbolExists)
+include(CheckCSourceRuns)
 include(CheckCXXSourceRuns)
 include(TestBigEndian)
 include(CheckFunctionExists)
@@ -94,6 +95,24 @@ if (CMAKE_COMPILER_IS_GNUCC)
     endif()
 endif()
 
+# CMake's check_function_exists doesn't check builtins
+check_c_source_runs("int main(int a) { __builtin_ctz(a); return 0; }"
+    HAVE_CTZ)
+check_c_source_runs("int main(int a) { __builtin_ctzll(a); return 0; }"
+    HAVE_CTZLL)
+check_c_source_runs("int main(int a) { __builtin_clz(a); return 0; }"
+    HAVE_CLZ)
+check_c_source_runs("int main(int a) { __builtin_clzll(a); return 0; }"
+    HAVE_CLZLL)
+check_c_source_runs("int main(int a) { __builtin_popcount(a); return 0; }"
+    HAVE_POPCOUNT)
+check_c_source_runs("int main(int a) { __builtin_popcountll(a); return 0; }"
+    HAVE_POPCOUNTLL)
+check_c_source_runs("int main(int a) { __builtin_bswap32(a); return 0; }"
+    HAVE_BSWAP32)
+check_c_source_runs("int main(int a) { __builtin_bswap64(a); return 0; }"
+    HAVE_BSWAP64)
+
 #
 # Enable 'make TAGS' target.
 #
diff --git a/include/config.h.cmake b/include/config.h.cmake
index 407b4d8fc7..b348c9504f 100644
--- a/include/config.h.cmake
+++ b/include/config.h.cmake
@@ -74,6 +74,18 @@
  */
 #cmakedefine HAVE_BYTE_ORDER_BIG_ENDIAN 1
 
+/*
+ * Set if compiler has __builtin_XXX methods.
+ */
+#cmakedefine HAVE_CTZ 1
+#cmakedefine HAVE_CTZLL 1
+#cmakedefine HAVE_CLZ 1
+#cmakedefine HAVE_CLZLL 1
+#cmakedefine HAVE_POPCOUNT 1
+#cmakedefine HAVE_POPCOUNTLL 1
+#cmakedefine HAVE_BSWAP32 1
+#cmakedefine HAVE_BSWAP64 1
+
 /*
  * pthread have problems with -std=c99
  */
diff --git a/include/lib/bit/bit.h b/include/lib/bit/bit.h
new file mode 100644
index 0000000000..5af73a025f
--- /dev/null
+++ b/include/lib/bit/bit.h
@@ -0,0 +1,295 @@
+#ifndef TARANTOOL_LIB_BIT_BIT_H_INCLUDED
+#define TARANTOOL_LIB_BIT_BIT_H_INCLUDED
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief Bit manipulation library
+ */
+#include "config.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <limits.h>
+
+/**
+ * @cond false
+ * @brief Naive implementation of ctz.
+ */
+#define CTZ_NAIVE(x, bitsize) {						\
+	if (x == 0) {							\
+		return (bitsize);					\
+	}								\
+									\
+	int r = 0;							\
+	for (; (x & 1) == 0; r++) {					\
+		x >>= 1;						\
+	}								\
+									\
+	return r;							\
+}
+/** @endcond */
+
+/**
+ * @brief Count Trailing Zeros.
+ * Returns the number of trailing 0-bits in @a x, starting at the least
+ * significant bit position. If @a x is 0, the result is undefined.
+ * @param x integer
+ * @see __builtin_ctz()
+ * @return the number trailing 0-bits
+ */
+ __attribute__((const)) inline int
+bit_ctz_u32(uint32_t x)
+{
+#if defined(HAVE_CTZ)
+	return __builtin_ctz(x);
+#else /* !defined(HAVE_CTZ) */
+	CTZ_NAIVE(x, sizeof(uint32_t) * CHAR_BIT);
+#endif
+}
+
+/**
+ * @copydoc bit_ctz_u32
+ */
+ __attribute__((const)) inline int
+bit_ctz_u64(uint64_t x)
+{
+#if   defined(HAVE_CTZLL)
+	return __builtin_ctzll(x);
+#else /* !defined(HAVE_CTZLL) */
+	CTZ_NAIVE(x, sizeof(uint64_t) * CHAR_BIT);
+#endif
+}
+
+#undef CTZ_NAIVE
+
+/**
+ * @cond false
+ * @brief Naive implementation of clz.
+ */
+#define CLZ_NAIVE(x, bitsize) {						\
+	if (x == 0) {							\
+		return  (bitsize);					\
+	}								\
+									\
+	int r = (bitsize);						\
+	for (; x; r--) {						\
+		x >>= 1;						\
+	}								\
+									\
+	return r;							\
+}
+/** @endcond */
+
+/**
+ * @brief Count Leading Zeros.
+ * Returns the number of leading 0-bits in @a x, starting at the most
+ * significant bit position. If @a x is 0, the result is undefined.
+ * @param x integer
+ * @see __builtin_clz()
+ * @return the number of leading 0-bits
+ */
+ __attribute__((const)) inline int
+bit_clz_u32(uint32_t x)
+{
+#if   defined(HAVE_CLZ)
+	return __builtin_clz(x);
+#else /* !defined(HAVE_CLZ) */
+	CLZ_NAIVE(x, sizeof(uint32_t) * CHAR_BIT);
+#endif
+}
+
+/**
+ * @copydoc bit_clz_u32
+ */
+__attribute__((const)) inline int
+bit_clz_u64(uint64_t x)
+{
+#if   defined(HAVE_CLZLL)
+	return __builtin_clzll(x);
+#else /* !defined(HAVE_CLZLL) */
+	CLZ_NAIVE(x, sizeof(uint64_t) * CHAR_BIT);
+#endif
+}
+
+#undef CLZ_NAIVE
+
+/**
+ * @cond false
+ * @brief Naive implementation of popcount.
+ */
+#define POPCOUNT_NAIVE(x, bitsize)  {					\
+	int r;								\
+	for (r = 0; x; r++) {						\
+		x &= (x-1);						\
+	}								\
+									\
+	return r;							\
+}
+/** @endcond */
+
+/**
+ * @brief Returns the number of 1-bits in @a x.
+ * @param x integer
+ * @see __builtin_popcount()
+ * @return the number of 1-bits in @a x
+ */
+__attribute__((const)) inline int
+bit_count_u32(uint32_t x)
+{
+#if   defined(HAVE_POPCOUNT)
+	return __builtin_popcount(x);
+#else /* !defined(HAVE_POPCOUNT) */
+	POPCOUNT_NAIVE(x, sizeof(uint32_t) * CHAR_BIT);
+#endif
+}
+
+/**
+ * @copydoc bit_count_u32
+ */
+__attribute__((const)) inline int
+bit_count_u64(uint64_t x)
+{
+#if   defined(HAVE_POPCOUNTLL)
+	return __builtin_popcountll(x);
+#else /* !defined(HAVE_POPCOUNTLL) */
+	POPCOUNT_NAIVE(x, sizeof(uint64_t) * CHAR_BIT);
+#endif
+}
+
+#undef POPCOUNT_NAIVE
+
+/**
+ * @brief Rotate @a x left by @a r bits
+ * @param x integer
+ * @param r number for bits to rotate
+ * @return @a x rotated left by @a r bits
+ */
+__attribute__ ((const)) inline uint32_t
+bit_rotl_u32(uint32_t x, int r)
+{
+	/* gcc recognises this code and generates a rotate instruction */
+	return ((x << r) | (x >> (32 - r)));
+}
+
+/**
+ * @copydoc bit_rotl_u32
+ */
+__attribute__ ((const)) inline uint64_t
+bit_rotl_u64(uint64_t x, int r)
+{
+	/* gcc recognises this code and generates a rotate instruction */
+	return ((x << r) | (x >> (64 - r)));
+}
+
+/**
+ * @brief Rotate @a x right by @a r bits
+ * @param x integer
+ * @param r number for bits to rotate
+ * @return @a x rotated right by @a r bits
+ * @todo Move this method to bit.h
+ */
+__attribute__ ((const)) inline uint32_t
+bit_rotr_u32(uint32_t x, int r)
+{
+	/* gcc recognises this code and generates a rotate instruction */
+	return ((x >> r) | (x << (32 - r)));
+}
+
+/**
+ * @copydoc bit_rotr_u32
+ */
+__attribute__ ((const)) inline uint64_t
+bit_rotr_u64(uint64_t x, int r)
+{
+	/* gcc recognises this code and generates a rotate instruction */
+	return ((x >> r) | (x << (64 - r)));
+}
+
+/**
+ * @brief Returns a byte order swapped integer @a x.
+ * This function does not take into account host architecture
+ * (as it done by htonl / ntohl functions) and always returns @a x
+ * with byte order swapped (BE -> LE if @a x is in BE and vice versa).
+ * @param x integer
+ * @return @a x with swapped bytes
+ */
+__attribute__ ((const)) inline uint32_t
+bswap_u32(uint32_t x)
+{
+#if defined(HAVE_BSWAP32)
+	return __builtin_bswap32(x);
+#else /* !defined(HAVE_BSWAP32) */
+	return	((x << 24) & UINT32_C(0xff000000)) |
+		((x <<  8) & UINT32_C(0x00ff0000)) |
+		((x >>  8) & UINT32_C(0x0000ff00)) |
+		((x >> 24) & UINT32_C(0x000000ff));
+#endif
+}
+
+/**
+ * @copydoc bswap_u32
+ */
+__attribute__ ((const)) inline uint64_t
+bswap_u64(uint64_t x)
+{
+#if defined(HAVE_BSWAP64)
+	return __builtin_bswap64(x);
+#else /* !defined(HAVE_BSWAP64) */
+	return  ( (x << 56) & UINT64_C(0xff00000000000000)) |
+		( (x << 40) & UINT64_C(0x00ff000000000000)) |
+		( (x << 24) & UINT64_C(0x0000ff0000000000)) |
+		( (x <<  8) & UINT64_C(0x000000ff00000000)) |
+		( (x >>  8) & UINT64_C(0x00000000ff000000)) |
+		( (x >> 24) & UINT64_C(0x0000000000ff0000)) |
+		( (x >> 40) & UINT64_C(0x000000000000ff00)) |
+		( (x >> 56) & UINT64_C(0x00000000000000ff));
+#endif
+}
+
+/**
+ * @brief Index bits in the @a x, i.e. find all positions where bits are set.
+ * This method fills @a indexes array with found positions in increasing order.
+ * @a offset is added to each index before putting it into @a indexes.
+ * @param x integer
+ * @param indexes memory array where found indexes are stored
+ * @param offset a number added to each index
+ * @return pointer to last+1 element in indexes array
+ */
+int *
+bit_index_u32(uint32_t x, int *indexes, int offset);
+
+/**
+ * @copydoc bit_index_u32
+ */
+int *
+bit_index_u64(uint64_t x, int *indexes, int offset);
+
+#endif /* TARANTOOL_LIB_BIT_BIT_H_INCLUDED */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d4dbfa4861..6d4a02ce73 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -143,6 +143,8 @@ endif()
 
 set (common_libraries ${common_libraries} PARENT_SCOPE)
 
+add_subdirectory(lib)
+
 function(tarantool_module mod)
     set (module_sources ${ARGN})
     set(cfg_c_flags "-Wno-unused -Wno-unused-parameter")
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
new file mode 100644
index 0000000000..e0794be0dc
--- /dev/null
+++ b/src/lib/CMakeLists.txt
@@ -0,0 +1,7 @@
+include_directories("${PROJECT_SOURCE_DIR}/include/lib")
+
+add_custom_target(libs)
+
+add_subdirectory(bit)
+add_dependencies(libs bit)
+
diff --git a/src/lib/bit/CMakeLists.txt b/src/lib/bit/CMakeLists.txt
new file mode 100644
index 0000000000..41489456e7
--- /dev/null
+++ b/src/lib/bit/CMakeLists.txt
@@ -0,0 +1,3 @@
+aux_source_directory(. lib_sources)
+set_source_files_compile_flags(${lib_sources})
+add_library(bit STATIC ${lib_sources})
diff --git a/src/lib/bit/bit.c b/src/lib/bit/bit.c
new file mode 100644
index 0000000000..a7bd02655e
--- /dev/null
+++ b/src/lib/bit/bit.c
@@ -0,0 +1,144 @@
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <lib/bit/bit.h>
+
+__attribute__((const)) extern inline int
+bit_ctz_u32(uint32_t x);
+
+__attribute__((const))  extern inline int
+bit_ctz_u64(uint64_t x);
+
+__attribute__((const)) extern inline int
+bit_clz_u32(uint32_t x);
+
+__attribute__((const)) extern inline int
+bit_clz_u64(uint64_t x);
+
+__attribute__((const)) extern inline int
+bit_count_u32(uint32_t x);
+
+__attribute__((const)) extern inline int
+bit_count_u64(uint64_t x);
+
+__attribute__ ((const)) extern inline uint32_t
+bit_rotl_u32(uint32_t x, int r);
+
+__attribute__ ((const)) extern inline uint64_t
+bit_rotl_u64(uint64_t x, int r);
+
+__attribute__ ((const)) extern inline uint32_t
+bit_rotr_u32(uint32_t x, int r);
+
+__attribute__ ((const)) extern inline uint64_t
+bit_rotr_u64(uint64_t x, int r);
+
+__attribute__ ((const)) extern inline uint32_t
+bswap_u32(uint32_t x);
+
+__attribute__ ((const)) extern inline uint64_t
+bswap_u64(uint64_t x);
+
+#define BITINDEX_NAIVE(x, bitsize) {					\
+	/* naive generic implementation, worst case */			\
+	typeof(x) bit = 1;						\
+	int i = 0;							\
+	for (int k = 0; k < bitsize; k++) {				\
+		if (x & bit) {						\
+			indexes[i++] = offset + k + 1;			\
+		}							\
+		bit <<= 1;						\
+	}								\
+									\
+	indexes[i] = 0;							\
+	return indexes + i;						\
+}
+
+int *
+bit_index_u32(uint32_t x, int *indexes, int offset)
+{
+#if  defined(HAVE_CTZ)
+	int prev_pos = 0;
+	int i = 0;
+
+#if defined(HAVE_POPCOUNT)
+	/* fast implementation using built-in popcount function */
+	const int count = bit_count_u32(x);
+	while (i < count) {
+#else
+	/* sligtly slower implementation without using built-in popcount */
+	while(x) {
+#endif
+		/* use ctz */
+		const int a = bit_ctz_u32(x);
+
+		prev_pos += a + 1;
+		x >>= a;
+		x >>= 1;
+		indexes[i++] = offset + prev_pos;
+	}
+
+	indexes[i] = 0;
+	return indexes + i;
+#else /* !defined(HAVE_CTZ) */
+	BITINDEX_NAIVE(x, sizeof(uint32_t) * CHAR_BIT);
+#endif
+}
+
+int *
+bit_index_u64(uint64_t x, int *indexes, int offset) {
+#if  defined(HAVE_CTZLL)
+	int prev_pos = 0;
+	int i = 0;
+
+#if defined(HAVE_POPCOUNTLL)
+	/* fast implementation using built-in popcount function */
+	const int count = bit_count_u64(x);
+	while (i < count) {
+#else
+	/* sligtly slower implementation without using built-in popcount */
+	while(x) {
+#endif
+		/* use ctz */
+		const int a = bit_ctz_u64(x);
+
+		prev_pos += a + 1;
+		x >>= a;
+		x >>= 1;
+		indexes[i++] = offset + prev_pos;
+	}
+
+	indexes[i] = 0;
+	return indexes + i;
+#else /* !defined(HAVE_CTZ) */
+	BITINDEX_NAIVE(x, sizeof(uint64_t) * CHAR_BIT);
+#endif
+}
+
+#undef BITINDEX_NAIVE
-- 
GitLab