From 6c5f05f177c793bcb53bdd3542a6a0ee03ad8dbc Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Wed, 14 Sep 2016 17:12:15 -0700 Subject: [PATCH] util: add bit array data structure Change-Id: Idab4473fa23486e72334ec07d0853c6325197c20 Signed-off-by: Daniel Verkamp --- autotest.sh | 1 + include/spdk/bit_array.h | 136 ++++++++++++ lib/util/Makefile | 2 +- lib/util/bit_array.c | 274 +++++++++++++++++++++++++ test/lib/Makefile | 2 +- test/lib/util/Makefile | 44 ++++ test/lib/util/bit_array/.gitignore | 1 + test/lib/util/bit_array/Makefile | 52 +++++ test/lib/util/bit_array/bit_array_ut.c | 241 ++++++++++++++++++++++ test/lib/util/util.sh | 13 ++ unittest.sh | 4 + 11 files changed, 768 insertions(+), 2 deletions(-) create mode 100644 include/spdk/bit_array.h create mode 100644 lib/util/bit_array.c create mode 100644 test/lib/util/Makefile create mode 100644 test/lib/util/bit_array/.gitignore create mode 100644 test/lib/util/bit_array/Makefile create mode 100644 test/lib/util/bit_array/bit_array_ut.c create mode 100755 test/lib/util/util.sh diff --git a/autotest.sh b/autotest.sh index bebbe9128..acde101b1 100755 --- a/autotest.sh +++ b/autotest.sh @@ -82,6 +82,7 @@ time test/lib/jsonrpc/jsonrpc.sh time test/lib/log/log.sh time test/lib/scsi/scsi.sh time test/lib/iscsi/iscsi.sh +time test/lib/util/util.sh timing_exit lib diff --git a/include/spdk/bit_array.h b/include/spdk/bit_array.h new file mode 100644 index 000000000..7d013d075 --- /dev/null +++ b/include/spdk/bit_array.h @@ -0,0 +1,136 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 + * Bit array data structure + */ + +#ifndef SPDK_BIT_ARRAY_H +#define SPDK_BIT_ARRAY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** + * Variable-length bit array. + */ +struct spdk_bit_array; + +/** + * Return the number of bits a bit array is currently sized to hold. + */ +uint32_t spdk_bit_array_capacity(const struct spdk_bit_array *ba); + +/** + * Create a bit array. + */ +struct spdk_bit_array *spdk_bit_array_create(uint32_t num_bits); + +/** + * Free a bit array and set the pointer to NULL. + */ +void spdk_bit_array_free(struct spdk_bit_array **bap); + +/** + * Create or resize a bit array. + * + * To create a new bit array, pass a pointer to a spdk_bit_array pointer that is NULL for bap. + * + * The bit array will be sized to hold at least num_bits. + * + * If num_bits is smaller than the previous size of the bit array, + * any data beyond the new num_bits size will be cleared. + * + * If num_bits is larger than the previous size of the bit array, + * any data beyond the old num_bits size will be cleared. + */ +int spdk_bit_array_resize(struct spdk_bit_array **bap, uint32_t num_bits); + +/** + * Get the value of a bit from the bit array. + * + * If bit_index is beyond the end of the current size of the bit array, + * this function will return false (i.e. bits beyond the end of the array are implicitly 0). + */ +bool spdk_bit_array_get(const struct spdk_bit_array *ba, uint32_t bit_index); + +/** + * Set (to 1) a bit in the bit array. + * + * If bit_index is beyond the end of the bit array, this function will return -EINVAL. + * On success, returns 0. + */ +int spdk_bit_array_set(struct spdk_bit_array *ba, uint32_t bit_index); + +/** + * Clear (to 0) a bit in the bit array. + * + * If bit_index is beyond the end of the bit array, no action is taken. Bits beyond the end of the + * bit array are implicitly 0. + */ +void spdk_bit_array_clear(struct spdk_bit_array *ba, uint32_t bit_index); + +/** + * Find the index of the first set bit in the array. + * + * \param ba The bit array to search. + * \param start_bit_index The bit index from which to start searching (0 to start from the beginning + * of the array). + * + * \return The index of the first set bit. If no bits are set, returns UINT32_MAX. + */ +uint32_t spdk_bit_array_find_first_set(const struct spdk_bit_array *ba, uint32_t start_bit_index); + +/** + * Find the index of the first cleared bit in the array. + * + * \param ba The bit array to search. + * \param start_bit_index The bit index from which to start searching (0 to start from the beginning + * of the array).. + * + * \return The index of the first cleared bit. Bits beyond the current size of the array are + * implicitly cleared, so if all bits within the current size are set, this function will return + * the current number of bits + 1. + */ +uint32_t spdk_bit_array_find_first_clear(const struct spdk_bit_array *ba, uint32_t start_bit_index); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/util/Makefile b/lib/util/Makefile index 8474619ca..b6f3a9a03 100644 --- a/lib/util/Makefile +++ b/lib/util/Makefile @@ -35,7 +35,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk CFLAGS += $(DPDK_INC) -C_SRCS = fd.c string.c pci.c +C_SRCS = bit_array.c fd.c string.c pci.c LIBNAME = util include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/lib/util/bit_array.c b/lib/util/bit_array.c new file mode 100644 index 000000000..596f3d3c5 --- /dev/null +++ b/lib/util/bit_array.c @@ -0,0 +1,274 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "spdk/bit_array.h" + +#include +#include +#include +#include +#include +#include +#include + +typedef uint64_t spdk_bit_array_word; +#define SPDK_BIT_ARRAY_WORD_TZCNT(x) (__builtin_ctzll(x)) +#define SPDK_BIT_ARRAY_WORD_C(x) ((spdk_bit_array_word)(x)) +#define SPDK_BIT_ARRAY_WORD_BYTES sizeof(spdk_bit_array_word) +#define SPDK_BIT_ARRAY_WORD_BITS (SPDK_BIT_ARRAY_WORD_BYTES * 8) +#define SPDK_BIT_ARRAY_WORD_INDEX_SHIFT (31u - __builtin_clz(SPDK_BIT_ARRAY_WORD_BITS)) +#define SPDK_BIT_ARRAY_WORD_INDEX_MASK ((1u << SPDK_BIT_ARRAY_WORD_INDEX_SHIFT) - 1) + +struct spdk_bit_array { + uint32_t bit_count; + spdk_bit_array_word words[]; +}; + +struct spdk_bit_array * +spdk_bit_array_create(uint32_t num_bits) +{ + struct spdk_bit_array *ba = NULL; + + spdk_bit_array_resize(&ba, num_bits); + + return ba; +} + +void +spdk_bit_array_free(struct spdk_bit_array **bap) +{ + struct spdk_bit_array *ba; + + if (!bap) { + return; + } + + ba = *bap; + *bap = NULL; + free(ba); +} + +static inline uint32_t +spdk_bit_array_word_count(uint32_t num_bits) +{ + return (num_bits + SPDK_BIT_ARRAY_WORD_BITS - 1) >> SPDK_BIT_ARRAY_WORD_INDEX_SHIFT; +} + +static inline spdk_bit_array_word +spdk_bit_array_word_mask(uint32_t num_bits) +{ + assert(num_bits < SPDK_BIT_ARRAY_WORD_BITS); + return (SPDK_BIT_ARRAY_WORD_C(1) << num_bits) - 1; +} + +int +spdk_bit_array_resize(struct spdk_bit_array **bap, uint32_t num_bits) +{ + struct spdk_bit_array *new_ba; + uint32_t old_word_count, new_word_count; + size_t new_size; + + if (!bap) { + return -EINVAL; + } + + new_word_count = spdk_bit_array_word_count(num_bits); + new_size = offsetof(struct spdk_bit_array, words) + new_word_count * SPDK_BIT_ARRAY_WORD_BYTES; + + /* + * Always keep one extra word with a 0 and a 1 past the actual required size so that the + * find_first functions can just keep going until they match. + */ + new_size += SPDK_BIT_ARRAY_WORD_BYTES; + + new_ba = (struct spdk_bit_array *)realloc(*bap, new_size); + if (!new_ba) { + return -ENOMEM; + } + + /* + * Set up special extra word (see above comment about find_first_clear). + * + * This is set to 0b10 so that find_first_clear will find a 0 at the very first + * bit past the end of the buffer, and find_first_set will find a 1 at the next bit + * past that. + */ + new_ba->words[new_word_count] = 0x2; + + if (*bap == NULL) { + old_word_count = 0; + new_ba->bit_count = 0; + } else { + old_word_count = spdk_bit_array_word_count(new_ba->bit_count); + } + + if (new_word_count > old_word_count) { + /* Zero out new entries */ + memset(&new_ba->words[old_word_count], 0, + (new_word_count - old_word_count) * SPDK_BIT_ARRAY_WORD_BYTES); + } else if (new_word_count == old_word_count && num_bits < new_ba->bit_count) { + /* Make sure any existing partial last word is cleared beyond the new num_bits. */ + uint32_t last_word_bits; + spdk_bit_array_word mask; + + last_word_bits = num_bits & SPDK_BIT_ARRAY_WORD_INDEX_MASK; + mask = spdk_bit_array_word_mask(last_word_bits); + new_ba->words[old_word_count - 1] &= mask; + } + + new_ba->bit_count = num_bits; + *bap = new_ba; + return 0; +} + +uint32_t +spdk_bit_array_capacity(const struct spdk_bit_array *ba) +{ + return ba->bit_count; +} + +static inline int +_spdk_bit_array_get_word(const struct spdk_bit_array *ba, uint32_t bit_index, + uint32_t *word_index, uint32_t *word_bit_index) +{ + if (bit_index >= ba->bit_count) { + return -EINVAL; + } + + *word_index = bit_index >> SPDK_BIT_ARRAY_WORD_INDEX_SHIFT; + *word_bit_index = bit_index & SPDK_BIT_ARRAY_WORD_INDEX_MASK; + + return 0; +} + +bool +spdk_bit_array_get(const struct spdk_bit_array *ba, uint32_t bit_index) +{ + uint32_t word_index, word_bit_index; + + if (_spdk_bit_array_get_word(ba, bit_index, &word_index, &word_bit_index)) { + return false; + } + + return (ba->words[word_index] >> word_bit_index) & 1U; +} + +int +spdk_bit_array_set(struct spdk_bit_array *ba, uint32_t bit_index) +{ + uint32_t word_index, word_bit_index; + + if (_spdk_bit_array_get_word(ba, bit_index, &word_index, &word_bit_index)) { + return -EINVAL; + } + + ba->words[word_index] |= (SPDK_BIT_ARRAY_WORD_C(1) << word_bit_index); + return 0; +} + +void +spdk_bit_array_clear(struct spdk_bit_array *ba, uint32_t bit_index) +{ + uint32_t word_index, word_bit_index; + + if (_spdk_bit_array_get_word(ba, bit_index, &word_index, &word_bit_index)) { + /* + * Clearing past the end of the bit array is a no-op, since bit past the end + * are implicitly 0. + */ + return; + } + + ba->words[word_index] &= ~(SPDK_BIT_ARRAY_WORD_C(1) << word_bit_index); +} + +static inline uint32_t +_spdk_bit_array_find_first(const struct spdk_bit_array *ba, uint32_t start_bit_index, + spdk_bit_array_word xor_mask) +{ + uint32_t word_index, first_word_bit_index; + spdk_bit_array_word word, first_word_mask; + const spdk_bit_array_word *words, *cur_word; + + if (start_bit_index >= ba->bit_count) { + return ba->bit_count; + } + + word_index = start_bit_index >> SPDK_BIT_ARRAY_WORD_INDEX_SHIFT; + words = ba->words; + cur_word = &words[word_index]; + + /* + * Special case for first word: skip start_bit_index % SPDK_BIT_ARRAY_WORD_BITS bits + * within the first word. + */ + first_word_bit_index = start_bit_index & SPDK_BIT_ARRAY_WORD_INDEX_MASK; + first_word_mask = spdk_bit_array_word_mask(first_word_bit_index); + + word = (*cur_word ^ xor_mask) & ~first_word_mask; + + /* + * spdk_bit_array_resize() guarantees that an extra word with a 1 and a 0 will always be + * at the end of the words[] array, so just keep going until a word matches. + */ + while (word == 0) { + word = *++cur_word ^ xor_mask; + } + + return ((uintptr_t)cur_word - (uintptr_t)words) * 8 + SPDK_BIT_ARRAY_WORD_TZCNT(word); +} + + +uint32_t +spdk_bit_array_find_first_set(const struct spdk_bit_array *ba, uint32_t start_bit_index) +{ + uint32_t bit_index; + + bit_index = _spdk_bit_array_find_first(ba, start_bit_index, 0); + + /* + * If we ran off the end of the array and found the 1 bit in the extra word, + * return UINT32_MAX to indicate no actual 1 bits were found. + */ + if (bit_index >= ba->bit_count) { + bit_index = UINT32_MAX; + } + + return bit_index; +} + +uint32_t +spdk_bit_array_find_first_clear(const struct spdk_bit_array *ba, uint32_t start_bit_index) +{ + return _spdk_bit_array_find_first(ba, start_bit_index, SPDK_BIT_ARRAY_WORD_C(-1)); +} diff --git a/test/lib/Makefile b/test/lib/Makefile index c5f8f81c2..4337d9b7a 100644 --- a/test/lib/Makefile +++ b/test/lib/Makefile @@ -34,7 +34,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk -DIRS-y = bdev event log iscsi json jsonrpc nvme nvmf memory scsi ioat +DIRS-y = bdev event log iscsi json jsonrpc nvme nvmf memory scsi ioat util .PHONY: all clean $(DIRS-y) diff --git a/test/lib/util/Makefile b/test/lib/util/Makefile new file mode 100644 index 000000000..efefedb15 --- /dev/null +++ b/test/lib/util/Makefile @@ -0,0 +1,44 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT +# OWNER 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. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +DIRS-y = bit_array + +.PHONY: all clean $(DIRS-y) + +all: $(DIRS-y) +clean: $(DIRS-y) + +include $(SPDK_ROOT_DIR)/mk/spdk.subdirs.mk diff --git a/test/lib/util/bit_array/.gitignore b/test/lib/util/bit_array/.gitignore new file mode 100644 index 000000000..24300cdb3 --- /dev/null +++ b/test/lib/util/bit_array/.gitignore @@ -0,0 +1 @@ +bit_array_ut diff --git a/test/lib/util/bit_array/Makefile b/test/lib/util/bit_array/Makefile new file mode 100644 index 000000000..4ecaa4ef4 --- /dev/null +++ b/test/lib/util/bit_array/Makefile @@ -0,0 +1,52 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT +# OWNER 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. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +CFLAGS += -I$(SPDK_ROOT_DIR)/test +CFLAGS += -I$(SPDK_ROOT_DIR)/lib/util +APP = bit_array_ut +C_SRCS := bit_array_ut.c + +LIBS += -lcunit + +all : $(APP) + +$(APP) : $(OBJS) $(SPDK_LIBS) + $(LINK_C) + +clean : + $(CLEAN_C) $(APP) + +include $(SPDK_ROOT_DIR)/mk/spdk.deps.mk diff --git a/test/lib/util/bit_array/bit_array_ut.c b/test/lib/util/bit_array/bit_array_ut.c new file mode 100644 index 000000000..f9d661838 --- /dev/null +++ b/test/lib/util/bit_array/bit_array_ut.c @@ -0,0 +1,241 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 + +#include "spdk_cunit.h" + +#include "bit_array.c" + +static void +test_1bit(void) +{ + struct spdk_bit_array *ba; + + ba = spdk_bit_array_create(1); + SPDK_CU_ASSERT_FATAL(ba != NULL); + + CU_ASSERT(spdk_bit_array_get(ba, 0) == false); + CU_ASSERT(spdk_bit_array_find_first_set(ba, 0) == UINT32_MAX); + + /* Set bit 0 */ + CU_ASSERT(spdk_bit_array_set(ba, 0) == 0); + CU_ASSERT(spdk_bit_array_get(ba, 0) == true); + CU_ASSERT(spdk_bit_array_find_first_set(ba, 0) == 0); + + /* Clear bit 0 */ + spdk_bit_array_clear(ba, 0); + CU_ASSERT(spdk_bit_array_get(ba, 0) == false); + CU_ASSERT(spdk_bit_array_find_first_set(ba, 0) == UINT32_MAX); + + spdk_bit_array_free(&ba); + CU_ASSERT(ba == NULL); +} + +static void +test_64bit(void) +{ + struct spdk_bit_array *ba; + + ba = spdk_bit_array_create(64); + SPDK_CU_ASSERT_FATAL(ba != NULL); + CU_ASSERT(spdk_bit_array_get(ba, 0) == false); + CU_ASSERT(spdk_bit_array_get(ba, 63) == false); + CU_ASSERT(spdk_bit_array_get(ba, 64) == false); + CU_ASSERT(spdk_bit_array_get(ba, 1000) == false); + CU_ASSERT(spdk_bit_array_find_first_set(ba, 0) == UINT32_MAX); + + /* Set bit 1 */ + CU_ASSERT(spdk_bit_array_set(ba, 1) == 0); + CU_ASSERT(spdk_bit_array_get(ba, 0) == false); + CU_ASSERT(spdk_bit_array_get(ba, 1) == true); + CU_ASSERT(spdk_bit_array_find_first_set(ba, 0) == 1); + + /* Set bit 63 (1 still set) */ + CU_ASSERT(spdk_bit_array_set(ba, 63) == 0); + CU_ASSERT(spdk_bit_array_get(ba, 0) == false); + CU_ASSERT(spdk_bit_array_get(ba, 1) == true); + CU_ASSERT(spdk_bit_array_get(ba, 63) == true); + CU_ASSERT(spdk_bit_array_find_first_set(ba, 0) == 1); + + /* Clear bit 1 (63 still set) */ + spdk_bit_array_clear(ba, 1); + CU_ASSERT(spdk_bit_array_get(ba, 1) == false); + CU_ASSERT(spdk_bit_array_find_first_set(ba, 0) == 63); + + /* Clear bit 63 (no bits set) */ + spdk_bit_array_clear(ba, 63); + CU_ASSERT(spdk_bit_array_get(ba, 63) == false); + CU_ASSERT(spdk_bit_array_find_first_set(ba, 0) == UINT32_MAX); + + spdk_bit_array_free(&ba); +} + +static void +test_find(void) +{ + struct spdk_bit_array *ba; + uint32_t i; + + ba = spdk_bit_array_create(256); + SPDK_CU_ASSERT_FATAL(ba != NULL); + + /* Set all bits */ + for (i = 0; i < 256; i++) { + CU_ASSERT(spdk_bit_array_set(ba, i) == 0); + } + + /* Verify that find_first_set and find_first_clear work for each starting position */ + for (i = 0; i < 256; i++) { + CU_ASSERT(spdk_bit_array_find_first_set(ba, i) == i); + CU_ASSERT(spdk_bit_array_find_first_clear(ba, i) == 256); + } + CU_ASSERT(spdk_bit_array_find_first_set(ba, 256) == UINT32_MAX); + CU_ASSERT(spdk_bit_array_find_first_clear(ba, 256) == 256); + + /* Clear bits 0 through 31 */ + for (i = 0; i < 32; i++) { + spdk_bit_array_clear(ba, i); + } + + for (i = 0; i < 32; i++) { + CU_ASSERT(spdk_bit_array_find_first_set(ba, i) == 32); + CU_ASSERT(spdk_bit_array_find_first_clear(ba, i) == i); + } + + for (i = 32; i < 256; i++) { + CU_ASSERT(spdk_bit_array_find_first_set(ba, i) == i); + CU_ASSERT(spdk_bit_array_find_first_clear(ba, i) == 256); + } + + /* Clear bit 255 */ + spdk_bit_array_clear(ba, 255); + + for (i = 0; i < 32; i++) { + CU_ASSERT(spdk_bit_array_find_first_set(ba, i) == 32); + CU_ASSERT(spdk_bit_array_find_first_clear(ba, i) == i); + } + + for (i = 32; i < 255; i++) { + CU_ASSERT(spdk_bit_array_find_first_set(ba, i) == i); + CU_ASSERT(spdk_bit_array_find_first_clear(ba, i) == 255); + } + + CU_ASSERT(spdk_bit_array_find_first_clear(ba, 256) == 256); + + spdk_bit_array_free(&ba); +} + +static void +test_resize(void) +{ + struct spdk_bit_array *ba; + + /* Start with a 0 bit array */ + ba = spdk_bit_array_create(0); + SPDK_CU_ASSERT_FATAL(ba != NULL); + CU_ASSERT(spdk_bit_array_get(ba, 0) == false); + CU_ASSERT(spdk_bit_array_set(ba, 0) == -EINVAL); + spdk_bit_array_clear(ba, 0); + + /* Increase size to 1 bit */ + SPDK_CU_ASSERT_FATAL(spdk_bit_array_resize(&ba, 1) == 0); + SPDK_CU_ASSERT_FATAL(ba != NULL); + CU_ASSERT(spdk_bit_array_get(ba, 0) == false); + CU_ASSERT(spdk_bit_array_set(ba, 0) == 0); + CU_ASSERT(spdk_bit_array_get(ba, 0) == true); + + /* Increase size to 2 bits */ + SPDK_CU_ASSERT_FATAL(spdk_bit_array_resize(&ba, 2) == 0); + SPDK_CU_ASSERT_FATAL(ba != NULL); + CU_ASSERT(spdk_bit_array_get(ba, 1) == false); + CU_ASSERT(spdk_bit_array_set(ba, 1) == 0); + CU_ASSERT(spdk_bit_array_get(ba, 1) == true); + + /* Shrink size back to 1 bit */ + SPDK_CU_ASSERT_FATAL(spdk_bit_array_resize(&ba, 1) == 0); + SPDK_CU_ASSERT_FATAL(ba != NULL); + CU_ASSERT(spdk_bit_array_get(ba, 0) == true); + CU_ASSERT(spdk_bit_array_get(ba, 1) == false); + + /* Increase size to 65 bits */ + SPDK_CU_ASSERT_FATAL(spdk_bit_array_resize(&ba, 65) == 0); + SPDK_CU_ASSERT_FATAL(ba != NULL); + CU_ASSERT(spdk_bit_array_get(ba, 0) == true); + CU_ASSERT(spdk_bit_array_get(ba, 1) == false); + CU_ASSERT(spdk_bit_array_set(ba, 64) == 0); + CU_ASSERT(spdk_bit_array_get(ba, 64) == true); + + /* Shrink size back to 0 bits */ + SPDK_CU_ASSERT_FATAL(spdk_bit_array_resize(&ba, 0) == 0); + SPDK_CU_ASSERT_FATAL(ba != NULL); + CU_ASSERT(spdk_bit_array_get(ba, 0) == false); + CU_ASSERT(spdk_bit_array_get(ba, 1) == false); + + spdk_bit_array_free(&ba); +} + +int +main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + if (CU_initialize_registry() != CUE_SUCCESS) { + return CU_get_error(); + } + + suite = CU_add_suite("bit_array", NULL, NULL); + if (suite == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ( + CU_add_test(suite, "test_1bit", test_1bit) == NULL || + CU_add_test(suite, "test_64bit", test_64bit) == NULL || + CU_add_test(suite, "test_find", test_find) == NULL || + CU_add_test(suite, "test_resize", test_resize) == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + + CU_basic_run_tests(); + + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + + return num_failures; +} diff --git a/test/lib/util/util.sh b/test/lib/util/util.sh new file mode 100755 index 000000000..22d5731d1 --- /dev/null +++ b/test/lib/util/util.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -xe + +testdir=$(readlink -f $(dirname $0)) +rootdir=$testdir/../../.. +source $rootdir/scripts/autotest_common.sh + +timing_enter util + +$testdir/bit_array/bit_array_ut + +timing_exit util diff --git a/unittest.sh b/unittest.sh index 5f41b66d9..2c3041954 100755 --- a/unittest.sh +++ b/unittest.sh @@ -47,3 +47,7 @@ make -C test/lib/scsi/lun CONFIG_WERROR=y test/lib/scsi/dev/dev_ut test/lib/scsi/lun/lun_ut #test/lib/scsi/scsi_bdev/scsi_bdev_ut + +make -C test/lib/util/bit_array CONFIG_WERROR=y + +test/lib/util/bit_array/bit_array_ut