diff --git a/src/cmc/bitset.h b/src/cmc/bitset.h new file mode 100644 index 0000000..c63b130 --- /dev/null +++ b/src/cmc/bitset.h @@ -0,0 +1,412 @@ +/** + * bitset.h + * + * Creation Date: 30/04/2020 + * + * Authors: + * Leonardo Vencovsky (https://github.com/LeoVen) + * + */ + +/** + * BitSet + * + * A BitSet is a dynamic array that stores bits. Each bit can be individually + * handled and perform all CRUD operations. + */ + +#ifndef CMC_BITSET_H +#define CMC_BITSET_H + +/* ------------------------------------------------------------------------- + * Core functionalities of the C Macro Collections Library + * ------------------------------------------------------------------------- */ +#include "../cor/core.h" + +/* ------------------------------------------------------------------------- + * BitSet specific + * ------------------------------------------------------------------------- */ +/* Defines the underlaying data type for the bitset array */ +/* Possible values: uint64_t, uint32_t, uint16_t, uint8_t, size_t */ +typedef uint32_t cmc_bitset_word; + +/* to_string format */ +static const char *cmc_string_fmt_bitset = "struct %s<%s> " + "at %p { " + "buffer:%p, " + "capacity:%" PRIuMAX ", " + "flag:%d, " + "alloc:%p, " + "callbacks:%p }"; + +#define CMC_GENERATE_BITSET(PFX, SNAME) \ + CMC_GENERATE_BITSET_HEADER(PFX, SNAME) \ + CMC_GENERATE_BITSET_SOURCE(PFX, SNAME) + +#define CMC_WRAPGEN_BITSET_HEADER(PFX, SNAME, K, V) \ + CMC_GENERATE_BITSET_HEADER(PFX, SNAME) + +#define CMC_WRAPGEN_BITSET_SOURCE(PFX, SNAME, K, V) \ + CMC_GENERATE_BITSET_SOURCE(PFX, SNAME) + +/* ------------------------------------------------------------------------- + * Header + * ------------------------------------------------------------------------- */ +#define CMC_GENERATE_BITSET_HEADER(PFX, SNAME) \ + \ + /* BitSet Structure */ \ + struct SNAME \ + { \ + /* Dynamic Array of Bits */ \ + cmc_bitset_word *buffer; \ + \ + /* Current array capacity */ \ + size_t capacity; \ + \ + /* Flags indicating errors or success */ \ + int flag; \ + \ + /* Custom allocation functions */ \ + struct cmc_alloc_node *alloc; \ + \ + /* Custom callback functions */ \ + struct cmc_callbacks *callbacks; \ + }; \ + \ + /* BitSet Iterator */ \ + struct SNAME##_iter \ + { \ + /* Target bitset */ \ + struct SNAME *target; \ + \ + /* Cursor's position (index) */ \ + cmc_bitset_word cursor; \ + \ + /* If the iterator has reached the start of the iteration */ \ + bool start; \ + \ + /* If the iterator has reached the end of the iteration */ \ + bool end; \ + }; \ + \ + /* Collection Functions */ \ + /* Collection Allocation and Deallocation */ \ + struct SNAME *PFX##_new(size_t n_bits); \ + struct SNAME *PFX##_new_custom(size_t n_bits, \ + struct cmc_alloc_node *alloc, \ + struct cmc_callbacks *callbacks); \ + struct SNAME PFX##_init(size_t n_bits); \ + struct SNAME PFX##_init_custom(size_t n_bits, \ + struct cmc_alloc_node *alloc, \ + struct cmc_callbacks *callbacks); \ + void PFX##_free(struct SNAME *_bitset_); \ + void PFX##_release(struct SNAME _bitset_); \ + void PFX##_customize(struct SNAME *_bitset_, struct cmc_alloc_node *alloc, \ + struct cmc_callbacks *callbacks); \ + /* Collection Input and Output */ \ + bool PFX##_set(struct SNAME *_bitset_, size_t bit_index); \ + bool PFX##_set_range(struct SNAME *_bitset_, size_t from, size_t to); \ + bool PFX##_clear(struct SNAME *_bitset_, size_t bit_index); \ + bool PFX##_clear_range(struct SNAME *_bitset_, size_t from, size_t to); \ + bool PFX##_flip(struct SNAME *_bitset_, size_t bit_index); \ + bool PFX##_flip_range(struct SNAME *_bitset_, size_t from, size_t to); \ + bool PFX##_put(struct SNAME *_bitset_, size_t bit_index); \ + bool PFX##_put_range(struct SNAME *_bitset_, size_t from, size_t to); \ + bool PFX##_fill(struct SNAME *_bitset_); \ + bool PFX##_empty(struct SNAME *_bitset_); \ + /* Element Access */ \ + bool PFX##_get(struct SNAME *_bitset_, size_t bit_index); \ + \ + /* Collection State */ \ + \ + /* Collection Utility */ \ + bool PFX##_resize(struct SNAME *_bitset_, size_t n_bits); \ + \ + /* Utility Methods */ \ + /* Translates a bit index to a word index */ \ + static inline cmc_bitset_word PFX##_bit_to_index(cmc_bitset_word idx) \ + { \ + /* Calculate how many shifts based on the size of cmc_bitset_word */ \ + static const cmc_bitset_word shift = \ + ((sizeof(cmc_bitset_word) * 8) >> 6) > 0 \ + ? 6 \ + : ((sizeof(cmc_bitset_word) * 8) >> 5) > 0 \ + ? 5 \ + : ((sizeof(cmc_bitset_word) * 8) >> 4) > 0 ? 4 : 3; \ + \ + return idx >> shift; \ + } + +/* ------------------------------------------------------------------------- + * Source + * ------------------------------------------------------------------------- */ +#define CMC_GENERATE_BITSET_SOURCE(PFX, SNAME) \ + \ + /* Implementation Detail Functions */ \ + bool PFX##_impl_resize(struct SNAME *_bitset_, size_t n_bits, \ + bool do_resize); \ + \ + struct SNAME *PFX##_new(size_t n_bits) \ + { \ + return PFX##_new_custom(n_bits, NULL, NULL); \ + } \ + \ + struct SNAME *PFX##_new_custom(size_t n_bits, \ + struct cmc_alloc_node *alloc, \ + struct cmc_callbacks *callbacks) \ + { \ + if (n_bits < 1) \ + return NULL; \ + \ + if (!alloc) \ + alloc = &cmc_alloc_node_default; \ + \ + size_t capacity = PFX##_bit_to_index(n_bits - 1) + 1; \ + \ + struct SNAME *_bitset_ = alloc->malloc(sizeof(struct SNAME)); \ + \ + if (!_bitset_) \ + return NULL; \ + \ + _bitset_->buffer = alloc->calloc(capacity, sizeof(cmc_bitset_word)); \ + \ + if (!_bitset_->buffer) \ + { \ + alloc->free(_bitset_); \ + return NULL; \ + } \ + \ + _bitset_->capacity = capacity; \ + _bitset_->flag = cmc_flags.OK; \ + _bitset_->alloc = alloc; \ + _bitset_->callbacks = callbacks; \ + \ + return _bitset_; \ + } \ + \ + struct SNAME PFX##_init(size_t n_bits) \ + { \ + return PFX##_init_custom(n_bits, NULL, NULL); \ + } \ + \ + struct SNAME PFX##_init_custom(size_t n_bits, \ + struct cmc_alloc_node *alloc, \ + struct cmc_callbacks *callbacks) \ + { \ + struct SNAME _bitset_ = { 0 }; \ + \ + if (n_bits < 1) \ + return _bitset_; \ + \ + if (!alloc) \ + alloc = &cmc_alloc_node_default; \ + \ + size_t capacity = PFX##_bit_to_index(n_bits - 1) + 1; \ + \ + _bitset_.buffer = alloc->calloc(capacity, sizeof(cmc_bitset_word)); \ + \ + if (!_bitset_.buffer) \ + return _bitset_; \ + \ + _bitset_.capacity = capacity; \ + _bitset_.flag = cmc_flags.OK; \ + _bitset_.alloc = alloc; \ + _bitset_.callbacks = callbacks; \ + \ + return _bitset_; \ + } \ + \ + void PFX##_free(struct SNAME *_bitset_) \ + { \ + free(_bitset_->buffer); \ + free(_bitset_); \ + } \ + \ + void PFX##_release(struct SNAME _bitset_) \ + { \ + free(_bitset_.buffer); \ + } \ + \ + void PFX##_customize(struct SNAME *_bitset_, struct cmc_alloc_node *alloc, \ + struct cmc_callbacks *callbacks) \ + { \ + if (!alloc) \ + _bitset_->alloc = &cmc_alloc_node_default; \ + else \ + _bitset_->alloc = alloc; \ + \ + _bitset_->callbacks = callbacks; \ + \ + _bitset_->flag = cmc_flags.OK; \ + } \ + \ + bool PFX##_set(struct SNAME *_bitset_, size_t bit_index) \ + { \ + if (!PFX##_impl_resize(_bitset_, bit_index + 1, false)) \ + return false; \ + \ + /* How many bits in a word */ \ + const cmc_bitset_word bits = sizeof(cmc_bitset_word) * CHAR_BIT; \ + \ + size_t i = PFX##_bit_to_index(bit_index); \ + \ + _bitset_->buffer[i] |= ((cmc_bitset_word)1) << (bit_index % bits); \ + \ + _bitset_->flag = cmc_flags.OK; \ + \ + if (_bitset_->callbacks && _bitset_->callbacks->create) \ + _bitset_->callbacks->create(); \ + \ + return true; \ + } \ + \ + bool PFX##_set_range(struct SNAME *_bitset_, size_t from, size_t to) \ + { \ + if (to < from) \ + { \ + _bitset_->flag = cmc_flags.INVALID; \ + return false; \ + } \ + \ + if (!PFX##_impl_resize(_bitset_, to + 1, false)) \ + return false; \ + \ + size_t start_index = PFX##_bit_to_index(from); \ + size_t end_index = PFX##_bit_to_index(to); \ + \ + /* How many bits in a word */ \ + const cmc_bitset_word bits = sizeof(cmc_bitset_word) * CHAR_BIT; \ + \ + /* 1111 ... 1111 */ \ + const cmc_bitset_word ones = ~((cmc_bitset_word)0); \ + \ + cmc_bitset_word shift_start = from % bits; \ + cmc_bitset_word shift_end = (cmc_bitset_word)(-(to + 1)) % bits; \ + \ + cmc_bitset_word start_mask = ones << shift_start; \ + cmc_bitset_word end_mask = ones >> shift_end; \ + \ + if (start_index == end_index) \ + { \ + _bitset_->buffer[end_index] |= (start_mask & end_mask); \ + } \ + else \ + { \ + _bitset_->buffer[start_index] |= start_mask; \ + \ + for (size_t i = start_index + 1; i < end_index; i++) \ + _bitset_->buffer[i] |= ~((cmc_bitset_word)0); \ + \ + _bitset_->buffer[end_index] |= end_mask; \ + } \ + \ + _bitset_->flag = cmc_flags.OK; \ + \ + if (_bitset_->callbacks && _bitset_->callbacks->create) \ + _bitset_->callbacks->create(); \ + \ + return true; \ + } \ + \ + bool PFX##_clear(struct SNAME *_bitset_, size_t bit_index) \ + { \ + return true; \ + } \ + \ + bool PFX##_clear_range(struct SNAME *_bitset_, size_t from, size_t to) \ + { \ + return true; \ + } \ + \ + bool PFX##_flip(struct SNAME *_bitset_, size_t bit_index) \ + { \ + return true; \ + } \ + \ + bool PFX##_flip_range(struct SNAME *_bitset_, size_t from, size_t to) \ + { \ + return true; \ + } \ + \ + bool PFX##_put(struct SNAME *_bitset_, size_t bit_index) \ + { \ + return true; \ + } \ + \ + bool PFX##_put_range(struct SNAME *_bitset_, size_t from, size_t to) \ + { \ + return true; \ + } \ + \ + bool PFX##_fill(struct SNAME *_bitset_) \ + { \ + return true; \ + } \ + \ + bool PFX##_empty(struct SNAME *_bitset_) \ + { \ + return true; \ + } \ + \ + bool PFX##_get(struct SNAME *_bitset_, size_t bit_index) \ + { \ + /* How many bits in a word */ \ + const cmc_bitset_word bits = sizeof(cmc_bitset_word) * CHAR_BIT; \ + \ + cmc_bitset_word w = _bitset_->buffer[PFX##_bit_to_index(bit_index)]; \ + \ + return w & ((cmc_bitset_word)1 << (bit_index % bits)); \ + } \ + \ + bool PFX##_resize(struct SNAME *_bitset_, size_t n_bits) \ + { \ + return PFX##_impl_resize(_bitset_, n_bits, true); \ + } \ + \ + /* When do_resize is false, then we just want to assure that the bitset */ \ + /* can accommodate n_bits */ \ + bool PFX##_impl_resize(struct SNAME *_bitset_, size_t n_bits, \ + bool do_resize) \ + { \ + if (n_bits == 0) \ + { \ + _bitset_->flag = cmc_flags.INVALID; \ + return false; \ + } \ + \ + /* -1 because n_bits is 1-based and we need to pass a 0-based index */ \ + /* +1 to have a 1-based result */ \ + size_t words = PFX##_bit_to_index(n_bits - 1) + 1; \ + \ + /* Be sure we are not always shrinking when we just want to make */ \ + /* sure that we have enough size for n_bits */ \ + if (!do_resize && words <= _bitset_->capacity) \ + return true; \ + \ + cmc_bitset_word *new_buffer = \ + realloc(_bitset_->buffer, words * sizeof(cmc_bitset_word)); \ + \ + if (!new_buffer) \ + { \ + _bitset_->flag = cmc_flags.ALLOC; \ + return false; \ + } \ + \ + _bitset_->buffer = new_buffer; \ + \ + /* Make new bits be all zeroes */ \ + if (_bitset_->capacity < words) \ + memset(_bitset_->buffer + _bitset_->capacity, 0, \ + (words - _bitset_->capacity) * sizeof(cmc_bitset_word)); \ + \ + _bitset_->capacity = words; \ + \ + if (_bitset_->callbacks && _bitset_->callbacks->resize) \ + _bitset_->callbacks->resize(); \ + \ + _bitset_->flag = cmc_flags.OK; \ + \ + return true; \ + } + +#endif /* CMC_BITSET_H */ diff --git a/tests/main/main.c b/tests/main/main.c index 08f149c..c64cfe8 100644 --- a/tests/main/main.c +++ b/tests/main/main.c @@ -1,6 +1,7 @@ #include "utl/timer.h" #include "unt/bidimap.c" +#include "unt/bitset.c" #include "unt/deque.c" #include "unt/hashmap.c" #include "unt/hashset.c" @@ -47,6 +48,8 @@ int main(void) uintmax_t tests = 0, units = 0; cmc_run(BidiMap, units, tests); + cmc_run(BitSet, units, tests); + cmc_run(BitSetIter, units, tests); cmc_run(Deque, units, tests); cmc_run(DequeIter, units, tests); cmc_run(HashMap, units, tests); diff --git a/tests/main/src/bitset.c b/tests/main/src/bitset.c new file mode 100644 index 0000000..61d4b8f --- /dev/null +++ b/tests/main/src/bitset.c @@ -0,0 +1,232 @@ +#include "cmc/bitset.h" + +struct bitset +{ + cmc_bitset_word *buffer; + size_t capacity; + int flag; + struct cmc_alloc_node *alloc; + struct cmc_callbacks *callbacks; +}; +struct bitset_iter +{ + struct bitset *target; + cmc_bitset_word cursor; + _Bool start; + _Bool end; +}; +struct bitset *bs_new(size_t n_bits); +struct bitset *bs_new_custom(size_t n_bits, struct cmc_alloc_node *alloc, + struct cmc_callbacks *callbacks); +struct bitset bs_init(size_t n_bits); +struct bitset bs_init_custom(size_t n_bits, struct cmc_alloc_node *alloc, + struct cmc_callbacks *callbacks); +void bs_free(struct bitset *_bitset_); +void bs_release(struct bitset _bitset_); +void bs_customize(struct bitset *_bitset_, struct cmc_alloc_node *alloc, + struct cmc_callbacks *callbacks); +_Bool bs_set(struct bitset *_bitset_, size_t bit_index); +_Bool bs_set_range(struct bitset *_bitset_, size_t from, size_t to); +_Bool bs_clear(struct bitset *_bitset_, size_t bit_index); +_Bool bs_clear_range(struct bitset *_bitset_, size_t from, size_t to); +_Bool bs_flip(struct bitset *_bitset_, size_t bit_index); +_Bool bs_flip_range(struct bitset *_bitset_, size_t from, size_t to); +_Bool bs_put(struct bitset *_bitset_, size_t bit_index); +_Bool bs_put_range(struct bitset *_bitset_, size_t from, size_t to); +_Bool bs_fill(struct bitset *_bitset_); +_Bool bs_empty(struct bitset *_bitset_); +_Bool bs_get(struct bitset *_bitset_, size_t bit_index); +_Bool bs_resize(struct bitset *_bitset_, size_t n_bits); +static inline cmc_bitset_word bs_bit_to_index(cmc_bitset_word idx) +{ + static const cmc_bitset_word shift = + ((sizeof(cmc_bitset_word) * 8) >> 6) > 0 + ? 6 + : ((sizeof(cmc_bitset_word) * 8) >> 5) > 0 + ? 5 + : ((sizeof(cmc_bitset_word) * 8) >> 4) > 0 ? 4 : 3; + return idx >> shift; +} +_Bool bs_impl_resize(struct bitset *_bitset_, size_t n_bits, _Bool do_resize); +struct bitset *bs_new(size_t n_bits) +{ + return bs_new_custom(n_bits, ((void *)0), ((void *)0)); +} +struct bitset *bs_new_custom(size_t n_bits, struct cmc_alloc_node *alloc, + struct cmc_callbacks *callbacks) +{ + if (n_bits < 1) + return ((void *)0); + if (!alloc) + alloc = &cmc_alloc_node_default; + size_t capacity = bs_bit_to_index(n_bits - 1) + 1; + struct bitset *_bitset_ = alloc->malloc(sizeof(struct bitset)); + if (!_bitset_) + return ((void *)0); + _bitset_->buffer = alloc->calloc(capacity, sizeof(cmc_bitset_word)); + if (!_bitset_->buffer) + { + alloc->free(_bitset_); + return ((void *)0); + } + _bitset_->capacity = capacity; + _bitset_->flag = cmc_flags.OK; + _bitset_->alloc = alloc; + _bitset_->callbacks = callbacks; + return _bitset_; +} +struct bitset bs_init(size_t n_bits) +{ + return bs_init_custom(n_bits, ((void *)0), ((void *)0)); +} +struct bitset bs_init_custom(size_t n_bits, struct cmc_alloc_node *alloc, + struct cmc_callbacks *callbacks) +{ + struct bitset _bitset_ = { 0 }; + if (n_bits < 1) + return _bitset_; + if (!alloc) + alloc = &cmc_alloc_node_default; + size_t capacity = bs_bit_to_index(n_bits - 1) + 1; + _bitset_.buffer = alloc->calloc(capacity, sizeof(cmc_bitset_word)); + if (!_bitset_.buffer) + return _bitset_; + _bitset_.capacity = capacity; + _bitset_.flag = cmc_flags.OK; + _bitset_.alloc = alloc; + _bitset_.callbacks = callbacks; + return _bitset_; +} +void bs_free(struct bitset *_bitset_) +{ + free(_bitset_->buffer); + free(_bitset_); +} +void bs_release(struct bitset _bitset_) +{ + free(_bitset_.buffer); +} +void bs_customize(struct bitset *_bitset_, struct cmc_alloc_node *alloc, + struct cmc_callbacks *callbacks) +{ + if (!alloc) + _bitset_->alloc = &cmc_alloc_node_default; + else + _bitset_->alloc = alloc; + _bitset_->callbacks = callbacks; + _bitset_->flag = cmc_flags.OK; +} +_Bool bs_set(struct bitset *_bitset_, size_t bit_index) +{ + if (!bs_impl_resize(_bitset_, bit_index + 1, 0)) + return 0; + const cmc_bitset_word bits = sizeof(cmc_bitset_word) * 8; + size_t i = bs_bit_to_index(bit_index); + _bitset_->buffer[i] |= ((cmc_bitset_word)1) << (bit_index % bits); + _bitset_->flag = cmc_flags.OK; + if (_bitset_->callbacks && _bitset_->callbacks->create) + _bitset_->callbacks->create(); + return 1; +} +_Bool bs_set_range(struct bitset *_bitset_, size_t from, size_t to) +{ + if (to < from) + { + _bitset_->flag = cmc_flags.INVALID; + return 0; + } + if (!bs_impl_resize(_bitset_, to + 1, 0)) + return 0; + size_t start_index = bs_bit_to_index(from); + size_t end_index = bs_bit_to_index(to); + const cmc_bitset_word bits = sizeof(cmc_bitset_word) * 8; + const cmc_bitset_word ones = ~((cmc_bitset_word)0); + cmc_bitset_word shift_start = from % bits; + cmc_bitset_word shift_end = (cmc_bitset_word)(-(to + 1)) % bits; + cmc_bitset_word start_mask = ones << shift_start; + cmc_bitset_word end_mask = ones >> shift_end; + if (start_index == end_index) + { + _bitset_->buffer[end_index] |= (start_mask & end_mask); + } + else + { + _bitset_->buffer[start_index] |= start_mask; + for (size_t i = start_index + 1; i < end_index; i++) + _bitset_->buffer[i] |= ~((cmc_bitset_word)0); + _bitset_->buffer[end_index] |= end_mask; + } + _bitset_->flag = cmc_flags.OK; + if (_bitset_->callbacks && _bitset_->callbacks->create) + _bitset_->callbacks->create(); + return 1; +} +_Bool bs_clear(struct bitset *_bitset_, size_t bit_index) +{ + return 1; +} +_Bool bs_clear_range(struct bitset *_bitset_, size_t from, size_t to) +{ + return 1; +} +_Bool bs_flip(struct bitset *_bitset_, size_t bit_index) +{ + return 1; +} +_Bool bs_flip_range(struct bitset *_bitset_, size_t from, size_t to) +{ + return 1; +} +_Bool bs_put(struct bitset *_bitset_, size_t bit_index) +{ + return 1; +} +_Bool bs_put_range(struct bitset *_bitset_, size_t from, size_t to) +{ + return 1; +} +_Bool bs_fill(struct bitset *_bitset_) +{ + return 1; +} +_Bool bs_empty(struct bitset *_bitset_) +{ + return 1; +} +_Bool bs_get(struct bitset *_bitset_, size_t bit_index) +{ + const cmc_bitset_word bits = sizeof(cmc_bitset_word) * 8; + cmc_bitset_word w = _bitset_->buffer[bs_bit_to_index(bit_index)]; + return w & ((cmc_bitset_word)1 << (bit_index % bits)); +} +_Bool bs_resize(struct bitset *_bitset_, size_t n_bits) +{ + return bs_impl_resize(_bitset_, n_bits, 1); +} +_Bool bs_impl_resize(struct bitset *_bitset_, size_t n_bits, _Bool do_resize) +{ + if (n_bits == 0) + { + _bitset_->flag = cmc_flags.INVALID; + return 0; + } + size_t words = bs_bit_to_index(n_bits - 1) + 1; + if (!do_resize && words <= _bitset_->capacity) + return 1; + cmc_bitset_word *new_buffer = + realloc(_bitset_->buffer, words * sizeof(cmc_bitset_word)); + if (!new_buffer) + { + _bitset_->flag = cmc_flags.ALLOC; + return 0; + } + _bitset_->buffer = new_buffer; + if (_bitset_->capacity < words) + memset(_bitset_->buffer + _bitset_->capacity, 0, + (words - _bitset_->capacity) * sizeof(cmc_bitset_word)); + _bitset_->capacity = words; + if (_bitset_->callbacks && _bitset_->callbacks->resize) + _bitset_->callbacks->resize(); + _bitset_->flag = cmc_flags.OK; + return 1; +} diff --git a/tests/main/unt/bitset.c b/tests/main/unt/bitset.c new file mode 100644 index 0000000..1f4a517 --- /dev/null +++ b/tests/main/unt/bitset.c @@ -0,0 +1,222 @@ +#include "utl.c" +#include "utl/assert.h" +#include "utl/test.h" + +#include "../src/bitset.c" + +struct cmc_alloc_node *bs_alloc_node = &(struct cmc_alloc_node){ + .malloc = malloc, .calloc = calloc, .realloc = realloc, .free = free +}; + +CMC_CREATE_UNIT(BitSet, true, { + const size_t word_bytes = sizeof(cmc_bitset_word); + const size_t word_bits = sizeof(cmc_bitset_word) * CHAR_BIT; + + CMC_CREATE_TEST(PFX##_new(), { + struct bitset *bs = bs_new(64); + + cmc_assert_not_equals(ptr, NULL, bs); + cmc_assert_not_equals(ptr, NULL, bs->buffer); + cmc_assert_equals(size_t, bs_bit_to_index(63) + 1, bs->capacity); + cmc_assert_equals(ptr, &cmc_alloc_node_default, bs->alloc); + cmc_assert_equals(ptr, NULL, bs->callbacks); + + bs_free(bs); + + bs = bs_new(129); + + cmc_assert_equals(size_t, bs_bit_to_index(128) + 1, bs->capacity); + + bs_free(bs); + + bs = bs_new(0); + + cmc_assert_equals(ptr, NULL, bs); + }); + + CMC_CREATE_TEST(PFX##_new_custom(), { + struct bitset *bs = bs_new_custom(64, bs_alloc_node, callbacks); + + cmc_assert_not_equals(ptr, NULL, bs); + cmc_assert_not_equals(ptr, NULL, bs->buffer); + cmc_assert_equals(size_t, bs_bit_to_index(63) + 1, bs->capacity); + cmc_assert_equals(ptr, bs_alloc_node, bs->alloc); + cmc_assert_equals(ptr, callbacks, bs->callbacks); + + bs_free(bs); + + bs = bs_new_custom(129, NULL, NULL); + + cmc_assert_equals(size_t, bs_bit_to_index(128) + 1, bs->capacity); + bs_free(bs); + + bs = bs_new_custom(0, bs_alloc_node, callbacks); + + cmc_assert_equals(ptr, NULL, bs); + }); + + CMC_CREATE_TEST(PFX##_init(), { + struct bitset bs = bs_init(64); + + cmc_assert_not_equals(ptr, NULL, bs.buffer); + cmc_assert_equals(size_t, bs_bit_to_index(63) + 1, bs.capacity); + cmc_assert_equals(ptr, &cmc_alloc_node_default, bs.alloc); + cmc_assert_equals(ptr, NULL, bs.callbacks); + + bs_release(bs); + + bs = bs_init(129); + + cmc_assert_equals(size_t, bs_bit_to_index(128) + 1, bs.capacity); + + bs_release(bs); + + bs = bs_init(0); + + cmc_assert_equals(ptr, NULL, bs.buffer); + }); + + CMC_CREATE_TEST(PFX##_init_custom(), { + struct bitset bs = bs_init_custom(64, bs_alloc_node, callbacks); + + cmc_assert_not_equals(ptr, NULL, bs.buffer); + cmc_assert_equals(size_t, bs_bit_to_index(63) + 1, bs.capacity); + cmc_assert_equals(ptr, bs_alloc_node, bs.alloc); + cmc_assert_equals(ptr, callbacks, bs.callbacks); + + bs_release(bs); + + bs = bs_init_custom(129, NULL, NULL); + + cmc_assert_equals(size_t, bs_bit_to_index(128) + 1, bs.capacity); + + bs_release(bs); + + bs = bs_init_custom(0, bs_alloc_node, callbacks); + + cmc_assert_equals(ptr, NULL, bs.buffer); + }); + + CMC_CREATE_TEST(PFX##_customize(), { + struct bitset *bs = bs_new(word_bits); + + cmc_assert_not_equals(ptr, NULL, bs); + + bs->flag = cmc_flags.ERROR; + bs_customize(bs, bs_alloc_node, callbacks); + + cmc_assert_equals(ptr, bs_alloc_node, bs->alloc); + cmc_assert_equals(ptr, callbacks, bs->callbacks); + cmc_assert_equals(int32_t, cmc_flags.OK, bs->flag); + + bs->flag = cmc_flags.ERROR; + bs_customize(bs, NULL, NULL); + + cmc_assert_equals(ptr, &cmc_alloc_node_default, bs->alloc); + cmc_assert_equals(ptr, NULL, bs->callbacks); + cmc_assert_equals(int32_t, cmc_flags.OK, bs->flag); + + bs_free(bs); + }); + + CMC_CREATE_TEST(PFX##_set(), { + struct bitset *bs = bs_new(word_bits); + + cmc_assert_not_equals(ptr, NULL, bs); + cmc_assert(0 == bs->buffer[0]); + + bs->flag = cmc_flags.ERROR; + cmc_assert(bs_set(bs, 0)); + cmc_assert(1 == bs->buffer[0]); + cmc_assert_equals(int32_t, cmc_flags.OK, bs->flag); + + bs->buffer[0] = 0; + + cmc_assert(bs_set(bs, word_bits - 1)); + cmc_assert(((cmc_bitset_word)1 << (word_bits - 1)) == bs->buffer[0]); + + cmc_assert(bs_set(bs, word_bits * 8 - 1)); + cmc_assert(((cmc_bitset_word)1 << (word_bits - 1)) == + bs->buffer[bs_bit_to_index(word_bits * 8 - 1)]); + + cmc_assert(bs_set(bs, word_bits * 18 - 2)); + cmc_assert(((cmc_bitset_word)1 << (word_bits - 2)) == + bs->buffer[bs_bit_to_index(word_bits * 18 - 2)]); + + bs_free(bs); + }); + + CMC_CREATE_TEST(PFX##_set_range(), { + struct bitset *bs = bs_new(word_bits); + + cmc_assert_not_equals(ptr, NULL, bs); + cmc_assert(0 == bs->buffer[0]); + + bs->flag = cmc_flags.ERROR; + cmc_assert(bs_set_range(bs, 0, 0)); + cmc_assert_equals(uint8_t, 1, bs->buffer[0]); + cmc_assert_equals(int32_t, cmc_flags.OK, bs->flag); + + bs->buffer[0] = 0; + + cmc_assert(bs_set_range(bs, 4, 7)); /* 1111 0000 */ + cmc_assert_equals(uint8_t, 240, bs->buffer[0]); + + cmc_assert(bs_set_range(bs, 2, 2)); /* 1111 0100 */ + cmc_assert_equals(uint8_t, 244, bs->buffer[0]); + + cmc_assert(bs_set_range(bs, word_bits * 6 - 2, word_bits * 6 - 2)); + cmc_assert(((cmc_bitset_word)1 << (word_bits - 2)) == + bs->buffer[bs_bit_to_index(word_bits * 6 - 2)]); + + // Make sure we have at leats word_bits * 40 slots + bs_set(bs, word_bits * 40); + + cmc_assert(bs_set_range(bs, word_bits * 20, word_bits * 21 - 1)); + cmc_assert((~(cmc_bitset_word)0) == + bs->buffer[bs_bit_to_index(word_bits * 20)]); + + cmc_assert(0 == bs->buffer[bs_bit_to_index(word_bits * 20 - 1)]); + cmc_assert(0 == bs->buffer[bs_bit_to_index(word_bits * 21 + 1)]); + + bs_free(bs); + }); + + CMC_CREATE_TEST(PFX##_clear(), { + struct bitset *bs = bs_new(64); + + // WIP + + bs_free(bs); + }); + + CMC_CREATE_TEST(PFX##_get(), { + struct bitset *bs = bs_new(64); + + cmc_assert_not_equals(ptr, NULL, bs); + + cmc_assert(bs_set(bs, 1)); + + cmc_assert(bs_get(bs, 1)); + + bs_free(bs); + }); + + CMC_CREATE_TEST(PFX##_resize(), { + struct bitset *bs = bs_new(64); + + // WIP + + bs_free(bs); + }); +}); + +CMC_CREATE_UNIT(BitSetIter, true, { + CMC_CREATE_TEST(PFX##_iter_start(), { + struct bitset *bs = bs_new(64); + + // WIP + + bs_free(bs); + }); +});