mirror of
https://github.com/bkthomps/Containers.git
synced 2025-11-16 04:24:47 +00:00
Reduce calls to malloc in list (#77)
Reduce the number of malloc calls in list. Improves efficiency by 15%.
This commit is contained in:
20
containers.h
20
containers.h
@@ -222,28 +222,28 @@ typedef struct internal_list *list;
|
||||
list list_init(size_t data_size);
|
||||
|
||||
/* Utility */
|
||||
int list_size(list me);
|
||||
size_t list_size(list me);
|
||||
int list_is_empty(list me);
|
||||
void list_copy_to_array(void *arr, list me);
|
||||
|
||||
/* Adding */
|
||||
int list_add_first(list me, void *data);
|
||||
int list_add_at(list me, int index, void *data);
|
||||
int list_add_at(list me, size_t index, void *data);
|
||||
int list_add_last(list me, void *data);
|
||||
|
||||
/* Removing */
|
||||
int list_remove_first(list me);
|
||||
int list_remove_at(list me, int index);
|
||||
int list_remove_at(list me, size_t index);
|
||||
int list_remove_last(list me);
|
||||
|
||||
/* Setting */
|
||||
int list_set_first(list me, void *data);
|
||||
int list_set_at(list me, int index, void *data);
|
||||
int list_set_at(list me, size_t index, void *data);
|
||||
int list_set_last(list me, void *data);
|
||||
|
||||
/* Getting */
|
||||
int list_get_first(void *data, list me);
|
||||
int list_get_at(void *data, list me, int index);
|
||||
int list_get_at(void *data, list me, size_t index);
|
||||
int list_get_last(void *data, list me);
|
||||
|
||||
/* Ending */
|
||||
@@ -337,28 +337,28 @@ typedef struct internal_forward_list *forward_list;
|
||||
forward_list forward_list_init(size_t data_size);
|
||||
|
||||
/* Utility */
|
||||
int forward_list_size(forward_list me);
|
||||
size_t forward_list_size(forward_list me);
|
||||
int forward_list_is_empty(forward_list me);
|
||||
void forward_list_copy_to_array(void *arr, forward_list me);
|
||||
|
||||
/* Adding */
|
||||
int forward_list_add_first(forward_list me, void *data);
|
||||
int forward_list_add_at(forward_list me, int index, void *data);
|
||||
int forward_list_add_at(forward_list me, size_t index, void *data);
|
||||
int forward_list_add_last(forward_list me, void *data);
|
||||
|
||||
/* Removing */
|
||||
int forward_list_remove_first(forward_list me);
|
||||
int forward_list_remove_at(forward_list me, int index);
|
||||
int forward_list_remove_at(forward_list me, size_t index);
|
||||
int forward_list_remove_last(forward_list me);
|
||||
|
||||
/* Setting */
|
||||
int forward_list_set_first(forward_list me, void *data);
|
||||
int forward_list_set_at(forward_list me, int index, void *data);
|
||||
int forward_list_set_at(forward_list me, size_t index, void *data);
|
||||
int forward_list_set_last(forward_list me, void *data);
|
||||
|
||||
/* Getting */
|
||||
int forward_list_get_first(void *data, forward_list me);
|
||||
int forward_list_get_at(void *data, forward_list me, int index);
|
||||
int forward_list_get_at(void *data, forward_list me, size_t index);
|
||||
int forward_list_get_last(void *data, forward_list me);
|
||||
|
||||
/* Ending */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2019 Bailey Thompson
|
||||
* Copyright (c) 2017-2020 Bailey Thompson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -34,28 +34,28 @@ typedef struct internal_list *list;
|
||||
list list_init(size_t data_size);
|
||||
|
||||
/* Utility */
|
||||
int list_size(list me);
|
||||
size_t list_size(list me);
|
||||
int list_is_empty(list me);
|
||||
void list_copy_to_array(void *arr, list me);
|
||||
|
||||
/* Adding */
|
||||
int list_add_first(list me, void *data);
|
||||
int list_add_at(list me, int index, void *data);
|
||||
int list_add_at(list me, size_t index, void *data);
|
||||
int list_add_last(list me, void *data);
|
||||
|
||||
/* Removing */
|
||||
int list_remove_first(list me);
|
||||
int list_remove_at(list me, int index);
|
||||
int list_remove_at(list me, size_t index);
|
||||
int list_remove_last(list me);
|
||||
|
||||
/* Setting */
|
||||
int list_set_first(list me, void *data);
|
||||
int list_set_at(list me, int index, void *data);
|
||||
int list_set_at(list me, size_t index, void *data);
|
||||
int list_set_last(list me, void *data);
|
||||
|
||||
/* Getting */
|
||||
int list_get_first(void *data, list me);
|
||||
int list_get_at(void *data, list me, int index);
|
||||
int list_get_at(void *data, list me, size_t index);
|
||||
int list_get_last(void *data, list me);
|
||||
|
||||
/* Ending */
|
||||
|
||||
161
src/list.c
161
src/list.c
@@ -26,16 +26,15 @@
|
||||
|
||||
struct internal_list {
|
||||
size_t bytes_per_item;
|
||||
int item_count;
|
||||
struct node *head;
|
||||
struct node *tail;
|
||||
size_t item_count;
|
||||
char *head;
|
||||
char *tail;
|
||||
};
|
||||
|
||||
struct node {
|
||||
struct node *prev;
|
||||
void *data;
|
||||
struct node *next;
|
||||
};
|
||||
static const size_t ptr_size = sizeof(char *);
|
||||
static const size_t node_next_ptr_offset = 0;
|
||||
static const size_t node_prev_ptr_offset = sizeof(char *);
|
||||
static const size_t node_data_ptr_offset = 2 * sizeof(char *);
|
||||
|
||||
/**
|
||||
* Initializes a doubly-linked list.
|
||||
@@ -70,7 +69,7 @@ list list_init(const size_t data_size)
|
||||
*
|
||||
* @return the number of elements
|
||||
*/
|
||||
int list_size(list me)
|
||||
size_t list_size(list me)
|
||||
{
|
||||
return me->item_count;
|
||||
}
|
||||
@@ -100,24 +99,25 @@ int list_is_empty(list me)
|
||||
*/
|
||||
void list_copy_to_array(void *const arr, list me)
|
||||
{
|
||||
struct node *traverse = me->head;
|
||||
int offset = 0;
|
||||
char *traverse = me->head;
|
||||
size_t offset = 0;
|
||||
while (traverse) {
|
||||
memcpy((char *) arr + offset, traverse->data, me->bytes_per_item);
|
||||
memcpy((char *) arr + offset, traverse + node_data_ptr_offset,
|
||||
me->bytes_per_item);
|
||||
memcpy(&traverse, traverse + node_next_ptr_offset, ptr_size);
|
||||
offset += me->bytes_per_item;
|
||||
traverse = traverse->next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the node at index starting from the head.
|
||||
*/
|
||||
static struct node *list_get_node_from_head(list me, const int index)
|
||||
static char *list_get_node_from_head(list me, const size_t index)
|
||||
{
|
||||
struct node *traverse = me->head;
|
||||
int i;
|
||||
char *traverse = me->head;
|
||||
size_t i;
|
||||
for (i = 0; i < index; i++) {
|
||||
traverse = traverse->next;
|
||||
memcpy(&traverse, traverse + node_next_ptr_offset, ptr_size);
|
||||
}
|
||||
return traverse;
|
||||
}
|
||||
@@ -125,12 +125,12 @@ static struct node *list_get_node_from_head(list me, const int index)
|
||||
/*
|
||||
* Gets the node at index starting from the tail.
|
||||
*/
|
||||
static struct node *list_get_node_from_tail(list me, const int index)
|
||||
static char *list_get_node_from_tail(list me, const size_t index)
|
||||
{
|
||||
struct node *traverse = me->tail;
|
||||
int i;
|
||||
char *traverse = me->tail;
|
||||
size_t i;
|
||||
for (i = me->item_count - 1; i > index; i--) {
|
||||
traverse = traverse->prev;
|
||||
memcpy(&traverse, traverse + node_prev_ptr_offset, ptr_size);
|
||||
}
|
||||
return traverse;
|
||||
}
|
||||
@@ -138,7 +138,7 @@ static struct node *list_get_node_from_tail(list me, const int index)
|
||||
/*
|
||||
* Gets the node at the specified index.
|
||||
*/
|
||||
static struct node *list_get_node_at(list me, const int index)
|
||||
static char *list_get_node_at(list me, const size_t index)
|
||||
{
|
||||
if (index < me->item_count / 2) {
|
||||
return list_get_node_from_head(me, index);
|
||||
@@ -179,45 +179,42 @@ int list_add_first(list me, void *const data)
|
||||
* @return -ENOMEM if out of memory
|
||||
* @return -EINVAL if invalid argument
|
||||
*/
|
||||
int list_add_at(list me, const int index, void *const data)
|
||||
int list_add_at(list me, const size_t index, void *const data)
|
||||
{
|
||||
struct node *add;
|
||||
if (index < 0 || index > me->item_count) {
|
||||
char *node;
|
||||
if (index > me->item_count) {
|
||||
return -EINVAL;
|
||||
}
|
||||
add = malloc(sizeof(struct node));
|
||||
if (!add) {
|
||||
node = malloc(2 * ptr_size + me->bytes_per_item);
|
||||
if (!node) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
add->data = malloc(me->bytes_per_item);
|
||||
if (!add->data) {
|
||||
free(add);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memcpy(add->data, data, me->bytes_per_item);
|
||||
memcpy(node + node_data_ptr_offset, data, me->bytes_per_item);
|
||||
if (!me->head) {
|
||||
add->prev = NULL;
|
||||
add->next = NULL;
|
||||
me->head = add;
|
||||
me->tail = add;
|
||||
/* Relies on next and prev being the first two blocks. */
|
||||
memset(node, 0, 2 * ptr_size);
|
||||
me->head = node;
|
||||
me->tail = node;
|
||||
} else if (index == 0) {
|
||||
struct node *const traverse = me->head;
|
||||
traverse->prev = add;
|
||||
add->prev = NULL;
|
||||
add->next = traverse;
|
||||
me->head = add;
|
||||
char *traverse = me->head;
|
||||
memcpy(traverse + node_prev_ptr_offset, &node, ptr_size);
|
||||
memcpy(node + node_next_ptr_offset, &traverse, ptr_size);
|
||||
memset(node + node_prev_ptr_offset, 0, ptr_size);
|
||||
me->head = node;
|
||||
} else if (index == me->item_count) {
|
||||
struct node *const traverse = me->tail;
|
||||
traverse->next = add;
|
||||
add->prev = traverse;
|
||||
add->next = NULL;
|
||||
me->tail = add;
|
||||
char *traverse = me->tail;
|
||||
memcpy(traverse + node_next_ptr_offset, &node, ptr_size);
|
||||
memset(node + node_next_ptr_offset, 0, ptr_size);
|
||||
memcpy(node + node_prev_ptr_offset, &traverse, ptr_size);
|
||||
me->tail = node;
|
||||
} else {
|
||||
struct node *const traverse = list_get_node_at(me, index);
|
||||
add->prev = traverse->prev;
|
||||
add->next = traverse;
|
||||
traverse->prev->next = add;
|
||||
traverse->prev = add;
|
||||
char *traverse = list_get_node_at(me, index);
|
||||
char *traverse_prev;
|
||||
memcpy(&traverse_prev, traverse + node_prev_ptr_offset, ptr_size);
|
||||
memcpy(node + node_prev_ptr_offset, &traverse_prev, ptr_size);
|
||||
memcpy(node + node_next_ptr_offset, &traverse, ptr_size);
|
||||
memcpy(traverse_prev + node_next_ptr_offset, &node, ptr_size);
|
||||
memcpy(traverse + node_prev_ptr_offset, &node, ptr_size);
|
||||
}
|
||||
me->item_count++;
|
||||
return 0;
|
||||
@@ -241,14 +238,6 @@ int list_add_last(list me, void *const data)
|
||||
return list_add_at(me, me->item_count, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines if the input is illegal.
|
||||
*/
|
||||
static int list_is_illegal_input(list me, const int index)
|
||||
{
|
||||
return index < 0 || index >= me->item_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the first piece of data from the doubly-linked list.
|
||||
*
|
||||
@@ -271,10 +260,10 @@ int list_remove_first(list me)
|
||||
* @return 0 if no error
|
||||
* @return -EINVAL if invalid argument
|
||||
*/
|
||||
int list_remove_at(list me, const int index)
|
||||
int list_remove_at(list me, const size_t index)
|
||||
{
|
||||
struct node *traverse;
|
||||
if (list_is_illegal_input(me, index)) {
|
||||
char *traverse;
|
||||
if (index >= me->item_count) {
|
||||
return -EINVAL;
|
||||
}
|
||||
traverse = list_get_node_at(me, index);
|
||||
@@ -282,16 +271,23 @@ int list_remove_at(list me, const int index)
|
||||
me->head = NULL;
|
||||
me->tail = NULL;
|
||||
} else if (index == 0) {
|
||||
traverse->next->prev = NULL;
|
||||
me->head = traverse->next;
|
||||
char *traverse_next;
|
||||
memcpy(&traverse_next, traverse + node_next_ptr_offset, ptr_size);
|
||||
memset(traverse_next + node_prev_ptr_offset, 0, ptr_size);
|
||||
me->head = traverse_next;
|
||||
} else if (index == me->item_count - 1) {
|
||||
traverse->prev->next = NULL;
|
||||
me->tail = traverse->prev;
|
||||
char *traverse_prev;
|
||||
memcpy(&traverse_prev, traverse + node_prev_ptr_offset, ptr_size);
|
||||
memset(traverse_prev + node_next_ptr_offset, 0, ptr_size);
|
||||
me->tail = traverse_prev;
|
||||
} else {
|
||||
traverse->prev->next = traverse->next;
|
||||
traverse->next->prev = traverse->prev;
|
||||
char *traverse_next;
|
||||
char *traverse_prev;
|
||||
memcpy(&traverse_next, traverse + node_next_ptr_offset, ptr_size);
|
||||
memcpy(&traverse_prev, traverse + node_prev_ptr_offset, ptr_size);
|
||||
memcpy(traverse_next + node_prev_ptr_offset, &traverse_prev, ptr_size);
|
||||
memcpy(traverse_prev + node_next_ptr_offset, &traverse_next, ptr_size);
|
||||
}
|
||||
free(traverse->data);
|
||||
free(traverse);
|
||||
me->item_count--;
|
||||
return 0;
|
||||
@@ -342,14 +338,14 @@ int list_set_first(list me, void *const data)
|
||||
* @return 0 if no error
|
||||
* @return -EINVAL if invalid argument
|
||||
*/
|
||||
int list_set_at(list me, const int index, void *const data)
|
||||
int list_set_at(list me, const size_t index, void *const data)
|
||||
{
|
||||
struct node *traverse;
|
||||
if (list_is_illegal_input(me, index)) {
|
||||
char *traverse;
|
||||
if (index >= me->item_count) {
|
||||
return -EINVAL;
|
||||
}
|
||||
traverse = list_get_node_at(me, index);
|
||||
memcpy(traverse->data, data, me->bytes_per_item);
|
||||
memcpy(traverse + node_data_ptr_offset, data, me->bytes_per_item);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -405,14 +401,14 @@ int list_get_first(void *const data, list me)
|
||||
* @return 0 if no error
|
||||
* @return -EINVAL if invalid argument
|
||||
*/
|
||||
int list_get_at(void *const data, list me, const int index)
|
||||
int list_get_at(void *const data, list me, const size_t index)
|
||||
{
|
||||
struct node *traverse;
|
||||
if (list_is_illegal_input(me, index)) {
|
||||
char *traverse;
|
||||
if (index >= me->item_count) {
|
||||
return -EINVAL;
|
||||
}
|
||||
traverse = list_get_node_at(me, index);
|
||||
memcpy(data, traverse->data, me->bytes_per_item);
|
||||
memcpy(data, traverse + node_data_ptr_offset, me->bytes_per_item);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -442,15 +438,14 @@ int list_get_last(void *const data, list me)
|
||||
*/
|
||||
void list_clear(list me)
|
||||
{
|
||||
struct node *traverse = me->head;
|
||||
char *traverse = me->head;
|
||||
while (traverse) {
|
||||
struct node *const temp = traverse;
|
||||
traverse = traverse->next;
|
||||
free(temp->data);
|
||||
char *temp = traverse;
|
||||
memcpy(&traverse, traverse + node_next_ptr_offset, ptr_size);
|
||||
free(temp);
|
||||
}
|
||||
me->head = NULL;
|
||||
me->item_count = 0;
|
||||
me->head = NULL;
|
||||
me->tail = NULL;
|
||||
}
|
||||
|
||||
|
||||
82
tst/list.c
82
tst/list.c
@@ -11,7 +11,7 @@ static void test_front(list me)
|
||||
int val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
int get_arr[10] = {0};
|
||||
int get;
|
||||
int i;
|
||||
size_t i;
|
||||
assert(list_size(me) == 0);
|
||||
assert(list_is_empty(me));
|
||||
for (i = 0; i < 10; i++) {
|
||||
@@ -176,15 +176,6 @@ static void test_add_first_out_of_memory(void)
|
||||
assert(list_get_at(&get, me, i) == 0);
|
||||
assert(get == 15 - i);
|
||||
}
|
||||
fail_malloc = 1;
|
||||
delay_fail_malloc = 1;
|
||||
assert(list_add_first(me, &i) == -ENOMEM);
|
||||
assert(list_size(me) == 16);
|
||||
for (i = 0; i < 16; i++) {
|
||||
int get = 0xfacade;
|
||||
assert(list_get_at(&get, me, i) == 0);
|
||||
assert(get == 15 - i);
|
||||
}
|
||||
assert(!list_destroy(me));
|
||||
}
|
||||
#endif
|
||||
@@ -218,21 +209,6 @@ static void test_add_at_out_of_memory(void)
|
||||
get = 0xfacade;
|
||||
assert(list_get_at(&get, me, 15) == 0);
|
||||
assert(get == 982);
|
||||
fail_malloc = 1;
|
||||
delay_fail_malloc = 1;
|
||||
assert(list_add_at(me, 5, &i) == -ENOMEM);
|
||||
assert(list_size(me) == 16);
|
||||
get = 0xfacade;
|
||||
assert(list_get_at(&get, me, 0) == 0);
|
||||
assert(get == 157);
|
||||
for (i = 1; i < 15; i++) {
|
||||
get = 0xfacade;
|
||||
assert(list_get_at(&get, me, i) == 0);
|
||||
assert(get == i);
|
||||
}
|
||||
get = 0xfacade;
|
||||
assert(list_get_at(&get, me, 15) == 0);
|
||||
assert(get == 982);
|
||||
assert(!list_destroy(me));
|
||||
}
|
||||
#endif
|
||||
@@ -255,22 +231,13 @@ static void test_add_last_out_of_memory(void)
|
||||
assert(list_get_at(&get, me, i) == 0);
|
||||
assert(get == i);
|
||||
}
|
||||
fail_malloc = 1;
|
||||
delay_fail_malloc = 1;
|
||||
assert(list_add_last(me, &i) == -ENOMEM);
|
||||
assert(list_size(me) == 16);
|
||||
for (i = 0; i < 16; i++) {
|
||||
int get = 0xfacade;
|
||||
assert(list_get_at(&get, me, i) == 0);
|
||||
assert(get == i);
|
||||
}
|
||||
assert(!list_destroy(me));
|
||||
}
|
||||
#endif
|
||||
|
||||
void test_remove_all(void)
|
||||
{
|
||||
int i;
|
||||
size_t i;
|
||||
list me = list_init(sizeof(int));
|
||||
for (i = 0; i < 100; i++) {
|
||||
list_add_first(me, &i);
|
||||
@@ -294,7 +261,7 @@ struct pair {
|
||||
int cur_cost;
|
||||
};
|
||||
|
||||
static int test_puzzle(int start_node, int dest_node)
|
||||
static int test_puzzle_forwards(int start_node, int dest_node)
|
||||
{
|
||||
list q = list_init(sizeof(struct pair));
|
||||
struct pair cur;
|
||||
@@ -331,6 +298,43 @@ static int test_puzzle(int start_node, int dest_node)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int test_puzzle_backwards(int start_node, int dest_node)
|
||||
{
|
||||
list q = list_init(sizeof(struct pair));
|
||||
struct pair cur;
|
||||
cur.cur_node = start_node;
|
||||
cur.cur_cost = 0;
|
||||
assert(list_is_empty(q));
|
||||
list_add_first(q, &cur);
|
||||
assert(list_size(q) == 1);
|
||||
while (!list_is_empty(q)) {
|
||||
int node;
|
||||
int cost;
|
||||
list_get_last(&cur, q);
|
||||
list_remove_last(q);
|
||||
node = cur.cur_node;
|
||||
cost = cur.cur_cost;
|
||||
if (node > 2 * dest_node || node < 1) {
|
||||
continue;
|
||||
}
|
||||
if (node == dest_node) {
|
||||
list_destroy(q);
|
||||
return cost;
|
||||
}
|
||||
cur.cur_cost = cost + 1;
|
||||
cur.cur_node = node - 1;
|
||||
list_add_first(q, &cur);
|
||||
assert(cur.cur_cost == cost + 1);
|
||||
assert(cur.cur_node == node - 1);
|
||||
cur.cur_node = 2 * node;
|
||||
list_add_first(q, &cur);
|
||||
assert(cur.cur_cost == cost + 1);
|
||||
assert(cur.cur_node == 2 * node);
|
||||
}
|
||||
list_destroy(q);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void test_list(void)
|
||||
{
|
||||
test_invalid_init();
|
||||
@@ -342,6 +346,8 @@ void test_list(void)
|
||||
test_add_last_out_of_memory();
|
||||
#endif
|
||||
test_remove_all();
|
||||
assert(test_puzzle(2, 5) == 4);
|
||||
assert(test_puzzle(2, 10) == 5);
|
||||
assert(test_puzzle_forwards(2, 5) == 4);
|
||||
assert(test_puzzle_forwards(2, 10) == 5);
|
||||
assert(test_puzzle_backwards(2, 5) == 4);
|
||||
assert(test_puzzle_backwards(2, 10) == 5);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user