diff --git a/src/utils/Makefile b/src/utils/Makefile index 0f1f19109..940b4d8f1 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -14,6 +14,7 @@ CFLAGS += -DCONFIG_IPV6 LIB_OBJS= \ base64.o \ + bitfield.o \ common.o \ ip_addr.o \ radiotap.o \ diff --git a/src/utils/bitfield.c b/src/utils/bitfield.c new file mode 100644 index 000000000..f90e4beb6 --- /dev/null +++ b/src/utils/bitfield.c @@ -0,0 +1,89 @@ +/* + * Bitfield + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "bitfield.h" + + +struct bitfield { + u8 *bits; + size_t max_bits; +}; + + +struct bitfield * bitfield_alloc(size_t max_bits) +{ + struct bitfield *bf; + + bf = os_zalloc(sizeof(*bf) + (max_bits + 7) / 8); + if (bf == NULL) + return NULL; + bf->bits = (u8 *) (bf + 1); + bf->max_bits = max_bits; + return bf; +} + + +void bitfield_free(struct bitfield *bf) +{ + os_free(bf); +} + + +void bitfield_set(struct bitfield *bf, size_t bit) +{ + if (bit >= bf->max_bits) + return; + bf->bits[bit / 8] |= BIT(bit % 8); +} + + +void bitfield_clear(struct bitfield *bf, size_t bit) +{ + if (bit >= bf->max_bits) + return; + bf->bits[bit / 8] &= ~BIT(bit % 8); +} + + +int bitfield_is_set(struct bitfield *bf, size_t bit) +{ + if (bit >= bf->max_bits) + return 0; + return !!(bf->bits[bit / 8] & BIT(bit % 8)); +} + + +static int first_zero(u8 val) +{ + int i; + for (i = 0; i < 8; i++) { + if (!(val & 0x01)) + return i; + val >>= 1; + } + return -1; +} + + +int bitfield_get_first_zero(struct bitfield *bf) +{ + size_t i; + for (i = 0; i <= (bf->max_bits + 7) / 8; i++) { + if (bf->bits[i] != 0xff) + break; + } + if (i > (bf->max_bits + 7) / 8) + return -1; + i = i * 8 + first_zero(bf->bits[i]); + if (i >= bf->max_bits) + return -1; + return i; +} diff --git a/src/utils/bitfield.h b/src/utils/bitfield.h new file mode 100644 index 000000000..7050a208c --- /dev/null +++ b/src/utils/bitfield.h @@ -0,0 +1,21 @@ +/* + * Bitfield + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BITFIELD_H +#define BITFIELD_H + +struct bitfield; + +struct bitfield * bitfield_alloc(size_t max_bits); +void bitfield_free(struct bitfield *bf); +void bitfield_set(struct bitfield *bf, size_t bit); +void bitfield_clear(struct bitfield *bf, size_t bit); +int bitfield_is_set(struct bitfield *bf, size_t bit); +int bitfield_get_first_zero(struct bitfield *bf); + +#endif /* BITFIELD_H */ diff --git a/tests/Makefile b/tests/Makefile index cccc3dcb0..80341a111 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,4 +1,5 @@ TESTS=test-base64 test-md4 test-md5 test-milenage test-ms_funcs \ + test-bitfield \ test-printf \ test-sha1 \ test-sha256 test-aes test-asn1 test-x509 test-x509v3 test-list test-rc4 @@ -47,6 +48,9 @@ test-asn1: test-asn1.o $(LIBS) test-base64: test-base64.o $(LIBS) $(LDO) $(LDFLAGS) -o $@ $^ +test-bitfield: test-bitfield.o $(LIBS) + $(LDO) $(LDFLAGS) -o $@ $^ + test-https: test-https.o $(LIBS) $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS) @@ -86,6 +90,7 @@ test-x509v3: test-x509v3.o $(LIBS) run-tests: $(TESTS) ./test-aes + ./test-bitfield ./test-list ./test-md4 ./test-md5 diff --git a/tests/test-bitfield.c b/tests/test-bitfield.c new file mode 100644 index 000000000..aff1790c0 --- /dev/null +++ b/tests/test-bitfield.c @@ -0,0 +1,90 @@ +/* + * bitfield unit tests + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include "utils/bitfield.h" + +int main(int argc, char *argv[]) +{ + struct bitfield *bf; + int i; + int errors = 0; + + bf = bitfield_alloc(123); + if (bf == NULL) + return -1; + + for (i = 0; i < 123; i++) { + if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1)) + errors++; + if (i > 0 && bitfield_is_set(bf, i - 1)) + errors++; + bitfield_set(bf, i); + if (!bitfield_is_set(bf, i)) + errors++; + bitfield_clear(bf, i); + if (bitfield_is_set(bf, i)) + errors++; + } + + for (i = 123; i < 200; i++) { + if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1)) + errors++; + if (i > 0 && bitfield_is_set(bf, i - 1)) + errors++; + bitfield_set(bf, i); + if (bitfield_is_set(bf, i)) + errors++; + bitfield_clear(bf, i); + if (bitfield_is_set(bf, i)) + errors++; + } + + for (i = 0; i < 123; i++) { + if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1)) + errors++; + bitfield_set(bf, i); + if (!bitfield_is_set(bf, i)) + errors++; + } + + for (i = 0; i < 123; i++) { + if (!bitfield_is_set(bf, i)) + errors++; + bitfield_clear(bf, i); + if (bitfield_is_set(bf, i)) + errors++; + } + + for (i = 0; i < 123; i++) { + if (bitfield_get_first_zero(bf) != i) + errors++; + bitfield_set(bf, i); + } + if (bitfield_get_first_zero(bf) != -1) + errors++; + for (i = 0; i < 123; i++) { + if (!bitfield_is_set(bf, i)) + errors++; + bitfield_clear(bf, i); + if (bitfield_get_first_zero(bf) != i) + errors++; + bitfield_set(bf, i); + } + if (bitfield_get_first_zero(bf) != -1) + errors++; + + bitfield_free(bf); + + if (errors) { + printf("%d test(s) failed\n", errors); + return -1; + } + + return 0; +}