Update list implementation to work with memory pools (#168)
* Introduce indexed list implementation * Fix CI tests for pool * Take bitset out of pool * Replace joker bitset interactions with wrappers * Add bitset tests * Add test to gitignore --------- Co-authored-by: rfehr-idexx <ric-fehr@idexx.com>
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
|
||||
CC := gcc
|
||||
CFLAGS := -I../../include -I. \
|
||||
-g -O3 -Wall -Werror
|
||||
SRC := bitset_test.c \
|
||||
../../source/bitset.c
|
||||
OUT := build/bitset_test
|
||||
|
||||
$(OUT): $(SRC) | build
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
build:
|
||||
mkdir -p build
|
||||
|
||||
clean:
|
||||
rm -f $(OUT)
|
||||
@@ -0,0 +1,122 @@
|
||||
#include "bitset.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
BITSET_DEFINE(test_bitset, BITSET_MAX_BITS)
|
||||
|
||||
// bitset_set_idx
|
||||
// bitset_get_idx
|
||||
// bitset_num_set_bits
|
||||
// bitset_is_empty
|
||||
// bitset_clear
|
||||
void test_bitset_fill_all_and_empty(void)
|
||||
{
|
||||
assert(bitset_is_empty(&test_bitset));
|
||||
|
||||
for(int i = 0; i < BITSET_MAX_BITS; i++)
|
||||
{
|
||||
bitset_set_idx(&test_bitset, i, true);
|
||||
assert(bitset_num_set_bits(&test_bitset) == (i + 1));
|
||||
}
|
||||
|
||||
for(int i = 0; i < BITSET_MAX_BITS; i++)
|
||||
{
|
||||
assert(bitset_get_idx(&test_bitset, i));
|
||||
}
|
||||
|
||||
assert(!bitset_is_empty(&test_bitset));
|
||||
|
||||
bitset_clear(&test_bitset);
|
||||
|
||||
for(int i = 0; i < BITSET_MAX_BITS; i++)
|
||||
{
|
||||
assert(!bitset_get_idx(&test_bitset, i));
|
||||
}
|
||||
|
||||
assert(bitset_is_empty(&test_bitset));
|
||||
}
|
||||
|
||||
// bitset_set_idx
|
||||
// bitset_num_set_bits
|
||||
// bitset_find_idx_of_nth_set
|
||||
// bitset_is_empty
|
||||
// bitset_clear
|
||||
void test_bitset_insertions_at_boundry(void)
|
||||
{
|
||||
assert(bitset_is_empty(&test_bitset));
|
||||
|
||||
bitset_set_idx(&test_bitset, 30, true);
|
||||
bitset_set_idx(&test_bitset, 31, true);
|
||||
bitset_set_idx(&test_bitset, 32, true);
|
||||
|
||||
assert(bitset_num_set_bits(&test_bitset) == 3);
|
||||
|
||||
bitset_set_idx(&test_bitset, 31, false);
|
||||
|
||||
assert(bitset_num_set_bits(&test_bitset) == 2);
|
||||
|
||||
assert(bitset_find_idx_of_nth_set(&test_bitset, 0) == 30);
|
||||
assert(bitset_find_idx_of_nth_set(&test_bitset, 1) == 32);
|
||||
|
||||
bitset_set_idx(&test_bitset, 0, true);
|
||||
|
||||
assert(bitset_find_idx_of_nth_set(&test_bitset, 0) == 0);
|
||||
|
||||
bitset_set_idx(&test_bitset, 0, false);
|
||||
bitset_set_idx(&test_bitset, 0, 30);
|
||||
|
||||
bitset_clear(&test_bitset);
|
||||
|
||||
assert(bitset_is_empty(&test_bitset));
|
||||
}
|
||||
|
||||
// bitset_set_idx
|
||||
// bitset_is_empty
|
||||
// bitset_clear
|
||||
// bitset_itr_create
|
||||
// bitset_itr_next
|
||||
void test_bitset_iterator(void)
|
||||
{
|
||||
assert(bitset_is_empty(&test_bitset));
|
||||
|
||||
int test_indices[6] = {0, 31, 32, 63, 64, 100};
|
||||
|
||||
for(int i = 0; i < 6; i++)
|
||||
{
|
||||
bitset_set_idx(&test_bitset, test_indices[i], true);
|
||||
}
|
||||
|
||||
BitsetItr itr = bitset_itr_create(&test_bitset);
|
||||
|
||||
int test_val = UNDEFINED;
|
||||
int index = 0;
|
||||
while((test_val = bitset_itr_next(&itr)) != UNDEFINED)
|
||||
{
|
||||
assert(test_val == test_indices[index++]);
|
||||
}
|
||||
|
||||
assert(index == 6);
|
||||
|
||||
bitset_clear(&test_bitset);
|
||||
|
||||
assert(bitset_is_empty(&test_bitset));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("Testing Bitset Fill All and Empty.\n");
|
||||
test_bitset_fill_all_and_empty();
|
||||
printf("Testing Bitset Insertions At Boundry.\n");
|
||||
test_bitset_insertions_at_boundry();
|
||||
printf("Testing Bitset Iterator.\n");
|
||||
test_bitset_iterator();
|
||||
|
||||
printf("-------------------------------------------------------------------------------\n");
|
||||
printf("Bitset Tests Passed :)\n");
|
||||
printf("-------------------------------------------------------------------------------\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
CC := gcc
|
||||
CFLAGS := -I../../include -I. \
|
||||
-g -O3 -Wall -Werror -DPOOLS_TEST_ENV=yes
|
||||
|
||||
SRC := list_test.c \
|
||||
../../source/list.c \
|
||||
../../source/pool.c \
|
||||
../../source/bitset.c
|
||||
OUT := build/list_test
|
||||
|
||||
$(OUT): $(SRC) | build
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
build:
|
||||
mkdir -p build
|
||||
|
||||
clean:
|
||||
rm -f $(OUT)
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "list.h"
|
||||
#include <stddef.h>
|
||||
|
||||
POOL_ENTRY(ListNode, MAX_LIST_NODES);
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
#include "list.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// As simple as it gets, just needs to be initialized correctly
|
||||
// - list_create
|
||||
// - list_is_empty
|
||||
// - list_get_len
|
||||
// - list_clear
|
||||
void create_and_clear_list(void)
|
||||
{
|
||||
List my_cool_list = list_create();
|
||||
|
||||
// verify no data
|
||||
assert(my_cool_list.head == NULL);
|
||||
assert(my_cool_list.tail == NULL);
|
||||
assert(list_get_len(&my_cool_list) == 0);
|
||||
assert(list_is_empty(&my_cool_list));
|
||||
|
||||
list_clear(&my_cool_list);
|
||||
|
||||
assert(list_is_empty(&my_cool_list));
|
||||
}
|
||||
|
||||
// Push back one entry, make sure it looks as expected
|
||||
// tests:
|
||||
// - list_create
|
||||
// - list_push_back
|
||||
// - list_is_empty
|
||||
// - list_get_len
|
||||
// - list_get_at_idx
|
||||
// - list_clear
|
||||
void push_back_one_entry(void)
|
||||
{
|
||||
List my_cool_list = list_create();
|
||||
|
||||
// verify no data
|
||||
assert(my_cool_list.head == NULL);
|
||||
assert(my_cool_list.tail == NULL);
|
||||
assert(list_get_len(&my_cool_list) == 0);
|
||||
assert(list_is_empty(&my_cool_list));
|
||||
|
||||
int test_data = 1337;
|
||||
|
||||
list_push_back(&my_cool_list, &test_data);
|
||||
|
||||
// should have data and be the same head/tail
|
||||
assert(my_cool_list.head != NULL);
|
||||
assert(my_cool_list.tail != NULL);
|
||||
assert(my_cool_list.head == my_cool_list.tail);
|
||||
assert(list_get_len(&my_cool_list) == 1);
|
||||
|
||||
// pointer should be the same
|
||||
assert(my_cool_list.head->data == &test_data);
|
||||
// and consequently the value should be the same
|
||||
assert(*(int*)(my_cool_list.head->data) == test_data);
|
||||
assert(list_get_at_idx(&my_cool_list, 0) == &test_data);
|
||||
assert(list_get_at_idx(&my_cool_list, 1) == NULL);
|
||||
assert(!list_is_empty(&my_cool_list));
|
||||
// clear the list
|
||||
list_clear(&my_cool_list);
|
||||
|
||||
// verify no data
|
||||
assert(my_cool_list.head == NULL);
|
||||
assert(my_cool_list.tail == NULL);
|
||||
assert(list_is_empty(&my_cool_list));
|
||||
}
|
||||
|
||||
// Push front one entry, make sure it looks as expected
|
||||
// - list_create
|
||||
// - list_push_front
|
||||
// - list_is_empty
|
||||
// - list_get_len
|
||||
// - list_get_at_idx
|
||||
// - list_clear
|
||||
void push_front_one_entry(void)
|
||||
{
|
||||
List my_cool_list = list_create();
|
||||
|
||||
// verify no data
|
||||
assert(my_cool_list.head == NULL);
|
||||
assert(my_cool_list.tail == NULL);
|
||||
assert(list_get_len(&my_cool_list) == 0);
|
||||
assert(list_is_empty(&my_cool_list));
|
||||
|
||||
int test_data = 1337;
|
||||
|
||||
list_push_front(&my_cool_list, &test_data);
|
||||
|
||||
// should have data and be the same head/tail
|
||||
assert(my_cool_list.head != NULL);
|
||||
assert(my_cool_list.tail != NULL);
|
||||
assert(my_cool_list.head == my_cool_list.tail);
|
||||
assert(list_get_len(&my_cool_list) == 1);
|
||||
assert(!list_is_empty(&my_cool_list));
|
||||
|
||||
// pointer should be the same
|
||||
assert(my_cool_list.head->data == &test_data);
|
||||
// and consequently the value should be the same
|
||||
assert(*(int*)(my_cool_list.head->data) == test_data);
|
||||
assert(list_get_at_idx(&my_cool_list, 0) == &test_data);
|
||||
assert(list_get_at_idx(&my_cool_list, 1) == NULL);
|
||||
// clear the list
|
||||
list_clear(&my_cool_list);
|
||||
|
||||
// verify no data
|
||||
assert(my_cool_list.head == NULL);
|
||||
assert(my_cool_list.tail == NULL);
|
||||
assert(list_is_empty(&my_cool_list));
|
||||
}
|
||||
|
||||
// This is meant to be a full test of every function including the use of
|
||||
// the ListItr iterator
|
||||
//
|
||||
// Push back three entries. To cover the 3 states of nodes outside of single node
|
||||
// lists. 3 states being, node at the front, node at the middle, and node at the end
|
||||
//
|
||||
// Afterwards remove one entry with `list_remove_at_idx` and then remove another
|
||||
// with `list_remove_node`
|
||||
//
|
||||
// Lastly, push to the front the same 3 entries and verify the state of the list
|
||||
// before detroying.
|
||||
//
|
||||
// - list_create
|
||||
// - list_push_front
|
||||
// - list_is_empty
|
||||
// - list_get_len
|
||||
// - list_get_at_idx
|
||||
// - list_remove_at_idx
|
||||
// - list_itr_create
|
||||
// - list_itr_next
|
||||
// - list_clear
|
||||
// - list_itr_remove_node_current;
|
||||
void push_back_three_remove_push_front_three_entries(void)
|
||||
{
|
||||
List my_cool_list = list_create();
|
||||
|
||||
// verify no data
|
||||
assert(my_cool_list.head == NULL);
|
||||
assert(my_cool_list.tail == NULL);
|
||||
|
||||
int test_data[3] = {0, 1, 2};
|
||||
|
||||
list_push_back(&my_cool_list, &test_data[0]);
|
||||
|
||||
// Make sure with one entry it looks right
|
||||
assert(my_cool_list.head == my_cool_list.tail);
|
||||
|
||||
list_push_back(&my_cool_list, &test_data[1]);
|
||||
|
||||
// Now with two it should be different
|
||||
assert(my_cool_list.head != my_cool_list.tail);
|
||||
|
||||
list_push_back(&my_cool_list, &test_data[2]);
|
||||
|
||||
// 3 entries?
|
||||
assert(list_get_len(&my_cool_list) == 3);
|
||||
|
||||
// now use an iterator to examine each node
|
||||
int* data;
|
||||
|
||||
ListItr list_itr = list_itr_create(&my_cool_list);
|
||||
|
||||
int itr = 0;
|
||||
|
||||
ListNode* prev_ln = NULL;
|
||||
|
||||
while((data = list_itr_next(&list_itr)))
|
||||
{
|
||||
assert(data != NULL);
|
||||
assert(data == &test_data[itr]);
|
||||
ListNode* ln = list_itr.current_node;
|
||||
switch(itr)
|
||||
{
|
||||
case 0:
|
||||
assert(ln->prev == NULL);
|
||||
assert(ln->next != NULL);
|
||||
assert(ln->next->prev == ln);
|
||||
break;
|
||||
case 1:
|
||||
assert(ln->prev == prev_ln);
|
||||
assert(ln->prev != NULL);
|
||||
assert(ln->next != NULL);
|
||||
assert(ln->next->prev == ln);
|
||||
assert(ln->prev->next == ln);
|
||||
break;
|
||||
case 2:
|
||||
assert(ln->prev != NULL);
|
||||
assert(ln->next == NULL);
|
||||
assert(ln->prev->next == ln);
|
||||
assert(ln->prev == prev_ln);
|
||||
break;
|
||||
default:
|
||||
assert(false); // shouldn't get here
|
||||
break;
|
||||
}
|
||||
itr++;
|
||||
prev_ln = ln;
|
||||
}
|
||||
|
||||
// remove the middle entry
|
||||
assert(list_remove_at_idx(&my_cool_list, 1));
|
||||
// can't remove entry 2 now (doesn't exist)
|
||||
assert(!list_remove_at_idx(&my_cool_list, 2));
|
||||
assert(list_get_len(&my_cool_list) == 2);
|
||||
|
||||
list_itr = list_itr_create(&my_cool_list);
|
||||
data = NULL;
|
||||
itr = 0;
|
||||
prev_ln = NULL;
|
||||
while((data = list_itr_next(&list_itr)))
|
||||
{
|
||||
assert(data != NULL);
|
||||
ListNode* ln = list_itr.current_node;
|
||||
switch(itr)
|
||||
{
|
||||
case 0:
|
||||
assert(ln->prev == NULL);
|
||||
assert(ln->next != NULL);
|
||||
assert(ln->next->prev == ln);
|
||||
break;
|
||||
case 1:
|
||||
assert(ln->prev != NULL);
|
||||
assert(ln->next == NULL);
|
||||
assert(ln->prev->next == ln);
|
||||
assert(ln->prev == prev_ln);
|
||||
list_itr_remove_current_node(&list_itr);
|
||||
break;
|
||||
default:
|
||||
assert(false); // shouldn't get here
|
||||
break;
|
||||
}
|
||||
itr++;
|
||||
prev_ln = ln;
|
||||
}
|
||||
|
||||
assert(list_get_len(&my_cool_list) == 1);
|
||||
assert(my_cool_list.head == my_cool_list.tail);
|
||||
|
||||
// now push the same 3 pointers to the front of the list
|
||||
list_push_front(&my_cool_list, &test_data[0]);
|
||||
list_push_front(&my_cool_list, &test_data[1]);
|
||||
list_push_front(&my_cool_list, &test_data[2]);
|
||||
|
||||
// now, the list should be in the order...
|
||||
// test_data[2] -> test_data[1] -> test_data[0] -> test_data[0]
|
||||
list_itr = list_itr_create(&my_cool_list);
|
||||
data = NULL;
|
||||
itr = 0;
|
||||
while((data = list_itr_next(&list_itr)))
|
||||
{
|
||||
assert(data != NULL);
|
||||
ListNode* ln = list_itr.current_node;
|
||||
|
||||
switch(itr)
|
||||
{
|
||||
case 0:
|
||||
assert(ln->data == &test_data[2]);
|
||||
break;
|
||||
case 1:
|
||||
assert(ln->data == &test_data[1]);
|
||||
break;
|
||||
case 2:
|
||||
assert(ln->data == &test_data[0]);
|
||||
break;
|
||||
case 3:
|
||||
assert(ln->data == &test_data[0]);
|
||||
break;
|
||||
default:
|
||||
assert(false); // shouldn't get here
|
||||
break;
|
||||
}
|
||||
itr++;
|
||||
}
|
||||
|
||||
assert(list_get_len(&my_cool_list) == 4);
|
||||
|
||||
// clear the list
|
||||
list_clear(&my_cool_list);
|
||||
|
||||
// verify no data
|
||||
assert(my_cool_list.head == NULL);
|
||||
assert(my_cool_list.tail == NULL);
|
||||
assert(list_is_empty(&my_cool_list));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("Testing List Create and Clear.\n");
|
||||
create_and_clear_list();
|
||||
|
||||
printf("Testing List Push Back.\n");
|
||||
push_back_one_entry();
|
||||
|
||||
printf("Testing List Push Front.\n");
|
||||
push_front_one_entry();
|
||||
printf("Testing List Complete Exercise.\n");
|
||||
push_back_three_remove_push_front_three_entries();
|
||||
|
||||
printf("-------------------------------------------------------------------------------\n");
|
||||
printf("List Tests Passed :)\n");
|
||||
printf("-------------------------------------------------------------------------------\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
+2
-2
@@ -3,8 +3,8 @@ CC := gcc
|
||||
CFLAGS := -I../../include -I. \
|
||||
-g -O3 -Wall -Werror -DPOOLS_TEST_ENV=yes
|
||||
|
||||
SRC := pool_test.c ../../source/pool.c
|
||||
OUT := build/pool_test
|
||||
SRC := pool_test.c ../../source/pool.c ../../source/bitset.c
|
||||
OUT := build/pool_test
|
||||
|
||||
$(OUT): $(SRC) | build
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
@@ -107,9 +107,9 @@ int main(void)
|
||||
printf("Testing Pool Fill and Empty.\n");
|
||||
if(!test_fill_and_empty()) return UNDEFINED;
|
||||
|
||||
printf("---------------------------------------------------------\n");
|
||||
printf("Pool Tests Passed\n");
|
||||
printf("---------------------------------------------------------\n");
|
||||
printf("-------------------------------------------------------------------------------\n");
|
||||
printf("Pool Tests Passed :)\n");
|
||||
printf("-------------------------------------------------------------------------------\n");
|
||||
printf("Testing execution time for fun :)\n\n");
|
||||
|
||||
timestamp_t t1 = get_time();
|
||||
|
||||
+15
-7
@@ -2,12 +2,20 @@
|
||||
|
||||
set -eu
|
||||
|
||||
run_pool_test() {
|
||||
cd pool
|
||||
make clean
|
||||
make
|
||||
./build/pool_test
|
||||
cd - > /dev/null
|
||||
run_test() {
|
||||
name="$1"
|
||||
|
||||
echo "==============================================================================="
|
||||
echo "Running test for: $1"
|
||||
echo "==============================================================================="
|
||||
|
||||
cd "$name" 2>&1 > /dev/null
|
||||
make clean > /dev/null
|
||||
make > /dev/null
|
||||
./build/"$name"_test
|
||||
cd - 2>&1 > /dev/null
|
||||
}
|
||||
|
||||
run_pool_test
|
||||
run_test bitset
|
||||
run_test pool
|
||||
run_test list
|
||||
|
||||
Reference in New Issue
Block a user